OSDN Git Service

chore(version): update version to 2.0.5 (#2102)
[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         log "github.com/sirupsen/logrus"
13
14         "github.com/bytom/bytom/account"
15         "github.com/bytom/bytom/asset"
16         "github.com/bytom/bytom/blockchain/pseudohsm"
17         "github.com/bytom/bytom/consensus"
18         dbm "github.com/bytom/bytom/database/leveldb"
19         "github.com/bytom/bytom/protocol/bc"
20         "github.com/bytom/bytom/protocol/bc/types"
21         "github.com/bytom/bytom/protocol/validation"
22         "github.com/bytom/bytom/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                 if result && t.TxFee != status.BTMValue {
91                         return fmt.Errorf("gas used dismatch, expected: %d, have: %d", t.TxFee, status.BTMValue)
92                 }
93         }
94         return nil
95 }
96
97 type ttTransaction struct {
98         wtTransaction
99         Describe string `json:"describe"`
100         Version  uint64 `json:"version"`
101         Valid    bool   `json:"valid"`
102         TxFee    uint64 `json:"tx_fee"`
103 }
104
105 // UnmarshalJSON unmarshal transaction with default version 1
106 func (t *ttTransaction) UnmarshalJSON(data []byte) error {
107         type typeAlias ttTransaction
108         tx := &typeAlias{
109                 Version: 1,
110         }
111
112         err := json.Unmarshal(data, tx)
113         if err != nil {
114                 return err
115         }
116         *t = ttTransaction(*tx)
117         return nil
118 }
119
120 func (t *ttTransaction) create(g *TxGenerator) (*types.Tx, error) {
121         g.Reset()
122         for _, input := range t.Inputs {
123                 switch input.Type {
124                 case "spend_account":
125                         utxo, err := g.mockUtxo(input.AccountAlias, input.AssetAlias, input.Amount)
126                         if err != nil {
127                                 return nil, err
128                         }
129                         if err := g.AddTxInputFromUtxo(utxo, input.AccountAlias); err != nil {
130                                 return nil, err
131                         }
132                 case "issue":
133                         _, err := g.createAsset(input.AccountAlias, input.AssetAlias)
134                         if err != nil {
135                                 return nil, err
136                         }
137                         if err := g.AddIssuanceInput(input.AssetAlias, input.Amount); err != nil {
138                                 return nil, err
139                         }
140                 }
141         }
142
143         for _, output := range t.Outputs {
144                 switch output.Type {
145                 case "output":
146                         if err := g.AddTxOutput(output.AccountAlias, output.AssetAlias, output.Amount); err != nil {
147                                 return nil, err
148                         }
149                 case "retire":
150                         if err := g.AddRetirement(output.AssetAlias, output.Amount); err != nil {
151                                 return nil, err
152                         }
153                 }
154         }
155         return g.Sign(t.Passwords)
156 }
157
158 func TestTx(t *testing.T) {
159         walk(t, txTestDir, func(t *testing.T, name string, test *TxTestConfig) {
160                 if err := test.Run(); err != nil {
161                         t.Fatal(err)
162                 }
163         })
164 }
165
166 func TestCoinbaseMature(t *testing.T) {
167         db := dbm.NewDB("test_coinbase_mature_db", "leveldb", "test_coinbase_mature_db")
168         defer os.RemoveAll("test_coinbase_mature_db")
169         chain, _, _, _ := MockChain(db)
170
171         defaultCtrlProg := []byte{byte(vm.OP_TRUE)}
172         if err := AppendBlocks(chain, 1); err != nil {
173                 t.Fatal(err)
174         }
175
176         height := chain.BestBlockHeight()
177         block, err := chain.GetBlockByHeight(height)
178         if err != nil {
179                 t.Fatal(err)
180         }
181
182         tx, err := CreateTxFromTx(block.Transactions[0], 0, 1000000000, defaultCtrlProg)
183         if err != nil {
184                 t.Fatal(err)
185         }
186
187         txs := []*types.Tx{tx}
188         matureHeight := chain.BestBlockHeight() + consensus.CoinbasePendingBlockNumber
189         currentHeight := chain.BestBlockHeight()
190         for h := currentHeight + 1; h < matureHeight; h++ {
191                 block, err := NewBlock(chain, txs, defaultCtrlProg)
192                 if err != nil {
193                         t.Fatal(err)
194                 }
195                 if _, err = chain.ProcessBlock(block); err == nil {
196                         t.Fatal("spent immature coinbase output success")
197                 }
198                 block, err = NewBlock(chain, nil, defaultCtrlProg)
199                 if err != nil {
200                         t.Fatal(err)
201                 }
202                 if _, err := chain.ProcessBlock(block); err != nil {
203                         t.Fatal(err)
204                 }
205         }
206
207         block, err = NewBlock(chain, txs, defaultCtrlProg)
208         if err != nil {
209                 t.Fatal(err)
210         }
211         if _, err = chain.ProcessBlock(block); err != nil {
212                 t.Fatalf("spent mature coinbase output failed: %s", err)
213         }
214 }
215
216 func TestCoinbaseTx(t *testing.T) {
217         db := dbm.NewDB("test_coinbase_tx_db", "leveldb", "test_coinbase_tx_db")
218         defer os.RemoveAll("test_coinbase_tx_db")
219         chain, _, _, _ := MockChain(db)
220
221         defaultCtrlProg := []byte{byte(vm.OP_TRUE)}
222         if err := AppendBlocks(chain, 1); err != nil {
223                 t.Fatal(err)
224         }
225
226         block, err := chain.GetBlockByHeight(chain.BestBlockHeight())
227         if err != nil {
228                 t.Fatal(err)
229         }
230
231         tx, err := CreateTxFromTx(block.Transactions[0], 0, 1000000000, defaultCtrlProg)
232         if err != nil {
233                 t.Fatal(err)
234         }
235
236         block, err = NewBlock(chain, []*types.Tx{tx}, defaultCtrlProg)
237         if err != nil {
238                 t.Fatal(err)
239         }
240
241         txFees := []uint64{100000, 5000000000000}
242         for _, txFee := range txFees {
243                 coinbaseTx, err := CreateCoinbaseTx(defaultCtrlProg, block.Height, txFee)
244                 if err != nil {
245                         t.Fatal(err)
246                 }
247
248                 if err := ReplaceCoinbase(block, coinbaseTx); err != nil {
249                         t.Fatal(err)
250                 }
251
252                 if _, err = chain.ProcessBlock(block); err == nil {
253                         t.Fatalf("invalid coinbase tx validate success")
254                 }
255         }
256 }