github.com/klaytn/klaytn@v1.12.1/accounts/abi/bind/backends/blockchain_test.go (about)

     1  // Copyright 2023 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backends
    18  
    19  import (
    20  	"context"
    21  	"encoding/hex"
    22  	"errors"
    23  	"math/big"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/golang/mock/gomock"
    29  	"github.com/klaytn/klaytn"
    30  	"github.com/klaytn/klaytn/accounts/abi"
    31  	"github.com/klaytn/klaytn/blockchain"
    32  	"github.com/klaytn/klaytn/blockchain/types"
    33  	"github.com/klaytn/klaytn/blockchain/vm"
    34  	"github.com/klaytn/klaytn/common"
    35  	"github.com/klaytn/klaytn/consensus/gxhash"
    36  	"github.com/klaytn/klaytn/crypto"
    37  	"github.com/klaytn/klaytn/event"
    38  	"github.com/klaytn/klaytn/node/cn/filters"
    39  	mock_filter "github.com/klaytn/klaytn/node/cn/filters/mock"
    40  	"github.com/klaytn/klaytn/params"
    41  	"github.com/klaytn/klaytn/storage/database"
    42  	"github.com/stretchr/testify/assert"
    43  )
    44  
    45  var (
    46  	testAddr  = crypto.PubkeyToAddress(testKey.PublicKey)
    47  	code1Addr = common.HexToAddress("0x1111111111111111111111111111111111111111")
    48  	code2Addr = common.HexToAddress("0x2222222222222222222222222222222222222222")
    49  
    50  	parsedAbi1, _ = abi.JSON(strings.NewReader(abiJSON))
    51  	parsedAbi2, _ = abi.JSON(strings.NewReader(reverterABI))
    52  	code1Bytes    = common.FromHex(deployedCode)
    53  	code2Bytes    = common.FromHex(reverterDeployedBin)
    54  )
    55  
    56  func newTestBlockchain() *blockchain.BlockChain {
    57  	config := params.TestChainConfig.Copy()
    58  	return newTestBlockchainWithConfig(config)
    59  }
    60  
    61  func newTestBlockchainWithConfig(config *params.ChainConfig) *blockchain.BlockChain {
    62  	alloc := blockchain.GenesisAlloc{
    63  		testAddr:  {Balance: big.NewInt(10000000000)},
    64  		code1Addr: {Balance: big.NewInt(0), Code: code1Bytes},
    65  		code2Addr: {Balance: big.NewInt(0), Code: code2Bytes},
    66  	}
    67  
    68  	db := database.NewMemoryDBManager()
    69  	genesis := blockchain.Genesis{Config: config, Alloc: alloc}
    70  	genesis.MustCommit(db)
    71  
    72  	bc, _ := blockchain.NewBlockChain(db, nil, genesis.Config, gxhash.NewFaker(), vm.Config{})
    73  
    74  	// Append 10 blocks to test with block numbers other than 0
    75  	block := bc.CurrentBlock()
    76  	blocks, _ := blockchain.GenerateChain(config, block, gxhash.NewFaker(), db, 10, func(i int, b *blockchain.BlockGen) {})
    77  	bc.InsertChain(blocks)
    78  
    79  	return bc
    80  }
    81  
    82  func TestBlockchainCodeAt(t *testing.T) {
    83  	bc := newTestBlockchain()
    84  	c := NewBlockchainContractBackend(bc, nil, nil)
    85  
    86  	// Normal cases
    87  	code, err := c.CodeAt(context.Background(), code1Addr, nil)
    88  	assert.Nil(t, err)
    89  	assert.Equal(t, code1Bytes, code)
    90  
    91  	code, err = c.CodeAt(context.Background(), code2Addr, nil)
    92  	assert.Nil(t, err)
    93  	assert.Equal(t, code2Bytes, code)
    94  
    95  	code, err = c.CodeAt(context.Background(), code1Addr, common.Big0)
    96  	assert.Nil(t, err)
    97  	assert.Equal(t, code1Bytes, code)
    98  
    99  	code, err = c.CodeAt(context.Background(), code1Addr, common.Big1)
   100  	assert.Nil(t, err)
   101  	assert.Equal(t, code1Bytes, code)
   102  
   103  	code, err = c.CodeAt(context.Background(), code1Addr, big.NewInt(10))
   104  	assert.Nil(t, err)
   105  	assert.Equal(t, code1Bytes, code)
   106  
   107  	// Non-code address
   108  	code, err = c.CodeAt(context.Background(), testAddr, nil)
   109  	assert.True(t, code == nil && err == nil)
   110  
   111  	// Invalid block number
   112  	code, err = c.CodeAt(context.Background(), code1Addr, big.NewInt(11))
   113  	assert.True(t, code == nil && err == errBlockDoesNotExist)
   114  }
   115  
   116  func TestBlockchainCallContract(t *testing.T) {
   117  	bc := newTestBlockchain()
   118  	c := NewBlockchainContractBackend(bc, nil, nil)
   119  
   120  	data_receive, _ := parsedAbi1.Pack("receive", []byte("X"))
   121  	data_revertString, _ := parsedAbi2.Pack("revertString")
   122  	data_revertNoString, _ := parsedAbi2.Pack("revertNoString")
   123  
   124  	// Normal case
   125  	ret, err := c.CallContract(context.Background(), klaytn.CallMsg{
   126  		From: testAddr,
   127  		To:   &code1Addr,
   128  		Gas:  1000000,
   129  		Data: data_receive,
   130  	}, nil)
   131  	assert.Nil(t, err)
   132  	assert.Equal(t, expectedReturn, ret)
   133  
   134  	// Error outside VM - Intrinsic Gas
   135  	ret, err = c.CallContract(context.Background(), klaytn.CallMsg{
   136  		From: testAddr,
   137  		To:   &code1Addr,
   138  		Gas:  20000,
   139  		Data: data_receive,
   140  	}, nil)
   141  	assert.True(t, errors.Is(err, blockchain.ErrIntrinsicGas))
   142  
   143  	// VM revert error - empty reason
   144  	ret, err = c.CallContract(context.Background(), klaytn.CallMsg{
   145  		From: testAddr,
   146  		To:   &code2Addr,
   147  		Gas:  100000,
   148  		Data: data_revertNoString,
   149  	}, nil)
   150  	assert.Equal(t, "execution reverted: ", err.Error())
   151  
   152  	// VM revert error - string reason
   153  	ret, err = c.CallContract(context.Background(), klaytn.CallMsg{
   154  		From: testAddr,
   155  		To:   &code2Addr,
   156  		Gas:  100000,
   157  		Data: data_revertString,
   158  	}, nil)
   159  	assert.Equal(t, "execution reverted: some error", err.Error())
   160  }
   161  
   162  func TestBlockchainPendingCodeAt(t *testing.T) {
   163  	bc := newTestBlockchain()
   164  	c := NewBlockchainContractBackend(bc, nil, nil)
   165  
   166  	// Normal cases
   167  	code, err := c.PendingCodeAt(context.Background(), code1Addr)
   168  	assert.Nil(t, err)
   169  	assert.Equal(t, code1Bytes, code)
   170  
   171  	code, err = c.PendingCodeAt(context.Background(), code2Addr)
   172  	assert.Nil(t, err)
   173  	assert.Equal(t, code2Bytes, code)
   174  
   175  	// Non-code address
   176  	code, err = c.PendingCodeAt(context.Background(), testAddr)
   177  	assert.True(t, code == nil && err == nil)
   178  }
   179  
   180  func TestBlockChainSuggestGasPrice(t *testing.T) {
   181  	bc := newTestBlockchain()
   182  	c := NewBlockchainContractBackend(bc, nil, nil)
   183  
   184  	// Normal case
   185  	gasPrice, err := c.SuggestGasPrice(context.Background())
   186  	assert.Nil(t, err)
   187  	assert.Equal(t, params.TestChainConfig.UnitPrice, gasPrice.Uint64())
   188  
   189  	config := params.TestChainConfig.Copy()
   190  	config.IstanbulCompatibleBlock = common.Big0
   191  	config.LondonCompatibleBlock = common.Big0
   192  	config.EthTxTypeCompatibleBlock = common.Big0
   193  	config.MagmaCompatibleBlock = common.Big0
   194  	config.KoreCompatibleBlock = common.Big0
   195  	config.Governance = params.GetDefaultGovernanceConfig()
   196  	config.Governance.KIP71.LowerBoundBaseFee = 0
   197  	bc = newTestBlockchainWithConfig(config)
   198  	c = NewBlockchainContractBackend(bc, nil, nil)
   199  
   200  	// Normal case
   201  	gasPrice, err = c.SuggestGasPrice(context.Background())
   202  	assert.Nil(t, err)
   203  	assert.Equal(t, bc.CurrentBlock().Header().BaseFee.Uint64()*2, gasPrice.Uint64())
   204  }
   205  
   206  func TestBlockChainEstimateGas(t *testing.T) {
   207  	bc := newTestBlockchain()
   208  	c := NewBlockchainContractBackend(bc, nil, nil)
   209  
   210  	// Normal case
   211  	gas, err := c.EstimateGas(context.Background(), klaytn.CallMsg{
   212  		From:  testAddr,
   213  		To:    &testAddr,
   214  		Value: big.NewInt(1000),
   215  	})
   216  	assert.Nil(t, err)
   217  	assert.Equal(t, uint64(params.TxGas), gas)
   218  
   219  	// Error case - simple transfer with insufficient funds with zero gasPrice
   220  	gas, err = c.EstimateGas(context.Background(), klaytn.CallMsg{
   221  		From:  code1Addr,
   222  		To:    &code1Addr,
   223  		Value: big.NewInt(1),
   224  	})
   225  	assert.Contains(t, err.Error(), "insufficient balance for transfer")
   226  	assert.Zero(t, gas)
   227  }
   228  
   229  func TestBlockChainSendTransaction(t *testing.T) {
   230  	bc := newTestBlockchain()
   231  	block := bc.CurrentBlock()
   232  	state, err := bc.State()
   233  	txPoolConfig := blockchain.DefaultTxPoolConfig
   234  	txPoolConfig.Journal = "/dev/null" // disable journaling to file
   235  	txPool := blockchain.NewTxPool(txPoolConfig, bc.Config(), bc)
   236  	defer txPool.Stop()
   237  	assert.Nil(t, err)
   238  	c := NewBlockchainContractBackend(bc, txPool, nil)
   239  
   240  	// create a signed transaction to send
   241  	nonce := state.GetNonce(testAddr)
   242  	tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
   243  	chainId, err := c.ChainID(context.Background())
   244  	if err != nil {
   245  		t.Errorf("could not get chain ID: %v", err)
   246  	}
   247  	signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainId), testKey)
   248  	if err != nil {
   249  		t.Errorf("could not sign tx: %v", err)
   250  	}
   251  
   252  	// send tx to simulated backend
   253  	err = c.SendTransaction(context.Background(), signedTx)
   254  	if err != nil {
   255  		t.Errorf("could not add tx to pending block: %v", err)
   256  	}
   257  
   258  	blocks, _ := blockchain.GenerateChain(bc.Config(), block, gxhash.NewFaker(), state.Database().TrieDB().DiskDB(), 1, func(i int, b *blockchain.BlockGen) {
   259  		txs, err := txPool.Pending()
   260  		if err != nil {
   261  			t.Errorf("could not get pending txs: %v", err)
   262  		}
   263  		for _, v := range txs {
   264  			for _, v2 := range v {
   265  				b.AddTx(v2)
   266  			}
   267  		}
   268  	})
   269  	bc.InsertChain(blocks)
   270  
   271  	block = bc.GetBlockByNumber(11)
   272  	if block == nil {
   273  		t.Errorf("could not get block at height 11")
   274  	}
   275  
   276  	assert.True(t, len(block.Transactions()) != 0)
   277  	if signedTx.Hash() != block.Transactions()[0].Hash() {
   278  		t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash())
   279  	}
   280  	assert.False(t, block.Header().EmptyReceipts())
   281  }
   282  
   283  func TestBlockChainChainID(t *testing.T) {
   284  	bc := newTestBlockchain()
   285  	c := NewBlockchainContractBackend(bc, nil, nil)
   286  
   287  	// Normal case
   288  	chainId, err := c.ChainID(context.Background())
   289  	assert.Nil(t, err)
   290  	assert.Equal(t, params.TestChainConfig.ChainID, chainId)
   291  }
   292  
   293  func initBackendForFiltererTests(t *testing.T, bc *blockchain.BlockChain) *BlockchainContractBackend {
   294  	block := bc.CurrentBlock()
   295  	state, _ := bc.State()
   296  
   297  	// Add one block with contract execution to generate logs
   298  	data_receive, _ := parsedAbi1.Pack("receive", []byte("X"))
   299  	tx := types.NewTransaction(uint64(0), code1Addr, big.NewInt(0), 50000, big.NewInt(1), data_receive)
   300  	chainId := bc.Config().ChainID
   301  
   302  	signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainId), testKey)
   303  	if err != nil {
   304  		t.Errorf("could not sign tx: %v", err)
   305  	}
   306  
   307  	blocks, _ := blockchain.GenerateChain(bc.Config(), block, gxhash.NewFaker(), state.Database().TrieDB().DiskDB(), 1, func(i int, b *blockchain.BlockGen) {
   308  		b.AddTx(signedTx)
   309  	})
   310  	bc.InsertChain(blocks)
   311  
   312  	// mock filterer backend
   313  	mockCtrl := gomock.NewController(t)
   314  	mockBackend := mock_filter.NewMockBackend(mockCtrl)
   315  
   316  	any := gomock.Any()
   317  	txPoolConfig := blockchain.DefaultTxPoolConfig
   318  	txPoolConfig.Journal = "/dev/null" // disable journaling to file
   319  	txPool := blockchain.NewTxPool(txPoolConfig, bc.Config(), bc)
   320  	subscribeNewTxsEvent := func(ch chan<- blockchain.NewTxsEvent) klaytn.Subscription {
   321  		return txPool.SubscribeNewTxsEvent(ch)
   322  	}
   323  	subscribeLogsEvent := func(ch chan<- []*types.Log) klaytn.Subscription {
   324  		return bc.SubscribeLogsEvent(ch)
   325  	}
   326  	subscribeRemovedLogsEvent := func(ch chan<- blockchain.RemovedLogsEvent) klaytn.Subscription {
   327  		return bc.SubscribeRemovedLogsEvent(ch)
   328  	}
   329  	subscribeChainEvent := func(ch chan<- blockchain.ChainEvent) klaytn.Subscription {
   330  		return bc.SubscribeChainEvent(ch)
   331  	}
   332  	mockBackend.EXPECT().SubscribeNewTxsEvent(any).DoAndReturn(subscribeNewTxsEvent).AnyTimes()
   333  	mockBackend.EXPECT().SubscribeLogsEvent(any).DoAndReturn(subscribeLogsEvent).AnyTimes()
   334  	mockBackend.EXPECT().SubscribeRemovedLogsEvent(any).DoAndReturn(subscribeRemovedLogsEvent).AnyTimes()
   335  	mockBackend.EXPECT().SubscribeChainEvent(any).DoAndReturn(subscribeChainEvent).AnyTimes()
   336  
   337  	f := filters.NewEventSystem(&event.TypeMux{}, mockBackend, false)
   338  	c := NewBlockchainContractBackend(bc, nil, f)
   339  
   340  	return c
   341  }
   342  
   343  func TestBlockChainFilterLogs(t *testing.T) {
   344  	bc := newTestBlockchain()
   345  	c := initBackendForFiltererTests(t, bc)
   346  
   347  	// Normal case
   348  	logs, err := c.FilterLogs(context.Background(), klaytn.FilterQuery{
   349  		FromBlock: big.NewInt(10),
   350  		ToBlock:   big.NewInt(11),
   351  		Addresses: []common.Address{code1Addr},
   352  	})
   353  	assert.Nil(t, err)
   354  	assert.Equal(t, 2, len(logs))
   355  
   356  	// No logs exist for code2Addr
   357  	logs, err = c.FilterLogs(context.Background(), klaytn.FilterQuery{
   358  		FromBlock: big.NewInt(0),
   359  		ToBlock:   big.NewInt(11),
   360  		Addresses: []common.Address{code2Addr},
   361  	})
   362  	assert.Nil(t, err)
   363  	assert.Equal(t, 0, len(logs))
   364  }
   365  
   366  func TestBlockChainSubscribeFilterLogs(t *testing.T) {
   367  	bc := newTestBlockchain()
   368  	c := initBackendForFiltererTests(t, bc)
   369  
   370  	logs := make(chan types.Log)
   371  	sub, err := c.SubscribeFilterLogs(context.Background(), klaytn.FilterQuery{
   372  		FromBlock: big.NewInt(0),
   373  		ToBlock:   big.NewInt(20),
   374  		Addresses: []common.Address{code1Addr},
   375  	}, logs)
   376  	assert.Nil(t, err)
   377  	assert.NotNil(t, sub)
   378  
   379  	// Insert a block with contract execution to generate logs
   380  	go func() {
   381  		state, _ := bc.State()
   382  		nonce := state.GetNonce(testAddr)
   383  		data_receive, _ := parsedAbi1.Pack("receive", []byte("X"))
   384  		tx := types.NewTransaction(nonce, code1Addr, big.NewInt(0), 50000, big.NewInt(1), data_receive)
   385  		chainId, _ := c.ChainID(context.Background())
   386  
   387  		signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainId), testKey)
   388  		if err != nil {
   389  			t.Errorf("could not sign tx: %v", err)
   390  		}
   391  
   392  		block := bc.CurrentBlock()
   393  
   394  		blocks, _ := blockchain.GenerateChain(c.bc.Config(), block, gxhash.NewFaker(), state.Database().TrieDB().DiskDB(), 1, func(i int, b *blockchain.BlockGen) {
   395  			b.AddTx(signedTx)
   396  		})
   397  		bc.InsertChain(blocks)
   398  	}()
   399  
   400  	// Wait for 2 logs
   401  	for i := 0; i < 2; i++ {
   402  		select {
   403  		case log := <-logs:
   404  			assert.Equal(t, code1Addr, log.Address)
   405  			assert.Contains(t, hex.EncodeToString(log.Data), hex.EncodeToString(testAddr.Bytes()))
   406  		case <-time.After(3 * time.Second):
   407  			t.Fatal("timeout while waiting for logs")
   408  		}
   409  	}
   410  }