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  }