github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/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  	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  }