OSDN Git Service

new repo
[bytom/vapor.git] / test / tx_test.go
1 // +build functional
2
3 package test
4
5 import (
6         "encoding/json"
7         "fmt"
8         "io/ioutil"
9         "os"
10         "testing"
11
12         log "github.com/sirupsen/logrus"
13         dbm "github.com/tendermint/tmlibs/db"
14
15         "github.com/vapor/account"
16         "github.com/vapor/asset"
17         "github.com/vapor/blockchain/pseudohsm"
18         "github.com/vapor/consensus"
19         "github.com/vapor/protocol/bc"
20         "github.com/vapor/protocol/bc/types"
21         "github.com/vapor/protocol/validation"
22         "github.com/vapor/protocol/vm"
23 )
24
25 func init() {
26         log.SetLevel(log.ErrorLevel)
27 }
28
29 type TxTestConfig struct {
30         Keys         []*keyInfo       `json:"keys"`
31         Accounts     []*accountInfo   `json:"accounts"`
32         Transactions []*ttTransaction `json:"transactions"`
33 }
34
35 func (cfg *TxTestConfig) Run() error {
36         dirPath, err := ioutil.TempDir(".", "pseudo_hsm")
37         if err != nil {
38                 return err
39         }
40         defer os.RemoveAll(dirPath)
41         hsm, err := pseudohsm.New(dirPath)
42         if err != nil {
43                 return err
44         }
45
46         chainDB := dbm.NewDB("chain_db", "leveldb", "chain_db")
47         defer os.RemoveAll("chain_db")
48         chain, _, _, _ := MockChain(chainDB)
49         txTestDB := dbm.NewDB("tx_test_db", "leveldb", "tx_test_db")
50         defer os.RemoveAll("tx_test_db")
51         accountManager := account.NewManager(txTestDB, chain)
52         assets := asset.NewRegistry(txTestDB, chain)
53
54         generator := NewTxGenerator(accountManager, assets, hsm)
55         for _, key := range cfg.Keys {
56                 if err := generator.createKey(key.Name, key.Password); err != nil {
57                         return err
58                 }
59         }
60
61         for _, acc := range cfg.Accounts {
62                 if err := generator.createAccount(acc.Name, acc.Keys, acc.Quorum); err != nil {
63                         return err
64                 }
65         }
66
67         block := &bc.Block{
68                 BlockHeader: &bc.BlockHeader{
69                         Height:  1,
70                         Version: 1,
71                 },
72         }
73         for _, t := range cfg.Transactions {
74                 tx, err := t.create(generator)
75                 if err != nil {
76                         return err
77                 }
78
79                 tx.TxData.Version = t.Version
80                 tx.Tx = types.MapTx(&tx.TxData)
81                 status, err := validation.ValidateTx(tx.Tx, block)
82                 result := err == nil
83                 if result != t.Valid {
84                         return fmt.Errorf("tx %s validate failed, expected: %t, have: %t", t.Describe, t.Valid, result)
85                 }
86                 if status == nil {
87                         continue
88                 }
89
90                 gasOnlyTx := false
91                 if err != nil && status.GasValid {
92                         gasOnlyTx = true
93                 }
94                 if gasOnlyTx != t.GasOnly {
95                         return fmt.Errorf("gas only tx %s validate failed", t.Describe)
96                 }
97                 if result && t.TxFee != status.BTMValue {
98                         return fmt.Errorf("gas used dismatch, expected: %d, have: %d", t.TxFee, status.BTMValue)
99                 }
100         }
101         return nil
102 }
103
104 type ttTransaction struct {
105         wtTransaction
106         Describe string `json:"describe"`
107         Version  uint64 `json:"version"`
108         Valid    bool   `json:"valid"`
109         GasOnly  bool   `json:"gas_only"`
110         TxFee    uint64 `json:"tx_fee"`
111 }
112
113 // UnmarshalJSON unmarshal transaction with default version 1
114 func (t *ttTransaction) UnmarshalJSON(data []byte) error {
115         type typeAlias ttTransaction
116         tx := &typeAlias{
117                 Version: 1,
118         }
119
120         err := json.Unmarshal(data, tx)
121         if err != nil {
122                 return err
123         }
124         *t = ttTransaction(*tx)
125         return nil
126 }
127
128 func (t *ttTransaction) create(g *TxGenerator) (*types.Tx, error) {
129         g.Reset()
130         for _, input := range t.Inputs {
131                 switch input.Type {
132                 case "spend_account":
133                         utxo, err := g.mockUtxo(input.AccountAlias, input.AssetAlias, input.Amount)
134                         if err != nil {
135                                 return nil, err
136                         }
137                         if err := g.AddTxInputFromUtxo(utxo, input.AccountAlias); err != nil {
138                                 return nil, err
139                         }
140                 case "issue":
141                         _, err := g.createAsset(input.AccountAlias, input.AssetAlias)
142                         if err != nil {
143                                 return nil, err
144                         }
145                         if err := g.AddIssuanceInput(input.AssetAlias, input.Amount); err != nil {
146                                 return nil, err
147                         }
148                 }
149         }
150
151         for _, output := range t.Outputs {
152                 switch output.Type {
153                 case "output":
154                         if err := g.AddTxOutput(output.AccountAlias, output.AssetAlias, output.Amount); err != nil {
155                                 return nil, err
156                         }
157                 case "retire":
158                         if err := g.AddRetirement(output.AssetAlias, output.Amount); err != nil {
159                                 return nil, err
160                         }
161                 }
162         }
163         return g.Sign(t.Passwords)
164 }
165
166 func TestTx(t *testing.T) {
167         walk(t, txTestDir, func(t *testing.T, name string, test *TxTestConfig) {
168                 if err := test.Run(); err != nil {
169                         t.Fatal(err)
170                 }
171         })
172 }
173
174 func TestCoinbaseMature(t *testing.T) {
175         db := dbm.NewDB("test_coinbase_mature_db", "leveldb", "test_coinbase_mature_db")
176         defer os.RemoveAll("test_coinbase_mature_db")
177         chain, _, _, _ := MockChain(db)
178
179         defaultCtrlProg := []byte{byte(vm.OP_TRUE)}
180         if err := AppendBlocks(chain, 1); err != nil {
181                 t.Fatal(err)
182         }
183
184         height := chain.BestBlockHeight()
185         block, err := chain.GetBlockByHeight(height)
186         if err != nil {
187                 t.Fatal(err)
188         }
189
190         tx, err := CreateTxFromTx(block.Transactions[0], 0, 1000000000, defaultCtrlProg)
191         if err != nil {
192                 t.Fatal(err)
193         }
194
195         txs := []*types.Tx{tx}
196         matureHeight := chain.BestBlockHeight() + consensus.CoinbasePendingBlockNumber
197         currentHeight := chain.BestBlockHeight()
198         for h := currentHeight + 1; h < matureHeight; h++ {
199                 block, err := NewBlock(chain, txs, defaultCtrlProg)
200                 if err != nil {
201                         t.Fatal(err)
202                 }
203                 if err := SolveAndUpdate(chain, block); err == nil {
204                         t.Fatal("spent immature coinbase output success")
205                 }
206                 block, err = NewBlock(chain, nil, defaultCtrlProg)
207                 if err != nil {
208                         t.Fatal(err)
209                 }
210                 if err := SolveAndUpdate(chain, block); err != nil {
211                         t.Fatal(err)
212                 }
213         }
214
215         block, err = NewBlock(chain, txs, defaultCtrlProg)
216         if err != nil {
217                 t.Fatal(err)
218         }
219         if err := SolveAndUpdate(chain, block); err != nil {
220                 t.Fatalf("spent mature coinbase output failed: %s", err)
221         }
222 }
223
224 func TestCoinbaseTx(t *testing.T) {
225         db := dbm.NewDB("test_coinbase_tx_db", "leveldb", "test_coinbase_tx_db")
226         defer os.RemoveAll("test_coinbase_tx_db")
227         chain, _, _, _ := MockChain(db)
228
229         defaultCtrlProg := []byte{byte(vm.OP_TRUE)}
230         if err := AppendBlocks(chain, 1); err != nil {
231                 t.Fatal(err)
232         }
233
234         block, err := chain.GetBlockByHeight(chain.BestBlockHeight())
235         if err != nil {
236                 t.Fatal(err)
237         }
238
239         tx, err := CreateTxFromTx(block.Transactions[0], 0, 1000000000, defaultCtrlProg)
240         if err != nil {
241                 t.Fatal(err)
242         }
243
244         block, err = NewBlock(chain, []*types.Tx{tx}, defaultCtrlProg)
245         if err != nil {
246                 t.Fatal(err)
247         }
248
249         txFees := []uint64{100000, 5000000000000}
250         for _, txFee := range txFees {
251                 coinbaseTx, err := CreateCoinbaseTx(defaultCtrlProg, block.Height, txFee)
252                 if err != nil {
253                         t.Fatal(err)
254                 }
255
256                 if err := ReplaceCoinbase(block, coinbaseTx); err != nil {
257                         t.Fatal(err)
258                 }
259
260                 if err := SolveAndUpdate(chain, block); err == nil {
261                         t.Fatalf("invalid coinbase tx validate success")
262                 }
263         }
264 }