github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/test/tx_test.go (about) 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 "github.com/bytom/bytom/protocol/bc" 19 "github.com/bytom/bytom/protocol/bc/types" 20 "github.com/bytom/bytom/protocol/validation" 21 "github.com/bytom/bytom/protocol/vm" 22 dbm "github.com/bytom/bytom/database/leveldb" 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 }