OSDN Git Service

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