OSDN Git Service

Hulk did something
[bytom/vapor.git] / test / tx_test.go
diff --git a/test/tx_test.go b/test/tx_test.go
new file mode 100644 (file)
index 0000000..06bccbd
--- /dev/null
@@ -0,0 +1,264 @@
+// +build functional
+
+package test
+
+import (
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "testing"
+
+       log "github.com/sirupsen/logrus"
+
+       "github.com/vapor/account"
+       "github.com/vapor/asset"
+       "github.com/vapor/blockchain/pseudohsm"
+       "github.com/vapor/consensus"
+       dbm "github.com/vapor/database/leveldb"
+       "github.com/vapor/protocol/bc"
+       "github.com/vapor/protocol/bc/types"
+       "github.com/vapor/protocol/validation"
+       "github.com/vapor/protocol/vm"
+)
+
+func init() {
+       log.SetLevel(log.ErrorLevel)
+}
+
+type TxTestConfig struct {
+       Keys         []*keyInfo       `json:"keys"`
+       Accounts     []*accountInfo   `json:"accounts"`
+       Transactions []*ttTransaction `json:"transactions"`
+}
+
+func (cfg *TxTestConfig) Run() error {
+       dirPath, err := ioutil.TempDir(".", "pseudo_hsm")
+       if err != nil {
+               return err
+       }
+       defer os.RemoveAll(dirPath)
+       hsm, err := pseudohsm.New(dirPath)
+       if err != nil {
+               return err
+       }
+
+       chainDB := dbm.NewDB("chain_db", "leveldb", "chain_db")
+       defer os.RemoveAll("chain_db")
+       chain, _, _, _ := MockChain(chainDB)
+       txTestDB := dbm.NewDB("tx_test_db", "leveldb", "tx_test_db")
+       defer os.RemoveAll("tx_test_db")
+       accountManager := account.NewManager(txTestDB, chain)
+       assets := asset.NewRegistry(txTestDB, chain)
+
+       generator := NewTxGenerator(accountManager, assets, hsm)
+       for _, key := range cfg.Keys {
+               if err := generator.createKey(key.Name, key.Password); err != nil {
+                       return err
+               }
+       }
+
+       for _, acc := range cfg.Accounts {
+               if err := generator.createAccount(acc.Name, acc.Keys, acc.Quorum); err != nil {
+                       return err
+               }
+       }
+
+       block := &bc.Block{
+               BlockHeader: &bc.BlockHeader{
+                       Height:  1,
+                       Version: 1,
+               },
+       }
+       for _, t := range cfg.Transactions {
+               tx, err := t.create(generator)
+               if err != nil {
+                       return err
+               }
+
+               tx.TxData.Version = t.Version
+               tx.Tx = types.MapTx(&tx.TxData)
+               status, err := validation.ValidateTx(tx.Tx, block)
+               result := err == nil
+               if result != t.Valid {
+                       return fmt.Errorf("tx %s validate failed, expected: %t, have: %t", t.Describe, t.Valid, result)
+               }
+               if status == nil {
+                       continue
+               }
+
+               gasOnlyTx := false
+               if err != nil && status.GasValid {
+                       gasOnlyTx = true
+               }
+               if gasOnlyTx != t.GasOnly {
+                       return fmt.Errorf("gas only tx %s validate failed", t.Describe)
+               }
+               if result && t.TxFee != status.BTMValue {
+                       return fmt.Errorf("gas used dismatch, expected: %d, have: %d", t.TxFee, status.BTMValue)
+               }
+       }
+       return nil
+}
+
+type ttTransaction struct {
+       wtTransaction
+       Describe string `json:"describe"`
+       Version  uint64 `json:"version"`
+       Valid    bool   `json:"valid"`
+       GasOnly  bool   `json:"gas_only"`
+       TxFee    uint64 `json:"tx_fee"`
+}
+
+// UnmarshalJSON unmarshal transaction with default version 1
+func (t *ttTransaction) UnmarshalJSON(data []byte) error {
+       type typeAlias ttTransaction
+       tx := &typeAlias{
+               Version: 1,
+       }
+
+       err := json.Unmarshal(data, tx)
+       if err != nil {
+               return err
+       }
+       *t = ttTransaction(*tx)
+       return nil
+}
+
+func (t *ttTransaction) create(g *TxGenerator) (*types.Tx, error) {
+       g.Reset()
+       for _, input := range t.Inputs {
+               switch input.Type {
+               case "spend_account":
+                       utxo, err := g.mockUtxo(input.AccountAlias, input.AssetAlias, input.Amount)
+                       if err != nil {
+                               return nil, err
+                       }
+                       if err := g.AddTxInputFromUtxo(utxo, input.AccountAlias); err != nil {
+                               return nil, err
+                       }
+               case "issue":
+                       _, err := g.createAsset(input.AccountAlias, input.AssetAlias)
+                       if err != nil {
+                               return nil, err
+                       }
+                       if err := g.AddIssuanceInput(input.AssetAlias, input.Amount); err != nil {
+                               return nil, err
+                       }
+               }
+       }
+
+       for _, output := range t.Outputs {
+               switch output.Type {
+               case "output":
+                       if err := g.AddTxOutput(output.AccountAlias, output.AssetAlias, output.Amount); err != nil {
+                               return nil, err
+                       }
+               case "retire":
+                       if err := g.AddRetirement(output.AssetAlias, output.Amount); err != nil {
+                               return nil, err
+                       }
+               }
+       }
+       return g.Sign(t.Passwords)
+}
+
+func TestTx(t *testing.T) {
+       walk(t, txTestDir, func(t *testing.T, name string, test *TxTestConfig) {
+               if err := test.Run(); err != nil {
+                       t.Fatal(err)
+               }
+       })
+}
+
+func TestCoinbaseMature(t *testing.T) {
+       db := dbm.NewDB("test_coinbase_mature_db", "leveldb", "test_coinbase_mature_db")
+       defer os.RemoveAll("test_coinbase_mature_db")
+       chain, _, _, _ := MockChain(db)
+
+       defaultCtrlProg := []byte{byte(vm.OP_TRUE)}
+       if err := AppendBlocks(chain, 1); err != nil {
+               t.Fatal(err)
+       }
+
+       height := chain.BestBlockHeight()
+       block, err := chain.GetBlockByHeight(height)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       tx, err := CreateTxFromTx(block.Transactions[0], 0, 1000000000, defaultCtrlProg)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       txs := []*types.Tx{tx}
+       matureHeight := chain.BestBlockHeight() + consensus.CoinbasePendingBlockNumber
+       currentHeight := chain.BestBlockHeight()
+       for h := currentHeight + 1; h < matureHeight; h++ {
+               block, err := NewBlock(chain, txs, defaultCtrlProg)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if err := SolveAndUpdate(chain, block); err == nil {
+                       t.Fatal("spent immature coinbase output success")
+               }
+               block, err = NewBlock(chain, nil, defaultCtrlProg)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if err := SolveAndUpdate(chain, block); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       block, err = NewBlock(chain, txs, defaultCtrlProg)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := SolveAndUpdate(chain, block); err != nil {
+               t.Fatalf("spent mature coinbase output failed: %s", err)
+       }
+}
+
+func TestCoinbaseTx(t *testing.T) {
+       db := dbm.NewDB("test_coinbase_tx_db", "leveldb", "test_coinbase_tx_db")
+       defer os.RemoveAll("test_coinbase_tx_db")
+       chain, _, _, _ := MockChain(db)
+
+       defaultCtrlProg := []byte{byte(vm.OP_TRUE)}
+       if err := AppendBlocks(chain, 1); err != nil {
+               t.Fatal(err)
+       }
+
+       block, err := chain.GetBlockByHeight(chain.BestBlockHeight())
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       tx, err := CreateTxFromTx(block.Transactions[0], 0, 1000000000, defaultCtrlProg)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       block, err = NewBlock(chain, []*types.Tx{tx}, defaultCtrlProg)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       txFees := []uint64{100000, 5000000000000}
+       for _, txFee := range txFees {
+               coinbaseTx, err := CreateCoinbaseTx(defaultCtrlProg, block.Height, txFee)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               if err := ReplaceCoinbase(block, coinbaseTx); err != nil {
+                       t.Fatal(err)
+               }
+
+               if err := SolveAndUpdate(chain, block); err == nil {
+                       t.Fatalf("invalid coinbase tx validate success")
+               }
+       }
+}