github.com/cgcardona/r-subnet-evm@v0.1.5/core/state_processor_test.go (about)

     1  // (c) 2019-2021, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2020 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package core
    28  
    29  import (
    30  	"math/big"
    31  	"testing"
    32  
    33  	"github.com/cgcardona/r-subnet-evm/consensus"
    34  	"github.com/cgcardona/r-subnet-evm/consensus/dummy"
    35  	"github.com/cgcardona/r-subnet-evm/core/rawdb"
    36  	"github.com/cgcardona/r-subnet-evm/core/types"
    37  	"github.com/cgcardona/r-subnet-evm/core/vm"
    38  	"github.com/cgcardona/r-subnet-evm/params"
    39  	"github.com/cgcardona/r-subnet-evm/precompile/contracts/txallowlist"
    40  	"github.com/cgcardona/r-subnet-evm/trie"
    41  	"github.com/ethereum/go-ethereum/common"
    42  	"github.com/ethereum/go-ethereum/crypto"
    43  	"golang.org/x/crypto/sha3"
    44  )
    45  
    46  var (
    47  	cpcfg      = *params.TestChainConfig
    48  	config     = &cpcfg
    49  	signer     = types.LatestSigner(config)
    50  	testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    51  )
    52  
    53  func makeTx(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction {
    54  	tx, _ := types.SignTx(types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), signer, testKey)
    55  	return tx
    56  }
    57  
    58  func mkDynamicTx(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction {
    59  	tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
    60  		Nonce:     nonce,
    61  		GasTipCap: gasTipCap,
    62  		GasFeeCap: gasFeeCap,
    63  		Gas:       gasLimit,
    64  		To:        &to,
    65  		Value:     big.NewInt(0),
    66  	}), signer, testKey)
    67  	return tx
    68  }
    69  
    70  // TestStateProcessorErrors tests the output from the 'core' errors
    71  // as defined in core/error.go. These errors are generated when the
    72  // blockchain imports bad blocks, meaning blocks which have valid headers but
    73  // contain invalid transactions
    74  func TestStateProcessorErrors(t *testing.T) {
    75  	config.FeeConfig.MinBaseFee = params.TestMaxBaseFee
    76  	{ // Tests against a 'recent' chain definition
    77  		var (
    78  			db    = rawdb.NewMemoryDatabase()
    79  			gspec = &Genesis{
    80  				Config: config,
    81  				Alloc: GenesisAlloc{
    82  					common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
    83  						Balance: big.NewInt(2000000000000000000), // 2 ether
    84  						Nonce:   0,
    85  					},
    86  				},
    87  				GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
    88  			}
    89  			genesis       = gspec.MustCommit(db)
    90  			blockchain, _ = NewBlockChain(db, DefaultCacheConfig, gspec.Config, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{})
    91  		)
    92  		defer blockchain.Stop()
    93  		bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
    94  		tooBigNumber := new(big.Int).Set(bigNumber)
    95  		tooBigNumber.Add(tooBigNumber, common.Big1)
    96  		for i, tt := range []struct {
    97  			txs  []*types.Transaction
    98  			want string
    99  		}{
   100  			{ // ErrNonceTooLow
   101  				txs: []*types.Transaction{
   102  					makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(225000000000), nil),
   103  					makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(225000000000), nil),
   104  				},
   105  				want: "could not apply tx 1 [0x734d821c990099c6ae42d78072aadd3931c35328cf03ef4cf5b2a4ac9c398522]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1",
   106  			},
   107  			{ // ErrNonceTooHigh
   108  				txs: []*types.Transaction{
   109  					makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(225000000000), nil),
   110  				},
   111  				want: "could not apply tx 0 [0x0df36254cfbef8ed6961b38fc68aecc777177166144c8a56bc8919e23a559bf4]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0",
   112  			},
   113  			{ // ErrGasLimitReached
   114  				txs: []*types.Transaction{
   115  					makeTx(0, common.Address{}, big.NewInt(0), 8000001, big.NewInt(225000000000), nil),
   116  				},
   117  				want: "could not apply tx 0 [0xfbe38b817aaa760c2766b56c019fcdba506560a28fd41c69ae96bdaa4569e317]: gas limit reached",
   118  			},
   119  			{ // ErrInsufficientFundsForTransfer
   120  				txs: []*types.Transaction{
   121  					makeTx(0, common.Address{}, big.NewInt(2000000000000000000), params.TxGas, big.NewInt(225000000000), nil),
   122  				},
   123  				want: "could not apply tx 0 [0xae1601ef55b676ebb824ee7e16a0d14af725b7f9cf5ec79e21f14833c26b5b35]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 2000000000000000000 want 2004725000000000000",
   124  			},
   125  			{ // ErrInsufficientFunds
   126  				txs: []*types.Transaction{
   127  					makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil),
   128  				},
   129  				want: "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 2000000000000000000 want 18900000000000000000000",
   130  			},
   131  			// ErrGasUintOverflow
   132  			// One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow",
   133  			// In order to trigger that one, we'd have to allocate a _huge_ chunk of data, such that the
   134  			// multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment
   135  			{ // ErrIntrinsicGas
   136  				txs: []*types.Transaction{
   137  					makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(225000000000), nil),
   138  				},
   139  				want: "could not apply tx 0 [0x2fc3e3b5cc26917d413e26983fe189475f47d4f0757e32aaa5561fcb9c9dc432]: intrinsic gas too low: have 20000, want 21000",
   140  			},
   141  			{ // ErrGasLimitReached
   142  				txs: []*types.Transaction{
   143  					makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*381, big.NewInt(225000000000), nil),
   144  				},
   145  				want: "could not apply tx 0 [0x9ee548e001369418ae53aaa11b5d823f081cc7fa9c9a7ee71a978ae17a2aece0]: gas limit reached",
   146  			},
   147  			{ // ErrFeeCapTooLow
   148  				txs: []*types.Transaction{
   149  					mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)),
   150  				},
   151  				want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 225000000000",
   152  			},
   153  			{ // ErrTipVeryHigh
   154  				txs: []*types.Transaction{
   155  					mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)),
   156  				},
   157  				want: "could not apply tx 0 [0x15b8391b9981f266b32f3ab7da564bbeb3d6c21628364ea9b32a21139f89f712]: max priority fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257",
   158  			},
   159  			{ // ErrFeeCapVeryHigh
   160  				txs: []*types.Transaction{
   161  					mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber),
   162  				},
   163  				want: "could not apply tx 0 [0x48bc299b83fdb345c57478f239e89814bb3063eb4e4b49f3b6057a69255c16bd]: max fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257",
   164  			},
   165  			{ // ErrTipAboveFeeCap
   166  				txs: []*types.Transaction{
   167  					mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)),
   168  				},
   169  				want: "could not apply tx 0 [0xf987a31ff0c71895780a7612f965a0c8b056deb54e020bb44fa478092f14c9b4]: max priority fee per gas higher than max fee per gas: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1",
   170  			},
   171  			{ // ErrInsufficientFunds
   172  				// Available balance:           1000000000000000000
   173  				// Effective cost:                   18375000021000
   174  				// FeeCap * gas:                1050000000000000000
   175  				// This test is designed to have the effective cost be covered by the balance, but
   176  				// the extended requirement on FeeCap*gas < balance to fail
   177  				txs: []*types.Transaction{
   178  					mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(100000000000000)),
   179  				},
   180  				want: "could not apply tx 0 [0x3388378ed60640e75d2edf728d5528a305f599997abc4f23ec46b351b6197499]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 2000000000000000000 want 2100000000000000000",
   181  			},
   182  			{ // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed
   183  				txs: []*types.Transaction{
   184  					mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber),
   185  				},
   186  				want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 2000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
   187  			},
   188  		} {
   189  			block := GenerateBadBlock(genesis, dummy.NewCoinbaseFaker(), tt.txs, gspec.Config)
   190  			_, err := blockchain.InsertChain(types.Blocks{block})
   191  			if err == nil {
   192  				t.Fatal("block imported without errors")
   193  			}
   194  			if have, want := err.Error(), tt.want; have != want {
   195  				t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
   196  			}
   197  		}
   198  	}
   199  
   200  	// ErrTxTypeNotSupported, For this, we need an older chain
   201  	{
   202  		var (
   203  			db    = rawdb.NewMemoryDatabase()
   204  			gspec = &Genesis{
   205  				Config: &params.ChainConfig{
   206  					ChainID:             big.NewInt(1),
   207  					FeeConfig:           params.DefaultFeeConfig,
   208  					HomesteadBlock:      big.NewInt(0),
   209  					EIP150Block:         big.NewInt(0),
   210  					EIP150Hash:          common.Hash{},
   211  					EIP155Block:         big.NewInt(0),
   212  					EIP158Block:         big.NewInt(0),
   213  					ByzantiumBlock:      big.NewInt(0),
   214  					ConstantinopleBlock: big.NewInt(0),
   215  					PetersburgBlock:     big.NewInt(0),
   216  					IstanbulBlock:       big.NewInt(0),
   217  					MuirGlacierBlock:    big.NewInt(0),
   218  				},
   219  				Alloc: GenesisAlloc{
   220  					common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
   221  						Balance: big.NewInt(1000000000000000000), // 1 ether
   222  						Nonce:   0,
   223  					},
   224  				},
   225  				GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
   226  			}
   227  			genesis       = gspec.MustCommit(db)
   228  			blockchain, _ = NewBlockChain(db, DefaultCacheConfig, gspec.Config, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{})
   229  		)
   230  		defer blockchain.Stop()
   231  		for i, tt := range []struct {
   232  			txs  []*types.Transaction
   233  			want string
   234  		}{
   235  			{ // ErrTxTypeNotSupported
   236  				txs: []*types.Transaction{
   237  					mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
   238  				},
   239  				want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported",
   240  			},
   241  		} {
   242  			block := GenerateBadBlock(genesis, dummy.NewCoinbaseFaker(), tt.txs, gspec.Config)
   243  			_, err := blockchain.InsertChain(types.Blocks{block})
   244  			if err == nil {
   245  				t.Fatal("block imported without errors")
   246  			}
   247  			if have, want := err.Error(), tt.want; have != want {
   248  				t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
   249  			}
   250  		}
   251  	}
   252  
   253  	// ErrSenderNoEOA, for this we need the sender to have contract code
   254  	{
   255  		var (
   256  			db    = rawdb.NewMemoryDatabase()
   257  			gspec = &Genesis{
   258  				Config: config,
   259  				Alloc: GenesisAlloc{
   260  					common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
   261  						Balance: big.NewInt(1000000000000000000), // 1 ether
   262  						Nonce:   0,
   263  						Code:    common.FromHex("0xB0B0FACE"),
   264  					},
   265  				},
   266  				GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
   267  			}
   268  			genesis       = gspec.MustCommit(db)
   269  			blockchain, _ = NewBlockChain(db, DefaultCacheConfig, gspec.Config, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{})
   270  		)
   271  		defer blockchain.Stop()
   272  		for i, tt := range []struct {
   273  			txs  []*types.Transaction
   274  			want string
   275  		}{
   276  			{ // ErrSenderNoEOA
   277  				txs: []*types.Transaction{
   278  					mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
   279  				},
   280  				want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
   281  			},
   282  		} {
   283  			block := GenerateBadBlock(genesis, dummy.NewCoinbaseFaker(), tt.txs, gspec.Config)
   284  			_, err := blockchain.InsertChain(types.Blocks{block})
   285  			if err == nil {
   286  				t.Fatal("block imported without errors")
   287  			}
   288  			if have, want := err.Error(), tt.want; have != want {
   289  				t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
   290  			}
   291  		}
   292  	}
   293  }
   294  
   295  // TestBadTxAllowListBlock tests the output generated when the
   296  // blockchain imports a bad block with a transaction from a
   297  // non-whitelisted TX Allow List address.
   298  func TestBadTxAllowListBlock(t *testing.T) {
   299  	var (
   300  		db       = rawdb.NewMemoryDatabase()
   301  		testAddr = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
   302  
   303  		config = &params.ChainConfig{
   304  			ChainID:             big.NewInt(1),
   305  			FeeConfig:           params.DefaultFeeConfig,
   306  			HomesteadBlock:      big.NewInt(0),
   307  			EIP150Block:         big.NewInt(0),
   308  			EIP150Hash:          common.Hash{},
   309  			EIP155Block:         big.NewInt(0),
   310  			EIP158Block:         big.NewInt(0),
   311  			ByzantiumBlock:      big.NewInt(0),
   312  			ConstantinopleBlock: big.NewInt(0),
   313  			PetersburgBlock:     big.NewInt(0),
   314  			IstanbulBlock:       big.NewInt(0),
   315  			MuirGlacierBlock:    big.NewInt(0),
   316  			MandatoryNetworkUpgrades: params.MandatoryNetworkUpgrades{
   317  				SubnetEVMTimestamp: big.NewInt(0),
   318  			},
   319  			GenesisPrecompiles: params.Precompiles{
   320  				txallowlist.ConfigKey: txallowlist.NewConfig(big.NewInt(0), nil, nil),
   321  			},
   322  		}
   323  		signer     = types.LatestSigner(config)
   324  		testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   325  
   326  		gspec = &Genesis{
   327  			Config: config,
   328  			Alloc: GenesisAlloc{
   329  				testAddr: GenesisAccount{
   330  					Balance: big.NewInt(1000000000000000000), // 1 ether
   331  					Nonce:   0,
   332  				},
   333  			},
   334  
   335  			GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
   336  		}
   337  		genesis       = gspec.MustCommit(db)
   338  		blockchain, _ = NewBlockChain(db, DefaultCacheConfig, gspec.Config, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{})
   339  	)
   340  
   341  	mkDynamicTx := func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction {
   342  		tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
   343  			Nonce:     nonce,
   344  			GasTipCap: gasTipCap,
   345  			GasFeeCap: gasFeeCap,
   346  			Gas:       gasLimit,
   347  			To:        &to,
   348  			Value:     big.NewInt(0),
   349  		}), signer, testKey)
   350  		return tx
   351  	}
   352  
   353  	defer blockchain.Stop()
   354  	for i, tt := range []struct {
   355  		txs  []*types.Transaction
   356  		want string
   357  	}{
   358  		{ // Nonwhitelisted address
   359  			txs: []*types.Transaction{
   360  				mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(225000000000)),
   361  			},
   362  			want: "could not apply tx 0 [0xc5725e8baac950b2925dd4fea446ccddead1cc0affdae18b31a7d910629d9225]: cannot issue transaction from non-allow listed address: 0x71562b71999873DB5b286dF957af199Ec94617F7",
   363  		},
   364  	} {
   365  		block := GenerateBadBlock(genesis, dummy.NewCoinbaseFaker(), tt.txs, gspec.Config)
   366  		_, err := blockchain.InsertChain(types.Blocks{block})
   367  		if err == nil {
   368  			t.Fatal("block imported without errors")
   369  		}
   370  		if have, want := err.Error(), tt.want; have != want {
   371  			t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
   372  		}
   373  	}
   374  }
   375  
   376  // GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
   377  // valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently
   378  // valid to be considered for import:
   379  // - valid pow (fake), ancestry, difficulty, gaslimit etc
   380  func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
   381  	header := &types.Header{
   382  		ParentHash: parent.Hash(),
   383  		Coinbase:   parent.Coinbase(),
   384  		Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
   385  			Number:     parent.Number(),
   386  			Time:       parent.Time(),
   387  			Difficulty: parent.Difficulty(),
   388  			UncleHash:  parent.UncleHash(),
   389  		}),
   390  		GasLimit:  parent.GasLimit(),
   391  		Number:    new(big.Int).Add(parent.Number(), common.Big1),
   392  		Time:      parent.Time() + 10,
   393  		UncleHash: types.EmptyUncleHash,
   394  	}
   395  
   396  	if config.IsSubnetEVM(new(big.Int).SetUint64(header.Time)) {
   397  		header.Extra, header.BaseFee, _ = dummy.CalcBaseFee(config, config.FeeConfig, parent.Header(), header.Time)
   398  		header.BlockGasCost = big.NewInt(0)
   399  	}
   400  	var receipts []*types.Receipt
   401  	// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
   402  	// Preferably something unique. So let's use a combo of blocknum + txhash
   403  	hasher := sha3.NewLegacyKeccak256()
   404  	hasher.Write(header.Number.Bytes())
   405  	var cumulativeGas uint64
   406  	for _, tx := range txs {
   407  		txh := tx.Hash()
   408  		hasher.Write(txh[:])
   409  		receipt := types.NewReceipt(nil, false, cumulativeGas+tx.Gas())
   410  		receipt.TxHash = tx.Hash()
   411  		receipt.GasUsed = tx.Gas()
   412  		receipts = append(receipts, receipt)
   413  		cumulativeGas += tx.Gas()
   414  	}
   415  	header.Root = common.BytesToHash(hasher.Sum(nil))
   416  	// Assemble and return the final block for sealing
   417  	return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
   418  }