From c8b477139f67f97d76c48d72be567fcc76bd4657 Mon Sep 17 00:00:00 2001 From: novosandara <160175841+novosandara@users.noreply.github.com> Date: Wed, 27 Mar 2024 08:44:52 +0100 Subject: [PATCH] Implemented GetHeaderByNumber and GetHeaderByHash (#149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * implemented methods: eth_getHeaderByNumber eth_getHeaderByHash. * The following methods were created: eth_createAccessList, eth_coinbase, eth_getBlockReceipts, txpool_ContentFrom * fix lint * CR fix, part 1 * CR fix, part 2 * lint fix * created UTs for methods: eth_createAccessList, eth_getBlockReceipts, txpool_ContentFrom * e2e test for eth_getBlockReceipts * e2e remove * CR fix * Remove dummy comment * CR fix * lint fix * CR fix and new tests e2e have been added * Address comments * Minor simplification * Minor changes in eth_getBlockReceipts * e2e test fix and cr fix * lint fix * e2e --------- Co-authored-by: Stefan Negovanović --- e2e-polybft/e2e/bridge_test.go | 4 +- e2e-polybft/e2e/consensus_test.go | 1 + e2e-polybft/e2e/jsonrpc_test.go | 36 +++- e2e-polybft/e2e/txpool_test.go | 3 + e2e-polybft/property/property_test.go | 3 + jsonrpc/codec_test.go | 2 + jsonrpc/dispatcher.go | 1 + jsonrpc/dispatcher_test.go | 4 + jsonrpc/eth_blockchain_test.go | 211 +++++++++++++++++++++++ jsonrpc/eth_endpoint.go | 135 ++++++++++++++- jsonrpc/eth_state_test.go | 3 + jsonrpc/filter_manager.go | 1 + jsonrpc/filter_manager_fuzz_test.go | 3 + jsonrpc/filter_manager_test.go | 13 +- jsonrpc/helper.go | 1 + jsonrpc/jsonrpc.go | 2 + jsonrpc/txpool_endpoint.go | 25 +++ jsonrpc/txpool_endpoint_test.go | 126 ++++++++++++++ jsonrpc/types.go | 89 +++++++--- jsonrpc/types_test.go | 13 +- network/identity_e2e_test.go | 1 + network/server_test.go | 4 + state/executor.go | 3 +- state/immutable-trie/copy_trie_test.go | 6 + state/immutable-trie/hasher.go | 1 + state/runtime/access_list.go | 25 +++ state/runtime/evm/bitmap.go | 1 + state/runtime/evm/bitmap_test.go | 1 + state/runtime/evm/dispatch_table.go | 1 + state/runtime/evm/dispatch_table_test.go | 1 + state/runtime/evm/evm_test.go | 3 + state/runtime/evm/instructions.go | 5 + state/runtime/evm/instructions_test.go | 5 + state/runtime/evm/state.go | 1 + state/runtime/runtime.go | 1 + state/transition_test.go | 1 + state/txn.go | 8 +- txpool/event_subscription_test.go | 5 + txpool/txpool_test.go | 45 +++++ types/rlp_marshal_storage.go | 2 + 40 files changed, 751 insertions(+), 45 deletions(-) diff --git a/e2e-polybft/e2e/bridge_test.go b/e2e-polybft/e2e/bridge_test.go index 620f389996..16964972c7 100644 --- a/e2e-polybft/e2e/bridge_test.go +++ b/e2e-polybft/e2e/bridge_test.go @@ -74,12 +74,13 @@ func TestE2E_Bridge_RootchainTokensTransfers(t *testing.T) { framework.WithBridge(), framework.WithSecretsCallback(func(addrs []types.Address, tcc *framework.TestClusterConfig) { for i := 0; i < len(addrs); i++ { - tcc.StakeAmounts = append(tcc.StakeAmounts, ethgo.Ether(10)) // premine receivers, so that they are able to do withdrawals + tcc.StakeAmounts = append(tcc.StakeAmounts, ethgo.Ether(10)) } tcc.Premine = append(tcc.Premine, receivers...) })) + defer cluster.Stop() cluster.WaitForReady(t) @@ -1345,6 +1346,7 @@ func TestE2E_Bridge_NonMintableERC20Token_WithPremine(t *testing.T) { checkBalancesFn(types.Address(rewardWalletKey.Address()), bigZero, command.DefaultPremineBalance, true) validatorsExpectedBalance := new(big.Int).Sub(command.DefaultPremineBalance, command.DefaultStake) + for _, server := range cluster.Servers { validatorAccount, err := validatorHelper.GetAccountFromDir(server.DataDir()) require.NoError(t, err) diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index e55a147e76..dfd8d50561 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -398,6 +398,7 @@ func TestE2E_Consensus_MintableERC20NativeToken(t *testing.T) { config.Premine = append(config.Premine, fmt.Sprintf("%s:%d", addr, initValidatorsBalance)) config.StakeAmounts = append(config.StakeAmounts, new(big.Int).Set(initValidatorsBalance)) validatorsAddrs[i] = addr + initialTotalSupply.Add(initialTotalSupply, initValidatorsBalance) } })) diff --git a/e2e-polybft/e2e/jsonrpc_test.go b/e2e-polybft/e2e/jsonrpc_test.go index 5e6b684c30..8741c7715d 100644 --- a/e2e-polybft/e2e/jsonrpc_test.go +++ b/e2e-polybft/e2e/jsonrpc_test.go @@ -195,6 +195,7 @@ func TestE2E_JsonRPC(t *testing.T) { Value: newBalance, }) require.NoError(t, err) + txPrice := gasPrice * estimatedGas // subtract gasPrice * estimatedGas from the balance and transfer the rest to the other account // in order to leave no funds on the account @@ -413,11 +414,44 @@ func TestE2E_JsonRPC(t *testing.T) { require.NotEqual(t, ethTxn.From, ethgo.ZeroAddress) }) + t.Run("eth_getHeaderByNumber", func(t *testing.T) { + key1, err := crypto.GenerateECDSAKey() + require.NoError(t, err) + + txn := cluster.Transfer(t, senderKey, key1.Address(), one) + require.NoError(t, txn.Wait()) + require.True(t, txn.Succeed()) + txReceipt := txn.Receipt() + + var header types.Header + err = jsonRPC.Call("eth_getHeaderByNumber", &header, ethgo.BlockNumber(txReceipt.BlockNumber)) + require.NoError(t, err) + + require.Equal(t, txReceipt.BlockNumber, header.Number) + require.Equal(t, txReceipt.BlockHash, ethgo.Hash(header.Hash)) + }) + + t.Run("eth_getHeaderByHash", func(t *testing.T) { + key1, err := crypto.GenerateECDSAKey() + require.NoError(t, err) + + txn := cluster.Transfer(t, senderKey, key1.Address(), one) + require.NoError(t, txn.Wait()) + require.True(t, txn.Succeed()) + txReceipt := txn.Receipt() + + var header types.Header + err = jsonRPC.Call("eth_getHeaderByHash", &header, txReceipt.BlockHash) + require.NoError(t, err) + + require.Equal(t, txReceipt.BlockNumber, header.Number) + require.Equal(t, txReceipt.BlockHash, ethgo.Hash(header.Hash)) + }) + t.Run("debug_traceTransaction", func(t *testing.T) { key1, err := crypto.GenerateECDSAKey() require.NoError(t, err) - // Test. We should be able to query the transaction by its hash txn := cluster.Transfer(t, senderKey, key1.Address(), one) require.NoError(t, txn.Wait()) require.True(t, txn.Succeed()) diff --git a/e2e-polybft/e2e/txpool_test.go b/e2e-polybft/e2e/txpool_test.go index a636f3b855..d76d322820 100644 --- a/e2e-polybft/e2e/txpool_test.go +++ b/e2e-polybft/e2e/txpool_test.go @@ -91,7 +91,9 @@ func TestE2E_TxPool_Transfer(t *testing.T) { if err != nil { return true } + t.Logf("Balance %s %s", receiver, balance) + if balance.Uint64() != uint64(sendAmount) { return false } @@ -295,6 +297,7 @@ func TestE2E_TxPool_BroadcastTransactions(t *testing.T) { for _, srv := range cluster.Servers { balance, err := srv.WaitForNonZeroBalance(ethgo.Address(recipient), time.Second*10) assert.NoError(t, err) + if balance != nil && balance.BitLen() > 0 { assert.Equal(t, sentAmount, balance) } else { diff --git a/e2e-polybft/property/property_test.go b/e2e-polybft/property/property_test.go index 28c518c7e1..55d7bcdc71 100644 --- a/e2e-polybft/property/property_test.go +++ b/e2e-polybft/property/property_test.go @@ -109,8 +109,11 @@ func TestProperty_DropValidators(t *testing.T) { // check that block production is stoped currentBlock, err = activeValidator.JSONRPC().Eth().BlockNumber() require.NoError(t, err) + oldBlockNumber := currentBlock + time.Sleep(2 * blockTime) + currentBlock, err = activeValidator.JSONRPC().Eth().BlockNumber() require.NoError(t, err) require.Equal(t, oldBlockNumber, currentBlock) diff --git a/jsonrpc/codec_test.go b/jsonrpc/codec_test.go index 38f41e7219..631145e237 100644 --- a/jsonrpc/codec_test.go +++ b/jsonrpc/codec_test.go @@ -105,9 +105,11 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { assert.Error(t, err) } else { assert.NoError(t, err) + if tt.expectedBnh.BlockNumber != nil { assert.Equal(t, *bnh.BlockNumber, *tt.expectedBnh.BlockNumber) } + if tt.expectedBnh.BlockHash != nil { assert.Equal(t, bnh.BlockHash.String(), tt.expectedBnh.BlockHash.String()) } diff --git a/jsonrpc/dispatcher.go b/jsonrpc/dispatcher.go index 6199deca3c..41d1cdfd9a 100644 --- a/jsonrpc/dispatcher.go +++ b/jsonrpc/dispatcher.go @@ -212,6 +212,7 @@ func (d *Dispatcher) handleSubscribe(req Request, conn wsConn) (string, Error) { if err != nil { return "", NewInternalError(err.Error()) } + filterID = d.filterManager.NewLogFilter(logQuery, conn) } else if subscribeMethod == "newPendingTransactions" { filterID = d.filterManager.NewPendingTxFilter(conn) diff --git a/jsonrpc/dispatcher_test.go b/jsonrpc/dispatcher_test.go index 63cfc7e90d..193e4a069e 100644 --- a/jsonrpc/dispatcher_test.go +++ b/jsonrpc/dispatcher_test.go @@ -467,20 +467,24 @@ func TestDispatcherBatchRequest(t *testing.T) { assert.Equal(t, c.err, resp.Error) } else { var batchResp []SuccessResponse + assert.NoError(t, expectBatchJSONResult(res, &batchResp)) if c.name == "leading-whitespace" { assert.Len(t, batchResp, 4) + for index, resp := range batchResp { assert.Equal(t, c.batchResponse[index].Error, resp.Error) } } else if c.name == "valid-batch-req" { assert.Len(t, batchResp, 6) + for index, resp := range batchResp { assert.Equal(t, c.batchResponse[index].Error, resp.Error) } } else if c.name == "no-limits" { assert.Len(t, batchResp, 12) + for index, resp := range batchResp { assert.Equal(t, c.batchResponse[index].Error, resp.Error) } diff --git a/jsonrpc/eth_blockchain_test.go b/jsonrpc/eth_blockchain_test.go index 61c62c7b83..fd4141b13d 100644 --- a/jsonrpc/eth_blockchain_test.go +++ b/jsonrpc/eth_blockchain_test.go @@ -3,6 +3,7 @@ package jsonrpc import ( "errors" "math/big" + "strconv" "testing" "github.com/0xPolygon/polygon-edge/blockchain" @@ -68,6 +69,59 @@ func TestEth_Block_GetBlockByHash(t *testing.T) { assert.Nil(t, res) } +func TestEth_Block_GetHeaderByNumber(t *testing.T) { + store := &mockBlockStore{} + for i := 0; i < 10; i++ { + store.add(newTestBlock(uint64(i), hash1)) + } + + eth := newTestEthEndpoint(store) + + cases := []struct { + description string + blockNum BlockNumber + isNotNil bool + err bool + }{ + {"should be able to get the latest block number", LatestBlockNumber, true, false}, + {"should be able to get the earliest block number", EarliestBlockNumber, true, false}, + {"should not be able to get block with negative number", BlockNumber(-50), false, true}, + {"should be able to get block with number 0", BlockNumber(0), true, false}, + {"should be able to get block with number 2", BlockNumber(2), true, false}, + {"should be able to get block with number greater than latest block", BlockNumber(50), false, false}, + } + for _, c := range cases { + res, err := eth.GetHeaderByNumber(c.blockNum) + + if c.isNotNil { + assert.NotNil(t, res, "expected to return block, but got nil") + } else { + assert.Nil(t, res, "expected to return nil, but got data") + } + + if c.err { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } +} + +func TestEth_Block_GetHeaderByHash(t *testing.T) { + store := &mockBlockStore{} + store.add(newTestBlock(1, hash1)) + + eth := newTestEthEndpoint(store) + + res, err := eth.GetHeaderByHash(hash1) + assert.NoError(t, err) + assert.NotNil(t, res) + + res, err = eth.GetHeaderByHash(hash2) + assert.NoError(t, err) + assert.Nil(t, res) +} + func TestEth_Block_BlockNumber(t *testing.T) { store := &mockBlockStore{} store.add(&types.Block{ @@ -261,8 +315,10 @@ func TestEth_GetTransactionReceipt(t *testing.T) { eth := newTestEthEndpoint(store) block := newTestBlock(1, hash4) store.add(block) + txn0 := newTestTransaction(uint64(0), addr0) txn1 := newTestTransaction(uint64(1), addr1) + block.Transactions = []*types.Transaction{txn0, txn1} receipt1 := &types.Receipt{ Logs: []*types.Log{ @@ -287,6 +343,7 @@ func TestEth_GetTransactionReceipt(t *testing.T) { }, } receipt1.SetStatus(types.ReceiptSuccess) + receipt2 := &types.Receipt{ Logs: []*types.Log{ { @@ -315,6 +372,68 @@ func TestEth_GetTransactionReceipt(t *testing.T) { }) } +func TestEth_GetBlockReceipts(t *testing.T) { + store := newMockBlockStore() + eth := newTestEthEndpoint(store) + block := newTestBlock(1, hash4) + store.add(block) + + txn0 := newTestTransaction(uint64(0), addr0) + txn1 := newTestTransaction(uint64(1), addr1) + + block.Transactions = []*types.Transaction{txn0, txn1} + receipt1 := &types.Receipt{ + Logs: []*types.Log{ + { + // log 0 + Topics: []types.Hash{ + hash1, + }, + }, + { + // log 1 + Topics: []types.Hash{ + hash2, + }, + }, + { + // log 2 + Topics: []types.Hash{ + hash3, + }, + }, + }, + } + receipt1.SetStatus(types.ReceiptSuccess) + + receipt2 := &types.Receipt{ + Logs: []*types.Log{ + { + // log 3 + Topics: []types.Hash{ + hash4, + }, + }, + }, + } + receipt2.SetStatus(types.ReceiptSuccess) + store.receipts[hash4] = []*types.Receipt{receipt1, receipt2} + + res, err := eth.GetBlockReceipts(1) + + assert.NoError(t, err) + assert.NotNil(t, res) + + response := res.([]*receipt) + assert.Equal(t, txn1.Hash(), response[1].TxHash) + assert.Equal(t, 2, len(response)) + assert.Equal(t, block.Hash(), response[1].BlockHash) + assert.NotNil(t, response[1].Logs) + assert.Len(t, response[1].Logs, 1) + assert.Equal(t, uint64(3), uint64(response[1].Logs[0].LogIndex)) + assert.Equal(t, uint64(1), uint64(response[1].Logs[0].TxIndex)) +} + func TestEth_Syncing(t *testing.T) { store := newMockBlockStore() eth := newTestEthEndpoint(store) @@ -508,6 +627,79 @@ func TestEth_Call(t *testing.T) { }) } +func TestEth_CreateAccessList(t *testing.T) { + store := newMockBlockStore() + hashs := make([]types.Hash, 10) + latest := LatestBlockNumber + blockNum1 := BlockNumber(1) + blockNum2 := BlockNumber(2) + + for i := 0; i < 10; i++ { + hashs[i] = types.StringToHash(strconv.Itoa(i)) + block := newTestBlock(uint64(i), hashs[i]) + block.Header.GasUsed = uint64(i * 10) + store.add(block) + } + + eth := newTestEthEndpoint(store) + + txn := &txnArgs{ + From: &addr0, + To: &addr1, + Gas: argUintPtr(100000), + GasPrice: argBytesPtr([]byte{0x64}), + Value: argBytesPtr([]byte{0x64}), + Data: nil, + Nonce: argUintPtr(0), + } + + cases := []struct { + filter BlockNumberOrHash + err bool + }{ + { + // both fields are empty + BlockNumberOrHash{}, + true, + }, + { + // return the latest block number + BlockNumberOrHash{BlockNumber: &latest}, + true, + }, + { + // specific real block number + BlockNumberOrHash{BlockNumber: &blockNum1}, + true, + }, + { + // specific block number (not found) + BlockNumberOrHash{BlockNumber: &blockNum2}, + true, + }, + { + // specific block by hash (found). By default all blocks in the mock have hash zero + BlockNumberOrHash{BlockHash: &types.ZeroHash}, + false, + }, + { + // specific block by hash (not found) + BlockNumberOrHash{BlockHash: &hashs[8]}, + true, + }, + } + + for _, c := range cases { + res, err := eth.CreateAccessList(txn, c.filter) + if c.err { + assert.NoError(t, err) + assert.NotNil(t, res) + } else { + assert.NoError(t, err) + } + } +} + type testStore interface { ethStore } @@ -558,6 +750,24 @@ func (m *mockBlockStore) appendBlocksToStore(blocks []*types.Block) { } } +func (m *mockBlockStore) GetHeaderByNumber(num uint64) (*types.Header, bool) { + block, ok := m.GetBlockByNumber(num, true) + if !ok { + return nil, false + } + + return block.Header, block.Header != nil +} + +func (m *mockBlockStore) GetHeaderByHash(hash types.Hash) (*types.Header, bool) { + block, ok := m.GetBlockByHash(hash, true) + if !ok { + return nil, false + } + + return block.Header, block.Header != nil +} + func (m *mockBlockStore) setupLogs() { m.receipts = make(map[types.Hash][]*types.Receipt) @@ -700,6 +910,7 @@ func (m *mockBlockStore) ApplyTxn(_ *types.Header, _ *types.Transaction, _ types return &runtime.ExecutionResult{ Err: m.ethCallError, ReturnValue: m.returnValue, + AccessList: runtime.NewAccessList(), }, nil } diff --git a/jsonrpc/eth_endpoint.go b/jsonrpc/eth_endpoint.go index 8b2978aea9..c2b7e66f09 100644 --- a/jsonrpc/eth_endpoint.go +++ b/jsonrpc/eth_endpoint.go @@ -53,6 +53,9 @@ type ethBlockchainStore interface { // GetBlockByHash gets a block using the provided hash GetBlockByHash(hash types.Hash, full bool) (*types.Block, bool) + // GetHeaderByHash returns the header by his hash + GetHeaderByHash(hash types.Hash) (*types.Header, bool) + // GetBlockByNumber returns a block using the provided number GetBlockByNumber(num uint64, full bool) (*types.Block, bool) @@ -155,23 +158,106 @@ func (e *Eth) GetBlockByHash(hash types.Hash, fullTx bool) (interface{}, error) return toBlock(block, fullTx), nil } -func (e *Eth) filterExtra(block *types.Block) error { +// GetHeaderByNumber returns the requested canonical block header. +func (e *Eth) GetHeaderByNumber(number BlockNumber) (interface{}, error) { + num, err := GetNumericBlockNumber(number, e.store) + if err != nil { + return nil, err + } + + header, ok := e.store.GetHeaderByNumber(num) + if !ok { + return nil, nil + } + + headerCopy, err := e.headerFilterExtra(header) + if err != nil { + return nil, err + } + + return toHeader(headerCopy), nil +} + +// GetHeaderByHash returns the requested header by hash. +func (e *Eth) GetHeaderByHash(hash types.Hash) (interface{}, error) { + header, ok := e.store.GetHeaderByHash(hash) + if !ok { + return nil, nil + } + + headerCopy, err := e.headerFilterExtra(header) + if err != nil { + return nil, err + } + + return toHeader(headerCopy), nil +} + +func (e *Eth) headerFilterExtra(header *types.Header) (*types.Header, error) { // we need to copy it because the store returns header from storage directly // and not a copy, so changing it, actually changes it in storage as well - headerCopy := block.Header.Copy() + headerCopy := header.Copy() filteredExtra, err := e.store.FilterExtra(headerCopy.ExtraData) if err != nil { - return err + return nil, err } headerCopy.ExtraData = filteredExtra + + return headerCopy, err +} + +func (e *Eth) filterExtra(block *types.Block) error { + headerCopy, err := e.headerFilterExtra(block.Header) + if err != nil { + return err + } + // no need to recompute hash (filtered out data is not in the hash in the first place) block.Header = headerCopy return nil } +// CreateAccessList creates a EIP-2930 type AccessList for the given transaction. +// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state. +func (e *Eth) CreateAccessList(arg *txnArgs, filter BlockNumberOrHash) (interface{}, error) { + header, err := GetHeaderFromBlockNumberOrHash(filter, e.store) + if err != nil { + return nil, err + } + + transaction, err := DecodeTxn(arg, header.Number, e.store, true) + if err != nil { + return nil, err + } + + // If the caller didn't supply the gas limit in the message, then we set it to maximum possible => block gas limit + if transaction.Gas() == 0 { + transaction.SetGas(header.GasLimit) + } + + // Force transaction gas price if empty + if err = e.fillTransactionGasPrice(transaction); err != nil { + return nil, err + } + + // The return value of the execution is saved in the transition (returnValue field) + result, err := e.store.ApplyTxn(header, transaction, nil, true) + if err != nil { + return nil, err + } + + res := &accessListResult{ + Accesslist: result.AccessList.ToTxAccessList(), + Error: result.Err, + GasUsed: argUint64(result.GasUsed), + } + + return res, nil +} + // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. func (e *Eth) GetBlockTransactionCountByHash(blockHash types.Hash) (interface{}, error) { block, ok := e.store.GetBlockByHash(blockHash, true) @@ -226,7 +312,7 @@ func (e *Eth) GetTransactionByBlockHashAndIndex(blockHash types.Hash, index argU func (e *Eth) BlockNumber() (interface{}, error) { h := e.store.Header() if h == nil { - return nil, fmt.Errorf("header has a nil value") + return nil, ErrHeaderNotFound } return argUintPtr(h.Number), nil @@ -375,6 +461,47 @@ func (e *Eth) GetTransactionReceipt(hash types.Hash) (interface{}, error) { return toReceipt(raw, txn, uint64(txIndex), block.Header, logs), nil } +// GetBlockReceipts returns all transaction receipts for a given block. +func (e *Eth) GetBlockReceipts(number BlockNumber) (interface{}, error) { + num, err := GetNumericBlockNumber(number, e.store) + if err != nil { + return nil, err + } + + block, ok := e.store.GetBlockByNumber(num, true) + if !ok { + return nil, ErrBlockNotFound + } + + if len(block.Transactions) == 0 { + return nil, nil + } + + receipts, err := e.store.GetReceiptsByHash(block.Hash()) + if err != nil { + return nil, err + } + + receiptsNum := len(receipts) + if receiptsNum == 0 { + return nil, nil + } + + resReceipts := make([]*receipt, receiptsNum) + logIndex := 0 + + for i, transaction := range block.Transactions { + raw := receipts[i] + // accumulate receipt logs indices from block transactions + // that are before the desired transaction + logs := toLogs(raw.Logs, uint64(logIndex), uint64(i), block.Header, raw.TxHash) + resReceipts[i] = toReceipt(raw, transaction, uint64(i), block.Header, logs) + logIndex += len(raw.Logs) + } + + return resReceipts, nil +} + // GetStorageAt returns the contract storage at the index position func (e *Eth) GetStorageAt( address types.Address, diff --git a/jsonrpc/eth_state_test.go b/jsonrpc/eth_state_test.go index f931294145..858681a562 100644 --- a/jsonrpc/eth_state_test.go +++ b/jsonrpc/eth_state_test.go @@ -131,6 +131,7 @@ func TestEth_State_GetBalance(t *testing.T) { assert.Equal(t, nil, balance) } else { assert.NoError(t, err) + if tt.expectedBalance == 0 { uintBalance, ok := balance.(*argUint64) if !ok { @@ -383,6 +384,7 @@ func TestEth_State_GetCode(t *testing.T) { assert.Error(t, err) } else { assert.NoError(t, err) + if tt.target.String() == uninitializedAddress.String() { assert.Equal(t, "0x", code) } else { @@ -553,6 +555,7 @@ func TestEth_State_GetStorageAt(t *testing.T) { storage: make(map[types.Hash][]byte), } account := store.account + for index, data := range storage { account.Storage(index, data.Bytes()) } diff --git a/jsonrpc/filter_manager.go b/jsonrpc/filter_manager.go index 03bacde8ff..b33c293177 100644 --- a/jsonrpc/filter_manager.go +++ b/jsonrpc/filter_manager.go @@ -973,6 +973,7 @@ func (h *headElem) getUpdates() ([]*block, *headElem) { if nextElem.header != nil { res = append(res, nextElem.header) } + cur = nextElem } } diff --git a/jsonrpc/filter_manager_fuzz_test.go b/jsonrpc/filter_manager_fuzz_test.go index d9e7d68da9..45b44d32e3 100644 --- a/jsonrpc/filter_manager_fuzz_test.go +++ b/jsonrpc/filter_manager_fuzz_test.go @@ -90,6 +90,7 @@ func FuzzGetLogsForQuery(f *testing.F) { if len(hash) != types.HashLength { t.Skip() } + blockHash := types.BytesToHash(hash) logQuery := LogQuery{ @@ -145,6 +146,7 @@ func FuzzGetLogFilterFromID(f *testing.F) { if len(address) != types.AddressLength { t.Skip() } + logFilter := &LogQuery{ Addresses: []types.Address{types.BytesToAddress(address)}, toBlock: BlockNumber(toBlock), @@ -153,6 +155,7 @@ func FuzzGetLogFilterFromID(f *testing.F) { retrivedLogFilter, err := m.GetLogFilterFromID( m.NewLogFilter(logFilter, &MockClosedWSConnection{}), ) + if err != nil { assert.Equal(t, logFilter, retrivedLogFilter.query) } diff --git a/jsonrpc/filter_manager_test.go b/jsonrpc/filter_manager_test.go index 48e43aca9c..e22dd9cfc8 100644 --- a/jsonrpc/filter_manager_test.go +++ b/jsonrpc/filter_manager_test.go @@ -661,13 +661,13 @@ func newMockWsConnWithMsgCh() (*mockWsConn, <-chan []byte) { func TestHeadStream_Basic(t *testing.T) { t.Parallel() - b := newBlockStream(&block{Hash: types.StringToHash("1")}) - b.push(&block{Hash: types.StringToHash("2")}) + b := newBlockStream(&block{header: header{Hash: types.StringToHash("1")}}) + b.push(&block{header: header{Hash: types.StringToHash("2")}}) cur := b.getHead() - b.push(&block{Hash: types.StringToHash("3")}) - b.push(&block{Hash: types.StringToHash("4")}) + b.push(&block{header: header{Hash: types.StringToHash("3")}}) + b.push(&block{header: header{Hash: types.StringToHash("4")}}) // get the updates, there are two new entries updates, next := cur.getUpdates() @@ -686,7 +686,7 @@ func TestHeadStream_Concurrent(t *testing.T) { nReaders := 20 nMessages := 10 - b := newBlockStream(&block{Number: 0}) + b := newBlockStream(&block{header: header{Number: 0}}) // Write co-routine with jitter go func() { @@ -696,7 +696,7 @@ func TestHeadStream_Concurrent(t *testing.T) { z := rand.NewZipf(rand.New(rand.NewSource(seed)), 1.5, 1.5, 50) for i := 0; i < nMessages; i++ { - b.push(&block{Number: argUint64(i)}) + b.push(&block{header: header{Number: argUint64(i)}}) wait := time.Duration(z.Uint64()) * time.Millisecond time.Sleep(wait) @@ -723,6 +723,7 @@ func TestHeadStream_Concurrent(t *testing.T) { return } + expect++ if expect == uint64(nMessages) { diff --git a/jsonrpc/helper.go b/jsonrpc/helper.go index 2b67fb43a3..5db6f5cdbf 100644 --- a/jsonrpc/helper.go +++ b/jsonrpc/helper.go @@ -195,6 +195,7 @@ func DecodeTxn(arg *txnArgs, blockNumber uint64, store nonceGetter, forceSetNonc if err != nil { return nil, err } + arg.Nonce = argUintPtr(nonce) } diff --git a/jsonrpc/jsonrpc.go b/jsonrpc/jsonrpc.go index c478eab397..aa238e0696 100644 --- a/jsonrpc/jsonrpc.go +++ b/jsonrpc/jsonrpc.go @@ -129,6 +129,7 @@ func (j *JSONRPC) setupHTTP() error { } j.logger.Info("http server started", "addr", j.config.Addr.String()) + return nil } @@ -151,6 +152,7 @@ func middlewareFactory(config *Config) func(http.Handler) http.Handler { break } } + next.ServeHTTP(w, r) }) } diff --git a/jsonrpc/txpool_endpoint.go b/jsonrpc/txpool_endpoint.go index 5c2bb5e69f..c2ecc6aac1 100644 --- a/jsonrpc/txpool_endpoint.go +++ b/jsonrpc/txpool_endpoint.go @@ -29,6 +29,11 @@ type ContentResponse struct { Queued map[types.Address]map[uint64]*transaction `json:"queued"` } +type ContentAddressResponse struct { + Pending map[uint64]*transaction `json:"pending"` + Queued map[uint64]*transaction `json:"queued"` +} + type InspectResponse struct { Pending map[string]map[string]string `json:"pending"` Queued map[string]map[string]string `json:"queued"` @@ -41,6 +46,26 @@ type StatusResponse struct { Queued uint64 `json:"queued"` } +// ContentFrom returns the transactions contained within the transaction pool. +func (t *TxPool) ContentFrom(addr types.Address) (interface{}, error) { + convertTxMap := func(txs []*types.Transaction) map[uint64]*transaction { + result := make(map[uint64]*transaction, len(txs)) + for _, tx := range txs { + result[tx.Nonce()] = toTransaction(tx, nil, &types.ZeroHash, nil) + } + + return result + } + + pendingTxs, queuedTxs := t.store.GetTxs(true) + resp := ContentAddressResponse{ + Pending: convertTxMap(pendingTxs[addr]), + Queued: convertTxMap(queuedTxs[addr]), + } + + return resp, nil +} + // Create response for txpool_content request. // See https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content. func (t *TxPool) Content() (interface{}, error) { diff --git a/jsonrpc/txpool_endpoint_test.go b/jsonrpc/txpool_endpoint_test.go index a46dae8e17..430f38f93e 100644 --- a/jsonrpc/txpool_endpoint_test.go +++ b/jsonrpc/txpool_endpoint_test.go @@ -152,6 +152,132 @@ func TestContentEndpoint(t *testing.T) { }) } +func TestContentFrom(t *testing.T) { + t.Parallel() + + t.Run("returns empty ContentAddressResponse if tx pool has no transactions", func(t *testing.T) { + t.Parallel() + + mockStore := newMockTxPoolStore() + txPoolEndpoint := &TxPool{mockStore} + address := types.Address{0x0} + result, _ := txPoolEndpoint.ContentFrom(address) + + response := result.(ContentAddressResponse) + + assert.True(t, mockStore.includeQueued) + assert.Equal(t, 0, len(response.Pending)) + assert.Equal(t, 0, len(response.Queued)) + }) + + t.Run("returns the correct data for the correct address", func(t *testing.T) { + t.Parallel() + + mockStore := newMockTxPoolStore() + address1 := types.Address{0x1} + testTx1 := newTestTransaction(2, address1) + testTx2 := newTestDynamicFeeTransaction(3, address1) + mockStore.pending[address1] = []*types.Transaction{testTx1, testTx2} + txPoolEndpoint := &TxPool{mockStore} + + result, _ := txPoolEndpoint.ContentFrom(address1) + response := result.(ContentAddressResponse) + + assert.Equal(t, 0, len(response.Queued)) + assert.Equal(t, 2, len(response.Pending)) + + txData := response.Pending[testTx1.Nonce()] + assert.NotNil(t, txData) + assert.Equal(t, testTx1.Gas(), uint64(txData.Gas)) + assert.Equal(t, *(testTx1.GasPrice()), big.Int(*txData.GasPrice)) + assert.Equal(t, *(testTx1.GasFeeCap()), big.Int(*txData.GasPrice)) + assert.Equal(t, *(testTx1.GasTipCap()), big.Int(*txData.GasPrice)) + assert.Equal(t, testTx1.To(), txData.To) + assert.Equal(t, testTx1.From(), txData.From) + assert.Equal(t, *(testTx1.Value()), big.Int(txData.Value)) + assert.Equal(t, testTx1.Input(), []byte(txData.Input)) + assert.Equal(t, (*argUint64)(nil), txData.BlockNumber) + assert.Equal(t, (*argUint64)(nil), txData.TxIndex) + + txData = response.Pending[testTx2.Nonce()] + assert.NotNil(t, txData) + assert.Equal(t, (argUint64)(types.DynamicFeeTxType), txData.Type) + assert.Equal(t, testTx2.Gas(), uint64(txData.Gas)) + assert.Nil(t, testTx2.GasPrice()) + assert.Equal(t, *(testTx2.GasFeeCap()), big.Int(*txData.GasFeeCap)) + assert.Equal(t, *(testTx2.GasTipCap()), big.Int(*txData.GasTipCap)) + assert.Equal(t, testTx2.To(), txData.To) + assert.Equal(t, testTx2.From(), txData.From) + assert.Equal(t, *(testTx2.ChainID()), big.Int(*txData.ChainID)) + assert.Equal(t, *(testTx2.Value()), big.Int(txData.Value)) + assert.Equal(t, testTx2.Input(), []byte(txData.Input)) + assert.Equal(t, (*argUint64)(nil), txData.BlockNumber) + assert.Equal(t, (*argUint64)(nil), txData.TxIndex) + }) + + t.Run("returns correct data for queued transaction", func(t *testing.T) { + t.Parallel() + + mockStore := newMockTxPoolStore() + address1, address2 := types.Address{0x1}, types.Address{0x2} + testTx1 := newTestTransaction(2, address1) + testTx2 := newTestDynamicFeeTransaction(1, address2) + mockStore.queued[address1] = []*types.Transaction{testTx1} + mockStore.queued[address2] = []*types.Transaction{testTx2} + txPoolEndpoint := &TxPool{mockStore} + + result, err := txPoolEndpoint.ContentFrom(address2) + assert.NoError(t, err) + + response := result.(ContentAddressResponse) + assert.Equal(t, 1, len(response.Queued)) + + txData := response.Queued[testTx2.Nonce()] + assert.NotNil(t, txData) + assert.Equal(t, (argUint64)(types.DynamicFeeTxType), txData.Type) + assert.Equal(t, testTx2.Gas(), uint64(txData.Gas)) + assert.Nil(t, testTx2.GasPrice()) + assert.Equal(t, *(testTx2.GasFeeCap()), big.Int(*txData.GasFeeCap)) + assert.Equal(t, *(testTx2.GasTipCap()), big.Int(*txData.GasTipCap)) + assert.Equal(t, testTx2.To(), txData.To) + assert.Equal(t, testTx2.From(), txData.From) + assert.Equal(t, *(testTx2.ChainID()), big.Int(*txData.ChainID)) + assert.Equal(t, *(testTx2.Value()), big.Int(txData.Value)) + assert.Equal(t, testTx2.Input(), []byte(txData.Input)) + assert.Equal(t, (*argUint64)(nil), txData.BlockNumber) + assert.Equal(t, (*argUint64)(nil), txData.TxIndex) + }) + + t.Run("returns correct ContentAddressResponse data for multiple transactions", func(t *testing.T) { + t.Parallel() + + mockStore := newMockTxPoolStore() + address1 := types.Address{0x1} + testTx1 := newTestTransaction(2, address1) + testTx2 := newTestTransaction(4, address1) + testTx3 := newTestTransaction(11, address1) + address2 := types.Address{0x2} + testTx4 := newTestTransaction(7, address2) + testTx5 := newTestTransaction(8, address2) + mockStore.pending[address1] = []*types.Transaction{testTx1, testTx2} + mockStore.pending[address2] = []*types.Transaction{testTx4} + mockStore.queued[address1] = []*types.Transaction{testTx3} + mockStore.queued[address2] = []*types.Transaction{testTx5} + txPoolEndpoint := &TxPool{mockStore} + + result, err := txPoolEndpoint.ContentFrom(address2) + assert.NoError(t, err) + + response := result.(ContentAddressResponse) + + assert.True(t, mockStore.includeQueued) + assert.Equal(t, 1, len(response.Pending)) + assert.Equal(t, testTx4.Hash(), response.Pending[testTx4.Nonce()].Hash) + assert.Equal(t, 1, len(response.Queued)) + assert.Equal(t, testTx5.Hash(), response.Queued[testTx5.Nonce()].Hash) + }) +} + func TestInspectEndpoint(t *testing.T) { t.Parallel() diff --git a/jsonrpc/types.go b/jsonrpc/types.go index 46b98b7cfd..a8055ae791 100644 --- a/jsonrpc/types.go +++ b/jsonrpc/types.go @@ -112,28 +112,38 @@ func toTransaction( return res } +type header struct { + ParentHash types.Hash `json:"parentHash"` + Sha3Uncles types.Hash `json:"sha3Uncles"` + Miner argBytes `json:"miner"` + StateRoot types.Hash `json:"stateRoot"` + TxRoot types.Hash `json:"transactionsRoot"` + ReceiptsRoot types.Hash `json:"receiptsRoot"` + LogsBloom types.Bloom `json:"logsBloom"` + Difficulty argUint64 `json:"difficulty"` + TotalDifficulty argUint64 `json:"totalDifficulty"` + Number argUint64 `json:"number"` + GasLimit argUint64 `json:"gasLimit"` + GasUsed argUint64 `json:"gasUsed"` + Timestamp argUint64 `json:"timestamp"` + ExtraData argBytes `json:"extraData"` + MixHash types.Hash `json:"mixHash"` + Nonce types.Nonce `json:"nonce"` + Hash types.Hash `json:"hash"` + BaseFee argUint64 `json:"baseFeePerGas,omitempty"` +} + +type accessListResult struct { + Accesslist types.TxAccessList `json:"accessList"` + Error error `json:"error,omitempty"` + GasUsed argUint64 `json:"gasUsed"` +} + type block struct { - ParentHash types.Hash `json:"parentHash"` - Sha3Uncles types.Hash `json:"sha3Uncles"` - Miner argBytes `json:"miner"` - StateRoot types.Hash `json:"stateRoot"` - TxRoot types.Hash `json:"transactionsRoot"` - ReceiptsRoot types.Hash `json:"receiptsRoot"` - LogsBloom types.Bloom `json:"logsBloom"` - Difficulty argUint64 `json:"difficulty"` - TotalDifficulty argUint64 `json:"totalDifficulty"` - Size argUint64 `json:"size"` - Number argUint64 `json:"number"` - GasLimit argUint64 `json:"gasLimit"` - GasUsed argUint64 `json:"gasUsed"` - Timestamp argUint64 `json:"timestamp"` - ExtraData argBytes `json:"extraData"` - MixHash types.Hash `json:"mixHash"` - Nonce types.Nonce `json:"nonce"` - Hash types.Hash `json:"hash"` - Transactions []transactionOrHash `json:"transactions"` - Uncles []types.Hash `json:"uncles"` - BaseFee argUint64 `json:"baseFeePerGas,omitempty"` + header + Size argUint64 `json:"size"` + Transactions []transactionOrHash `json:"transactions"` + Uncles []types.Hash `json:"uncles"` } func (b *block) Copy() *block { @@ -151,7 +161,7 @@ func (b *block) Copy() *block { func toBlock(b *types.Block, fullTx bool) *block { h := b.Header - res := &block{ + resHeader := header{ ParentHash: h.ParentHash, Sha3Uncles: h.Sha3Uncles, Miner: argBytes(h.Miner), @@ -161,7 +171,6 @@ func toBlock(b *types.Block, fullTx bool) *block { LogsBloom: h.LogsBloom, Difficulty: argUint64(h.Difficulty), TotalDifficulty: argUint64(h.Difficulty), // not needed for POS - Size: argUint64(b.Size()), Number: argUint64(h.Number), GasLimit: argUint64(h.GasLimit), GasUsed: argUint64(h.GasUsed), @@ -170,11 +179,16 @@ func toBlock(b *types.Block, fullTx bool) *block { MixHash: h.MixHash, Nonce: h.Nonce, Hash: h.Hash, - Transactions: []transactionOrHash{}, - Uncles: []types.Hash{}, BaseFee: argUint64(h.BaseFee), } + res := &block{ + header: resHeader, + Size: argUint64(b.Size()), + Transactions: []transactionOrHash{}, + Uncles: []types.Hash{}, + } + for idx, txn := range b.Transactions { if fullTx { txn.SetGasPrice(txn.GetGasPrice(b.Header.BaseFee)) @@ -202,6 +216,31 @@ func toBlock(b *types.Block, fullTx bool) *block { return res } +func toHeader(h *types.Header) *header { + res := &header{ + ParentHash: h.ParentHash, + Sha3Uncles: h.Sha3Uncles, + Miner: argBytes(h.Miner), + StateRoot: h.StateRoot, + TxRoot: h.TxRoot, + ReceiptsRoot: h.ReceiptsRoot, + LogsBloom: h.LogsBloom, + Difficulty: argUint64(h.Difficulty), + TotalDifficulty: argUint64(h.Difficulty), // not needed for POS + Number: argUint64(h.Number), + GasLimit: argUint64(h.GasLimit), + GasUsed: argUint64(h.GasUsed), + Timestamp: argUint64(h.Timestamp), + ExtraData: argBytes(h.ExtraData), + MixHash: h.MixHash, + Nonce: h.Nonce, + Hash: h.Hash, + BaseFee: argUint64(h.BaseFee), + } + + return res +} + type receipt struct { Root types.Hash `json:"root"` CumulativeGasUsed argUint64 `json:"cumulativeGasUsed"` diff --git a/jsonrpc/types_test.go b/jsonrpc/types_test.go index 3b6da3a0ba..286b4cbf3c 100644 --- a/jsonrpc/types_test.go +++ b/jsonrpc/types_test.go @@ -156,9 +156,12 @@ func TestToTransaction_EIP1559(t *testing.T) { func TestBlock_Copy(t *testing.T) { b := &block{ - ExtraData: []byte{0x1}, - Miner: []byte{0x2}, - Uncles: []types.Hash{{0x0, 0x1}}, + header: header{ + ExtraData: []byte{0x1}, + Miner: []byte{0x2}, + }, + + Uncles: []types.Hash{{0x0, 0x1}}, } bb := b.Copy() @@ -170,7 +173,7 @@ var testsuite embed.FS func TestBlock_Encoding(t *testing.T) { getBlock := func() block { - return block{ + return block{header: header{ ParentHash: types.Hash{0x1}, Sha3Uncles: types.Hash{0x2}, Miner: types.Address{0x1}.Bytes(), @@ -188,7 +191,7 @@ func TestBlock_Encoding(t *testing.T) { Nonce: types.Nonce{10}, Hash: types.Hash{0x8}, BaseFee: 15, - } + }} } testBlock := func(name string, b block) { diff --git a/network/identity_e2e_test.go b/network/identity_e2e_test.go index 6d6f7c3ed2..7d493b050a 100644 --- a/network/identity_e2e_test.go +++ b/network/identity_e2e_test.go @@ -45,6 +45,7 @@ func TestIdentityHandshake(t *testing.T) { }, } servers, createErr := createServers(2, params) + if createErr != nil { t.Fatalf("Unable to create servers, %v", createErr) } diff --git a/network/server_test.go b/network/server_test.go index 43f06582a6..c458b5f3b4 100644 --- a/network/server_test.go +++ b/network/server_test.go @@ -654,6 +654,7 @@ func TestRunDial(t *testing.T) { t.Fatalf("Unable to join peer, %v", joinErr) } } + closeServers(servers...) }) @@ -674,6 +675,7 @@ func TestRunDial(t *testing.T) { assert.Error(t, joinErr) } } + closeServers(servers...) }) @@ -766,6 +768,7 @@ func TestSubscribe(t *testing.T) { server := setupServer(t, true) ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(func() { cancel() }) @@ -801,6 +804,7 @@ func TestSubscribe(t *testing.T) { server := setupServer(t, false) ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(func() { cancel() }) diff --git a/state/executor.go b/state/executor.go index 4f094c0c86..5274549368 100644 --- a/state/executor.go +++ b/state/executor.go @@ -667,6 +667,8 @@ func (t *Transition) apply(msg *types.Transaction) (*runtime.ExecutionResult, er result = t.Call2(msg.From(), *(msg.To()), msg.Input(), value, gasLeft) } + result.AccessList = t.accessList + refundQuotient := LegacyRefundQuotient if t.config.London { refundQuotient = LondonRefundQuotient @@ -882,7 +884,6 @@ func (t *Transition) applyCreate(c *runtime.Contract, host runtime.Host) *runtim } } - //Berlin: check // we add this to the access-list before taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back according to EIP2929 specs if t.config.Berlin { diff --git a/state/immutable-trie/copy_trie_test.go b/state/immutable-trie/copy_trie_test.go index 4d678f5eed..7faa764d8f 100644 --- a/state/immutable-trie/copy_trie_test.go +++ b/state/immutable-trie/copy_trie_test.go @@ -14,9 +14,11 @@ func TestCompareModelOfTrieCopy(t *testing.T) { ldbStorageOld := ldbstorage.NewMemStorage() ldbStorageNew := ldbstorage.NewMemStorage() ldb, err := leveldb.Open(ldbStorageOld, nil) + if err != nil { t.Fatal(err) } + defer ldb.Close() ldbNew, err := leveldb.Open(ldbStorageNew, nil) @@ -39,11 +41,14 @@ func TestCompareModelOfTrieCopy(t *testing.T) { } tx.Commit() + stateRoot := trie.Hash() result, err := HashChecker(stateRoot.Bytes(), kv) + if err != nil { t.Fatal(err) } + if stateRoot != result { t.Fatal("Hashes are not equal", stateRoot, result) } @@ -57,6 +62,7 @@ func TestCompareModelOfTrieCopy(t *testing.T) { if err != nil { t.Error(err) } + if stateRoot != result { t.Error("Hashes are not equal", stateRoot, result) } diff --git a/state/immutable-trie/hasher.go b/state/immutable-trie/hasher.go index 38ea53350c..aa5b80a8e3 100644 --- a/state/immutable-trie/hasher.go +++ b/state/immutable-trie/hasher.go @@ -117,6 +117,7 @@ func (t *Txn) Hash() ([]byte, error) { } } else { tmp := val.MarshalTo(nil) + h.hash.Reset() h.hash.Write(tmp) diff --git a/state/runtime/access_list.go b/state/runtime/access_list.go index 8b806931a5..7471829932 100644 --- a/state/runtime/access_list.go +++ b/state/runtime/access_list.go @@ -113,3 +113,28 @@ func (al *AccessList) DeleteSlot(address types.Address, slot types.Hash) { delete(slotMap, slot) } } + +// ToTxAccessList converts access list from internal representation to the types.TxAccessList +func (al *AccessList) ToTxAccessList() types.TxAccessList { + convertToKeysSlice := func(m map[types.Hash]struct{}) []types.Hash { + storageKeys := make([]types.Hash, 0, len(m)) + for key := range m { + storageKeys = append(storageKeys, key) + } + + return storageKeys + } + + result := make(types.TxAccessList, 0, len(*al)) + for address, storageKeys := range *al { + result = append( + result, + types.AccessTuple{ + Address: address, + StorageKeys: convertToKeysSlice(storageKeys), + }, + ) + } + + return result +} diff --git a/state/runtime/evm/bitmap.go b/state/runtime/evm/bitmap.go index ae6929ea57..7a830bc806 100644 --- a/state/runtime/evm/bitmap.go +++ b/state/runtime/evm/bitmap.go @@ -39,6 +39,7 @@ func (b *bitmap) setCode(code []byte) { // jumpdest b.set(uint64(i)) } + i++ } } diff --git a/state/runtime/evm/bitmap_test.go b/state/runtime/evm/bitmap_test.go index 932fc69a39..b4f6809fb6 100644 --- a/state/runtime/evm/bitmap_test.go +++ b/state/runtime/evm/bitmap_test.go @@ -13,6 +13,7 @@ func TestIsPush(t *testing.T) { if !strings.HasPrefix(OpCode(i).String(), "PUSH") { t.Fatal("err") } + num++ } } diff --git a/state/runtime/evm/dispatch_table.go b/state/runtime/evm/dispatch_table.go index ec7b44b4c5..e7b45264b1 100644 --- a/state/runtime/evm/dispatch_table.go +++ b/state/runtime/evm/dispatch_table.go @@ -22,6 +22,7 @@ func registerRange(from, to OpCode, factory func(n int) instruction, gas uint64) c := 1 for i := from; i <= to; i++ { register(i, handler{factory(c), 0, gas}) + c++ } } diff --git a/state/runtime/evm/dispatch_table_test.go b/state/runtime/evm/dispatch_table_test.go index a3dfe906a6..9c296660aa 100644 --- a/state/runtime/evm/dispatch_table_test.go +++ b/state/runtime/evm/dispatch_table_test.go @@ -29,6 +29,7 @@ func TestPushOpcodes(t *testing.T) { assert.Len(t, res, c) assert.True(t, bytes.HasPrefix(code[1:], res)) + c++ } } diff --git a/state/runtime/evm/evm_test.go b/state/runtime/evm/evm_test.go index 6b20b48e29..f690305fcb 100644 --- a/state/runtime/evm/evm_test.go +++ b/state/runtime/evm/evm_test.go @@ -237,9 +237,11 @@ func TestRun(t *testing.T) { contract := newMockContract(tt.value, tt.gas, tt.code) host := &mockHost{} config := tt.config + if config == nil { config = &chain.ForksInTime{} } + res := evm.Run(contract, host, config) assert.Equal(t, tt.expected, res) }) @@ -408,6 +410,7 @@ func TestRunWithTracer(t *testing.T) { host := &mockHost{ tracer: tracer, } + config := tt.config if config == nil { config = &chain.ForksInTime{} diff --git a/state/runtime/evm/instructions.go b/state/runtime/evm/instructions.go index 8fc6d3bb19..b7d369b648 100644 --- a/state/runtime/evm/instructions.go +++ b/state/runtime/evm/instructions.go @@ -88,9 +88,11 @@ func opSDiv(c *state) { } else { neg := a.Sign() != b.Sign() b.Div(a.Abs(a), b.Abs(b)) + if neg { b.Neg(b) } + toU256(b) } } @@ -118,9 +120,11 @@ func opSMod(c *state) { } else { neg := a.Sign() < 0 b.Mod(a.Abs(a), b.Abs(b)) + if neg { b.Neg(b) } + toU256(b) } } @@ -1347,6 +1351,7 @@ func (c *state) buildCallContract(op OpCode) (*runtime.Contract, uint64, uint64, return nil, 0, 0, nil } + gas = initialGas.Uint64() } diff --git a/state/runtime/evm/instructions_test.go b/state/runtime/evm/instructions_test.go index 143921b989..74fa195204 100644 --- a/state/runtime/evm/instructions_test.go +++ b/state/runtime/evm/instructions_test.go @@ -639,6 +639,7 @@ func TestSStore(t *testing.T) { mockHost := &mockHost{} mockHost.On("SetStorage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(runtime.StorageUnchanged).Once() + s.host = mockHost s.push(one) @@ -659,6 +660,7 @@ func TestSStore(t *testing.T) { mockHost := &mockHost{} mockHost.On("SetStorage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(runtime.StorageModified).Once() + s.host = mockHost s.push(one) @@ -676,6 +678,7 @@ func TestSStore(t *testing.T) { mockHost := &mockHost{} mockHost.On("SetStorage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(runtime.StorageAdded).Once() + s.host = mockHost s.push(one) @@ -696,6 +699,7 @@ func TestSStore(t *testing.T) { mockHost := &mockHost{} mockHost.On("SetStorage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(runtime.StorageDeleted).Once() + s.host = mockHost s.push(one) @@ -1093,6 +1097,7 @@ func TestExtCodeCopy(t *testing.T) { t.Run("NonEIP150Fork", func(t *testing.T) { leftGas := uint64(974) + s, cancelFn := getState(&chain.ForksInTime{}) defer cancelFn() diff --git a/state/runtime/evm/state.go b/state/runtime/evm/state.go index 8f6fa2ebd0..52aba2b9f9 100644 --- a/state/runtime/evm/state.go +++ b/state/runtime/evm/state.go @@ -104,6 +104,7 @@ func (c *state) reset() { return x.SetInt64(0) }, c.stack[i]) } + c.stack = c.stack[:0] c.tmp = c.tmp[:0] c.ret = c.ret[:0] diff --git a/state/runtime/runtime.go b/state/runtime/runtime.go index c180c7c777..809b9564d9 100644 --- a/state/runtime/runtime.go +++ b/state/runtime/runtime.go @@ -119,6 +119,7 @@ type ExecutionResult struct { GasUsed uint64 // Total gas used as result of execution Err error // Any error encountered during the execution, listed below Address types.Address // Contract address + AccessList *AccessList // Access list } func (r *ExecutionResult) Succeeded() bool { return r.Err == nil } diff --git a/state/transition_test.go b/state/transition_test.go index c225652b87..692f39b187 100644 --- a/state/transition_test.go +++ b/state/transition_test.go @@ -151,6 +151,7 @@ func TestTransfer(t *testing.T) { err := transition.Transfer(tt.from, tt.to, amount) assert.Equal(t, tt.expectedErr, err) + if err == nil { // should move balance oldBalanceOfFrom := big.NewInt(int64(tt.preState[tt.from].Balance)) diff --git a/state/txn.go b/state/txn.go index 0eeb618ff7..168481d22c 100644 --- a/state/txn.go +++ b/state/txn.go @@ -150,7 +150,7 @@ func (txn *Txn) upsertAccount(addr types.Address, create bool, f func(object *St func (txn *Txn) AddSealingReward(addr types.Address, balance *big.Int) { txn.upsertAccount(addr, true, func(object *StateObject) { if object.Suicide { - *object = *newStateObject(txn) + *object = *newStateObject() object.Account.Balance.SetBytes(balance.Bytes()) } else { object.Account.Balance.Add(object.Account.Balance, balance) @@ -366,6 +366,7 @@ func (txn *Txn) IncrNonce(addr types.Address) error { return } + object.Account.Nonce++ }) @@ -447,6 +448,7 @@ func (txn *Txn) Suicide(addr types.Address) bool { suicided = true object.Suicide = true } + if object != nil { object.Account.Balance = new(big.Int) } @@ -536,7 +538,7 @@ func (txn *Txn) Empty(addr types.Address) bool { return obj.Empty() } -func newStateObject(txn *Txn) *StateObject { +func newStateObject() *StateObject { return &StateObject{ Account: &Account{ Balance: big.NewInt(0), @@ -571,6 +573,7 @@ func (txn *Txn) CleanDeleteObjects(deleteEmptyObjects bool) error { if !ok { return false } + if a.Suicide || a.Empty() && deleteEmptyObjects { remove = append(remove, k) } @@ -637,6 +640,7 @@ func (txn *Txn) Commit(deleteEmptyObjects bool) ([]*Object, error) { } else { store.Val = v.([]byte) //nolint:forcetypeassert } + obj.Storage = append(obj.Storage, store) return false diff --git a/txpool/event_subscription_test.go b/txpool/event_subscription_test.go index e7c6412927..d003a71141 100644 --- a/txpool/event_subscription_test.go +++ b/txpool/event_subscription_test.go @@ -123,6 +123,7 @@ func TestEventSubscription_ProcessedEvents(t *testing.T) { // Set the event listener processed := int64(0) + go func() { for range subscription.outputCh { atomic.AddInt64(&processed, 1) @@ -133,6 +134,7 @@ func TestEventSubscription_ProcessedEvents(t *testing.T) { var wg sync.WaitGroup for _, event := range testCase.events { wg.Add(1) + go func(event *proto.TxPoolEvent) { defer wg.Done() @@ -141,8 +143,11 @@ func TestEventSubscription_ProcessedEvents(t *testing.T) { } wg.Wait() + eventWaitCtx, eventWaitFn := context.WithTimeout(context.Background(), 30*time.Second) + defer eventWaitFn() + if _, err := tests.RetryUntilTimeout(eventWaitCtx, func() (interface{}, bool) { return nil, atomic.LoadInt64(&processed) != int64(testCase.expectedProcessed) }); err != nil { diff --git a/txpool/txpool_test.go b/txpool/txpool_test.go index 0f4cfa3ed3..22c01dbad6 100644 --- a/txpool/txpool_test.go +++ b/txpool/txpool_test.go @@ -152,6 +152,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrInvalidTxType", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.StateTxType) @@ -170,7 +171,9 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrTxTypeNotSupported London hardfork not enabled", func(t *testing.T) { t.Parallel() + pool := setupPool() + pool.forks.RemoveFork(chain.London) tx := newTx(defaultAddr, 0, 1, types.DynamicFeeTxType) @@ -188,6 +191,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrNegativeValue", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -201,6 +205,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrBlockLimitExceeded", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -217,6 +222,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrExtractSignature", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -231,6 +237,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrInvalidSender", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(addr1, 0, 1, types.LegacyTxType) @@ -247,7 +254,9 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrUnderpriced", func(t *testing.T) { t.Parallel() + pool := setupPool() + pool.priceLimit = 1000000 tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) // gasPrice == 1 @@ -261,7 +270,9 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrInvalidAccountState", func(t *testing.T) { t.Parallel() + pool := setupPool() + pool.store = faultyMockStore{} // nonce is 1000000 so ErrNonceTooLow @@ -277,6 +288,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrTxPoolOverflow", func(t *testing.T) { t.Parallel() + pool := setupPool() // fill the pool @@ -293,6 +305,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("FillTxPoolToTheLimit", func(t *testing.T) { t.Parallel() + pool := setupPool() // fill the pool leaving only 1 slot @@ -309,6 +322,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrIntrinsicGas", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -323,6 +337,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrAlreadyKnown", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -340,6 +355,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrAlreadyKnown", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -362,6 +378,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrOversizedData", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -382,6 +399,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrNonceTooLow", func(t *testing.T) { t.Parallel() + pool := setupPool() // faultyMockStore.GetNonce() == 99999 @@ -397,6 +415,7 @@ func TestAddTxErrors(t *testing.T) { t.Run("ErrInsufficientFunds", func(t *testing.T) { t.Parallel() + pool := setupPool() tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) @@ -543,6 +562,7 @@ func TestAddTxHighPressure(t *testing.T) { pool.SetSigner(&mockSigner{}) pool.getOrCreateAccount(addr1) + pool.accounts.get(addr1).nextNonce = 5 // mock high pressure @@ -570,6 +590,7 @@ func TestAddTxHighPressure(t *testing.T) { pool.SetSigner(&mockSigner{}) pool.getOrCreateAccount(addr1) + pool.accounts.get(addr1).nextNonce = 5 // mock high pressure @@ -602,6 +623,7 @@ func TestAddGossipTx(t *testing.T) { pool, err := newTestPool() pool.localPeerID = peer.ID("test") + assert.NoError(t, err) pool.SetSigner(signer) @@ -2191,7 +2213,9 @@ func Test_TxPool_validateTx(t *testing.T) { t.Run("tx input larger than the TxPoolMaxInitCodeSize", func(t *testing.T) { t.Parallel() + pool := setupPool() + pool.forks = chain.AllForksEnabled.Copy() input := make([]byte, state.TxPoolMaxInitCodeSize+1) @@ -2210,7 +2234,9 @@ func Test_TxPool_validateTx(t *testing.T) { t.Run("tx input the same as TxPoolMaxInitCodeSize", func(t *testing.T) { t.Parallel() + pool := setupPool() + pool.forks = chain.AllForksEnabled.Copy() input := make([]byte, state.TxPoolMaxInitCodeSize) @@ -2330,7 +2356,9 @@ func Test_TxPool_validateTx(t *testing.T) { t.Run("eip-1559 tx placed without eip-1559 fork enabled", func(t *testing.T) { t.Parallel() + pool := setupPool() + pool.forks = chain.AllForksEnabled.Copy() pool.forks.RemoveFork(chain.London) @@ -2619,10 +2647,13 @@ func TestResetAccounts_Enqueued(t *testing.T) { // setup prestate totalTx := 0 expectedPromoted := uint64(0) + for addr, txs := range allTxs { expectedPromoted += expected.accounts[addr].promoted + for _, tx := range txs { totalTx++ + assert.NoError(t, pool.addTx(local, tx)) } } @@ -2714,10 +2745,13 @@ func TestResetAccounts_Enqueued(t *testing.T) { // setup prestate expectedEnqueuedTx := 0 expectedPromotedTx := uint64(0) + for addr, txs := range allTxs { expectedPromotedTx += expected.accounts[addr].promoted + for _, tx := range txs { expectedEnqueuedTx++ + assert.NoError(t, pool.addTx(local, tx)) } } @@ -2903,6 +2937,7 @@ func TestExecutablesOrder(t *testing.T) { ) expectedPromotedTx := 0 + for _, txs := range test.allTxs { for _, tx := range txs { expectedPromotedTx++ @@ -2921,8 +2956,10 @@ func TestExecutablesOrder(t *testing.T) { pool.Prepare() var successful []*types.Transaction + for { tx := pool.Peek() + if tx == nil { break } @@ -3102,6 +3139,7 @@ func TestRecovery(t *testing.T) { // setup prestate totalTx := 0 expectedEnqueued := uint64(0) + for addr, txs := range test.allTxs { // preset nonce so promotions can happen acc := pool.getOrCreateAccount(addr) @@ -3112,6 +3150,7 @@ func TestRecovery(t *testing.T) { // send txs for _, sTx := range txs { totalTx++ + assert.NoError(t, pool.addTx(local, sTx.tx)) } } @@ -3124,8 +3163,10 @@ func TestRecovery(t *testing.T) { func() { pool.Prepare() + for { tx := pool.Peek() + if tx == nil { break } @@ -3322,9 +3363,11 @@ func TestGetTxs(t *testing.T) { // send txs expectedPromotedTx := 0 + for _, txs := range test.allTxs { nonce := uint64(0) promotable := uint64(0) + for _, tx := range txs { // send all txs if tx.Nonce() == nonce+promotable { @@ -3419,6 +3462,7 @@ func TestSetSealing(t *testing.T) { // Set initial value pool.sealing.Store(false) + if test.initialValue { pool.sealing.Store(true) } @@ -3700,6 +3744,7 @@ func TestAddTx_TxReplacement(t *testing.T) { pool, err := newTestPool() pool.chainID = big.NewInt(100) + require.NoError(t, err) pool.baseFee = 100 diff --git a/types/rlp_marshal_storage.go b/types/rlp_marshal_storage.go index 6d11d009ab..1c8f5055a0 100644 --- a/types/rlp_marshal_storage.go +++ b/types/rlp_marshal_storage.go @@ -21,6 +21,7 @@ func (b *Body) marshalRLPWith(ar *fastrlp.Arena) *fastrlp.Value { for _, tx := range b.Transactions { v0.Set(tx.marshalStoreRLPWith(ar)) } + vv.Set(v0) } @@ -31,6 +32,7 @@ func (b *Body) marshalRLPWith(ar *fastrlp.Arena) *fastrlp.Value { for _, uncle := range b.Uncles { v1.Set(uncle.MarshalRLPWith(ar)) } + vv.Set(v1) }