go-ethereum/internal/ethapi/api_test.go

3969 lines
142 KiB
Go

// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package ethapi
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
"maps"
"math/big"
"os"
"path/filepath"
"reflect"
"slices"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/blocktest"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/triedb"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
)
func testTransactionMarshal(t *testing.T, tests []txData, config *params.ChainConfig) {
var (
signer = types.LatestSigner(config)
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
)
for i, tt := range tests {
var tx2 types.Transaction
tx, err := types.SignNewTx(key, signer, tt.Tx)
if err != nil {
t.Fatalf("test %d: signing failed: %v", i, err)
}
// Regular transaction
if data, err := json.Marshal(tx); err != nil {
t.Fatalf("test %d: marshalling failed; %v", i, err)
} else if err = tx2.UnmarshalJSON(data); err != nil {
t.Fatalf("test %d: sunmarshal failed: %v", i, err)
} else if want, have := tx.Hash(), tx2.Hash(); want != have {
t.Fatalf("test %d: stx changed, want %x have %x", i, want, have)
}
// rpcTransaction
rpcTx := newRPCTransaction(tx, common.Hash{}, 0, 0, 0, nil, config)
if data, err := json.Marshal(rpcTx); err != nil {
t.Fatalf("test %d: marshalling failed; %v", i, err)
} else if err = tx2.UnmarshalJSON(data); err != nil {
t.Fatalf("test %d: unmarshal failed: %v", i, err)
} else if want, have := tx.Hash(), tx2.Hash(); want != have {
t.Fatalf("test %d: tx changed, want %x have %x", i, want, have)
} else {
want, have := tt.Want, string(data)
require.JSONEqf(t, want, have, "test %d: rpc json not match, want %s have %s", i, want, have)
}
}
}
func TestTransaction_RoundTripRpcJSON(t *testing.T) {
t.Parallel()
var (
config = params.AllEthashProtocolChanges
tests = allTransactionTypes(common.Address{0xde, 0xad}, config)
)
testTransactionMarshal(t, tests, config)
}
func TestTransactionBlobTx(t *testing.T) {
t.Parallel()
config := *params.TestChainConfig
config.ShanghaiBlock = big.NewInt(0)
config.CancunBlock = big.NewInt(0)
tests := allBlobTxs(common.Address{0xde, 0xad}, &config)
testTransactionMarshal(t, tests, &config)
}
type txData struct {
Tx types.TxData
Want string
}
func allTransactionTypes(addr common.Address, config *params.ChainConfig) []txData {
return []txData{
{
Tx: &types.LegacyTx{
Nonce: 5,
GasPrice: big.NewInt(6),
Gas: 7,
To: &addr,
Value: big.NewInt(8),
Data: []byte{0, 1, 2, 3, 4},
V: big.NewInt(9),
R: big.NewInt(10),
S: big.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x7",
"gasPrice": "0x6",
"hash": "0x5f3240454cd09a5d8b1c5d651eefae7a339262875bcd2d0e6676f3d989967008",
"input": "0x0001020304",
"nonce": "0x5",
"to": "0xdead000000000000000000000000000000000000",
"transactionIndex": null,
"value": "0x8",
"type": "0x0",
"chainId": "0x539",
"v": "0xa96",
"r": "0xbc85e96592b95f7160825d837abb407f009df9ebe8f1b9158a4b8dd093377f75",
"s": "0x1b55ea3af5574c536967b039ba6999ef6c89cf22fc04bcb296e0e8b0b9b576f5"
}`,
}, {
Tx: &types.LegacyTx{
Nonce: 5,
GasPrice: big.NewInt(6),
Gas: 7,
To: nil,
Value: big.NewInt(8),
Data: []byte{0, 1, 2, 3, 4},
V: big.NewInt(32),
R: big.NewInt(10),
S: big.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x7",
"gasPrice": "0x6",
"hash": "0x806e97f9d712b6cb7e781122001380a2837531b0fc1e5f5d78174ad4cb699873",
"input": "0x0001020304",
"nonce": "0x5",
"to": null,
"transactionIndex": null,
"value": "0x8",
"type": "0x0",
"chainId": "0x539",
"v": "0xa96",
"r": "0x9dc28b267b6ad4e4af6fe9289668f9305c2eb7a3241567860699e478af06835a",
"s": "0xa0b51a071aa9bed2cd70aedea859779dff039e3630ea38497d95202e9b1fec7"
}`,
},
{
Tx: &types.AccessListTx{
ChainID: config.ChainID,
Nonce: 5,
GasPrice: big.NewInt(6),
Gas: 7,
To: &addr,
Value: big.NewInt(8),
Data: []byte{0, 1, 2, 3, 4},
AccessList: types.AccessList{
types.AccessTuple{
Address: common.Address{0x2},
StorageKeys: []common.Hash{types.EmptyRootHash},
},
},
V: big.NewInt(32),
R: big.NewInt(10),
S: big.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x7",
"gasPrice": "0x6",
"hash": "0x121347468ee5fe0a29f02b49b4ffd1c8342bc4255146bb686cd07117f79e7129",
"input": "0x0001020304",
"nonce": "0x5",
"to": "0xdead000000000000000000000000000000000000",
"transactionIndex": null,
"value": "0x8",
"type": "0x1",
"accessList": [
{
"address": "0x0200000000000000000000000000000000000000",
"storageKeys": [
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
]
}
],
"chainId": "0x539",
"v": "0x0",
"r": "0xf372ad499239ae11d91d34c559ffc5dab4daffc0069e03afcabdcdf231a0c16b",
"s": "0x28573161d1f9472fa0fd4752533609e72f06414f7ab5588699a7141f65d2abf",
"yParity": "0x0"
}`,
}, {
Tx: &types.AccessListTx{
ChainID: config.ChainID,
Nonce: 5,
GasPrice: big.NewInt(6),
Gas: 7,
To: nil,
Value: big.NewInt(8),
Data: []byte{0, 1, 2, 3, 4},
AccessList: types.AccessList{
types.AccessTuple{
Address: common.Address{0x2},
StorageKeys: []common.Hash{types.EmptyRootHash},
},
},
V: big.NewInt(32),
R: big.NewInt(10),
S: big.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x7",
"gasPrice": "0x6",
"hash": "0x067c3baebede8027b0f828a9d933be545f7caaec623b00684ac0659726e2055b",
"input": "0x0001020304",
"nonce": "0x5",
"to": null,
"transactionIndex": null,
"value": "0x8",
"type": "0x1",
"accessList": [
{
"address": "0x0200000000000000000000000000000000000000",
"storageKeys": [
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
]
}
],
"chainId": "0x539",
"v": "0x1",
"r": "0x542981b5130d4613897fbab144796cb36d3cb3d7807d47d9c7f89ca7745b085c",
"s": "0x7425b9dd6c5deaa42e4ede35d0c4570c4624f68c28d812c10d806ffdf86ce63",
"yParity": "0x1"
}`,
}, {
Tx: &types.DynamicFeeTx{
ChainID: config.ChainID,
Nonce: 5,
GasTipCap: big.NewInt(6),
GasFeeCap: big.NewInt(9),
Gas: 7,
To: &addr,
Value: big.NewInt(8),
Data: []byte{0, 1, 2, 3, 4},
AccessList: types.AccessList{
types.AccessTuple{
Address: common.Address{0x2},
StorageKeys: []common.Hash{types.EmptyRootHash},
},
},
V: big.NewInt(32),
R: big.NewInt(10),
S: big.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x7",
"gasPrice": "0x9",
"maxFeePerGas": "0x9",
"maxPriorityFeePerGas": "0x6",
"hash": "0xb63e0b146b34c3e9cb7fbabb5b3c081254a7ded6f1b65324b5898cc0545d79ff",
"input": "0x0001020304",
"nonce": "0x5",
"to": "0xdead000000000000000000000000000000000000",
"transactionIndex": null,
"value": "0x8",
"type": "0x2",
"accessList": [
{
"address": "0x0200000000000000000000000000000000000000",
"storageKeys": [
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
]
}
],
"chainId": "0x539",
"v": "0x1",
"r": "0x3b167e05418a8932cd53d7578711fe1a76b9b96c48642402bb94978b7a107e80",
"s": "0x22f98a332d15ea2cc80386c1ebaa31b0afebfa79ebc7d039a1e0074418301fef",
"yParity": "0x1"
}`,
}, {
Tx: &types.DynamicFeeTx{
ChainID: config.ChainID,
Nonce: 5,
GasTipCap: big.NewInt(6),
GasFeeCap: big.NewInt(9),
Gas: 7,
To: nil,
Value: big.NewInt(8),
Data: []byte{0, 1, 2, 3, 4},
AccessList: types.AccessList{},
V: big.NewInt(32),
R: big.NewInt(10),
S: big.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x7",
"gasPrice": "0x9",
"maxFeePerGas": "0x9",
"maxPriorityFeePerGas": "0x6",
"hash": "0xcbab17ee031a9d5b5a09dff909f0a28aedb9b295ac0635d8710d11c7b806ec68",
"input": "0x0001020304",
"nonce": "0x5",
"to": null,
"transactionIndex": null,
"value": "0x8",
"type": "0x2",
"accessList": [],
"chainId": "0x539",
"v": "0x0",
"r": "0x6446b8a682db7e619fc6b4f6d1f708f6a17351a41c7fbd63665f469bc78b41b9",
"s": "0x7626abc15834f391a117c63450047309dbf84c5ce3e8e609b607062641e2de43",
"yParity": "0x0"
}`,
},
}
}
func allBlobTxs(addr common.Address, config *params.ChainConfig) []txData {
return []txData{
{
Tx: &types.BlobTx{
Nonce: 6,
GasTipCap: uint256.NewInt(1),
GasFeeCap: uint256.NewInt(5),
Gas: 6,
To: addr,
BlobFeeCap: uint256.NewInt(1),
BlobHashes: []common.Hash{{1}},
Value: new(uint256.Int),
V: uint256.NewInt(32),
R: uint256.NewInt(10),
S: uint256.NewInt(11),
},
Want: `{
"blockHash": null,
"blockNumber": null,
"from": "0x71562b71999873db5b286df957af199ec94617f7",
"gas": "0x6",
"gasPrice": "0x5",
"maxFeePerGas": "0x5",
"maxPriorityFeePerGas": "0x1",
"maxFeePerBlobGas": "0x1",
"hash": "0x1f2b59a20e61efc615ad0cbe936379d6bbea6f938aafaf35eb1da05d8e7f46a3",
"input": "0x",
"nonce": "0x6",
"to": "0xdead000000000000000000000000000000000000",
"transactionIndex": null,
"value": "0x0",
"type": "0x3",
"accessList": [],
"chainId": "0x1",
"blobVersionedHashes": [
"0x0100000000000000000000000000000000000000000000000000000000000000"
],
"v": "0x0",
"r": "0x618be8908e0e5320f8f3b48042a079fe5a335ebd4ed1422a7d2207cd45d872bc",
"s": "0x27b2bc6c80e849a8e8b764d4549d8c2efac3441e73cf37054eb0a9b9f8e89b27",
"yParity": "0x0"
}`,
},
}
}
func newTestAccountManager(t *testing.T) (*accounts.Manager, accounts.Account) {
var (
dir = t.TempDir()
am = accounts.NewManager(nil)
b = keystore.NewKeyStore(dir, 2, 1)
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
)
acc, err := b.ImportECDSA(testKey, "")
if err != nil {
t.Fatalf("failed to create test account: %v", err)
}
if err := b.Unlock(acc, ""); err != nil {
t.Fatalf("failed to unlock account: %v\n", err)
}
am.AddBackend(b)
return am, acc
}
type testBackend struct {
db ethdb.Database
chain *core.BlockChain
pending *types.Block
accman *accounts.Manager
acc accounts.Account
}
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.Engine, generator func(i int, b *core.BlockGen)) *testBackend {
var (
cacheConfig = &core.CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 0,
TrieDirtyDisabled: true, // Archive mode
}
)
accman, acc := newTestAccountManager(t)
// gspec.Alloc[acc.Address] = types.Account{Balance: big.NewInt(params.Ether)}
// Generate blocks for testing
db, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
txlookupLimit := uint64(0)
chain, err := core.NewBlockChain(db, cacheConfig, gspec, nil, engine, vm.Config{}, nil, &txlookupLimit, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
backend := &testBackend{db: db, chain: chain, accman: accman, acc: acc}
return backend
}
func (b *testBackend) setPendingBlock(block *types.Block) {
b.pending = block
}
func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return big.NewInt(0), nil
}
func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
return nil, nil, nil, nil, nil, nil, nil
}
func (b testBackend) BlobBaseFee(ctx context.Context) *big.Int { return new(big.Int) }
func (b testBackend) ChainDb() ethdb.Database { return b.db }
func (b testBackend) AccountManager() *accounts.Manager { return b.accman }
func (b testBackend) ExtRPCEnabled() bool { return false }
func (b testBackend) RPCGasCap() uint64 { return 10000000 }
func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second }
func (b testBackend) RPCTxFeeCap() float64 { return 0 }
func (b testBackend) UnprotectedAllowed() bool { return false }
func (b testBackend) SetHead(number uint64) {}
func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
if number == rpc.LatestBlockNumber {
return b.chain.CurrentBlock(), nil
}
if number == rpc.PendingBlockNumber && b.pending != nil {
return b.pending.Header(), nil
}
return b.chain.GetHeaderByNumber(uint64(number)), nil
}
func (b testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
return b.chain.GetHeaderByHash(hash), nil
}
func (b testBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.HeaderByNumber(ctx, blockNr)
}
if blockHash, ok := blockNrOrHash.Hash(); ok {
return b.HeaderByHash(ctx, blockHash)
}
panic("unknown type rpc.BlockNumberOrHash")
}
func (b testBackend) CurrentHeader() *types.Header { return b.chain.CurrentHeader() }
func (b testBackend) CurrentBlock() *types.Header { return b.chain.CurrentBlock() }
func (b testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
if number == rpc.LatestBlockNumber {
head := b.chain.CurrentBlock()
return b.chain.GetBlock(head.Hash(), head.Number.Uint64()), nil
}
if number == rpc.PendingBlockNumber {
return b.pending, nil
}
return b.chain.GetBlockByNumber(uint64(number)), nil
}
func (b testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
return b.chain.GetBlockByHash(hash), nil
}
func (b testBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.BlockByNumber(ctx, blockNr)
}
if blockHash, ok := blockNrOrHash.Hash(); ok {
return b.BlockByHash(ctx, blockHash)
}
panic("unknown type rpc.BlockNumberOrHash")
}
func (b testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
return b.chain.GetBlock(hash, uint64(number.Int64())).Body(), nil
}
func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
if number == rpc.PendingBlockNumber {
panic("pending state not implemented")
}
header, err := b.HeaderByNumber(ctx, number)
if err != nil {
return nil, nil, err
}
if header == nil {
return nil, nil, errors.New("header not found")
}
stateDb, err := b.chain.StateAt(header.Root)
return stateDb, header, err
}
func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
if blockNr, ok := blockNrOrHash.Number(); ok {
return b.StateAndHeaderByNumber(ctx, blockNr)
}
panic("only implemented for number")
}
func (b testBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { panic("implement me") }
func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
header, err := b.HeaderByHash(ctx, hash)
if header == nil || err != nil {
return nil, err
}
receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config())
return receipts, nil
}
func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
if b.pending != nil && hash == b.pending.Hash() {
return nil
}
return big.NewInt(1)
}
func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM {
if vmConfig == nil {
vmConfig = b.chain.GetVMConfig()
}
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(header, b.chain, nil)
if blockContext != nil {
context = *blockContext
}
return vm.NewEVM(context, txContext, state, b.chain.Config(), *vmConfig)
}
func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
panic("implement me")
}
func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
panic("implement me")
}
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
panic("implement me")
}
func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
found := true
if tx == nil {
found = false
}
return found, tx, blockHash, blockNumber, index, nil
}
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction {
return nil
}
func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
return 0, nil
}
func (b testBackend) Stats() (pending int, queued int) { panic("implement me") }
func (b testBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
panic("implement me")
}
func (b testBackend) TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) {
panic("implement me")
}
func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event.Subscription {
panic("implement me")
}
func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() }
func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() }
func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
panic("implement me")
}
func (b testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
panic("implement me")
}
func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
panic("implement me")
}
func (b testBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
panic("implement me")
}
func (b testBackend) BloomStatus() (uint64, uint64) { panic("implement me") }
func (b testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
panic("implement me")
}
// GetBorBlockTransaction returns bor block tx
func (b testBackend) GetBorBlockTransaction(ctx context.Context, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
tx, blockHash, blockNumber, index := rawdb.ReadBorTransaction(b.ChainDb(), hash)
return tx, blockHash, blockNumber, index, nil
}
func (b testBackend) GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
tx, blockHash, blockNumber, index := rawdb.ReadBorTransactionWithBlockHash(b.ChainDb(), txHash, blockHash)
return tx, blockHash, blockNumber, index, nil
}
func (b testBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
panic("implement me")
}
func (b testBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
panic("implement me")
}
func (b testBackend) GetWhitelistedCheckpoint() (bool, uint64, common.Hash) {
panic("implement me")
}
func (b testBackend) GetWhitelistedMilestone() (bool, uint64, common.Hash) {
panic("implement me")
}
func (b testBackend) PurgeWhitelistedMilestone() {
panic("implement me")
}
func (b testBackend) PurgeWhitelistedCheckpoint() {
panic("implement me")
}
func (b testBackend) RPCRpcReturnDataLimit() uint64 {
return 0
}
func (b testBackend) SubscribeChain2HeadEvent(ch chan<- core.Chain2HeadEvent) event.Subscription {
panic("implement me")
}
func (b testBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
panic("implement me")
}
func (b testBackend) PeerStats() interface{} {
panic("implement me")
}
func (b testBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
receipt, err := b.GetBorBlockReceipt(ctx, hash)
if err != nil || receipt == nil {
return nil, err
}
return receipt.Logs, nil
}
func (b testBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
receipt := b.chain.GetBorReceiptByHash(hash)
if receipt == nil {
return nil, ethereum.NotFound
}
return receipt, nil
}
func TestEstimateGas(t *testing.T) {
t.Parallel()
// Initialize test accounts
var (
accounts = newAccounts(2)
genesis = &core.Genesis{
Config: params.MergedTestChainConfig,
Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
},
}
genBlocks = 10
signer = types.HomesteadSigner{}
randomAccounts = newAccounts(2)
)
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
b.AddTx(tx)
b.SetPoS()
}))
var testSuite = []struct {
blockNumber rpc.BlockNumber
call TransactionArgs
overrides StateOverride
expectErr error
want uint64
}{
// simple transfer on latest block
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: nil,
want: 21000,
},
// simple transfer with insufficient funds on latest block
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &randomAccounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: core.ErrInsufficientFunds,
want: 21000,
},
// empty create
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{},
expectErr: nil,
want: 53000,
},
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{},
overrides: StateOverride{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
},
expectErr: nil,
want: 53000,
},
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
overrides: StateOverride{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
},
expectErr: core.ErrInsufficientFunds,
},
// Test for a bug where the gas price was set to zero but the basefee non-zero
//
// contract BasefeeChecker {
// constructor() {
// require(tx.gasprice >= block.basefee);
// if (tx.gasprice > 0) {
// require(block.basefee > 0);
// }
// }
//}
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
Input: hex2Bytes("6080604052348015600f57600080fd5b50483a1015601c57600080fd5b60003a111560315760004811603057600080fd5b5b603f80603e6000396000f3fe6080604052600080fdfea264697066735822122060729c2cee02b10748fae5200f1c9da4661963354973d9154c13a8e9ce9dee1564736f6c63430008130033"),
GasPrice: (*hexutil.Big)(big.NewInt(1_000_000_000)), // Legacy as pricing
},
expectErr: nil,
want: 67617,
},
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
Input: hex2Bytes("6080604052348015600f57600080fd5b50483a1015601c57600080fd5b60003a111560315760004811603057600080fd5b5b603f80603e6000396000f3fe6080604052600080fdfea264697066735822122060729c2cee02b10748fae5200f1c9da4661963354973d9154c13a8e9ce9dee1564736f6c63430008130033"),
MaxFeePerGas: (*hexutil.Big)(big.NewInt(1_000_000_000)), // 1559 gas pricing
},
expectErr: nil,
want: 67617,
},
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
Input: hex2Bytes("6080604052348015600f57600080fd5b50483a1015601c57600080fd5b60003a111560315760004811603057600080fd5b5b603f80603e6000396000f3fe6080604052600080fdfea264697066735822122060729c2cee02b10748fae5200f1c9da4661963354973d9154c13a8e9ce9dee1564736f6c63430008130033"),
GasPrice: nil, // No legacy gas pricing
MaxFeePerGas: nil, // No 1559 gas pricing
},
expectErr: nil,
want: 67595,
},
// Blobs should have no effect on gas estimate
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1)),
BlobHashes: []common.Hash{{0x01, 0x22}},
BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
},
want: 21000,
},
}
for i, tc := range testSuite {
result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides)
if tc.expectErr != nil {
if err == nil {
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
continue
}
if !errors.Is(err, tc.expectErr) {
t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
}
continue
}
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
if float64(result) > float64(tc.want)*(1+estimateGasErrorRatio) {
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, uint64(result), tc.want)
}
}
}
func TestCall(t *testing.T) {
t.Parallel()
// Initialize test accounts
var (
accounts = newAccounts(3)
dad = common.HexToAddress("0x0000000000000000000000000000000000000dad")
genesis = &core.Genesis{
Config: params.MergedTestChainConfig,
Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
dad: {
Balance: big.NewInt(params.Ether),
Nonce: 1,
Storage: map[common.Hash]common.Hash{
common.Hash{}: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"),
},
},
},
}
genBlocks = 10
signer = types.HomesteadSigner{}
)
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
b.AddTx(tx)
b.SetPoS()
}))
randomAccounts := newAccounts(3)
var testSuite = []struct {
name string
blockNumber rpc.BlockNumber
overrides StateOverride
call TransactionArgs
blockOverrides BlockOverrides
expectErr error
want string
}{
// transfer on genesis
{
name: "transfer-on-genesis",
blockNumber: rpc.BlockNumber(0),
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: nil,
want: "0x",
},
// transfer on the head
{
name: "transfer-on-the-head",
blockNumber: rpc.BlockNumber(genBlocks),
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: nil,
want: "0x",
},
// transfer on a non-existent block, error expects
{
name: "transfer-non-existent-block",
blockNumber: rpc.BlockNumber(genBlocks + 1),
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: errors.New("header not found"),
},
// transfer on the latest block
{
name: "transfer-latest-block",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: nil,
want: "0x",
},
// Call which can only succeed if state is state overridden
{
name: "state-override-success",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
overrides: StateOverride{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
},
want: "0x",
},
// Invalid call without state overriding
{
name: "insufficient-funds-simple",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
expectErr: core.ErrInsufficientFunds,
},
// Successful simple contract call
//
// // SPDX-License-Identifier: GPL-3.0
//
// pragma solidity >=0.7.0 <0.8.0;
//
// /**
// * @title Storage
// * @dev Store & retrieve value in a variable
// */
// contract Storage {
// uint256 public number;
// constructor() {
// number = block.number;
// }
// }
{
name: "simple-contract-call",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
Data: hex2Bytes("8381f58a"), // call number()
},
overrides: StateOverride{
randomAccounts[2].addr: OverrideAccount{
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))},
},
},
want: "0x000000000000000000000000000000000000000000000000000000000000007b",
},
// Block overrides should work
{
name: "block-override",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[1].addr,
Input: &hexutil.Bytes{
0x43, // NUMBER
0x60, 0x00, 0x52, // MSTORE offset 0
0x60, 0x20, 0x60, 0x00, 0xf3,
},
},
blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
want: "0x000000000000000000000000000000000000000000000000000000000000000b",
},
// Clear storage trie
{
name: "clear-storage-trie",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[1].addr,
// Yul:
// object "Test" {
// code {
// let dad := 0x0000000000000000000000000000000000000dad
// if eq(balance(dad), 0) {
// revert(0, 0)
// }
// let slot := sload(0)
// mstore(0, slot)
// return(0, 32)
// }
// }
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
},
overrides: StateOverride{
dad: OverrideAccount{
State: map[common.Hash]common.Hash{},
},
},
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
},
// Invalid blob tx
{
name: "invalid-blob-tx",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[1].addr,
Input: &hexutil.Bytes{0x00},
BlobHashes: []common.Hash{},
},
expectErr: core.ErrBlobTxCreate,
},
// BOR Doens't support blob tx
// BLOBHASH opcode
// {
// blockNumber: rpc.LatestBlockNumber,
// call: TransactionArgs{
// From: &accounts[1].addr,
// To: &randomAccounts[2].addr,
// BlobHashes: []common.Hash{{0x01, 0x22}},
// BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
// },
// overrides: StateOverride{
// randomAccounts[2].addr: {
// Code: hex2Bytes("60004960005260206000f3"),
// },
// },
// want: "0x0122000000000000000000000000000000000000000000000000000000000000",
// },
// Clear the entire storage set
{
name: "blobhash-opcode",
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[1].addr,
// Yul:
// object "Test" {
// code {
// let dad := 0x0000000000000000000000000000000000000dad
// if eq(balance(dad), 0) {
// revert(0, 0)
// }
// let slot := sload(0)
// mstore(0, slot)
// return(0, 32)
// }
// }
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
},
overrides: StateOverride{
dad: OverrideAccount{
State: map[common.Hash]common.Hash{},
},
},
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
},
// Clear the entire storage set
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[1].addr,
// Yul:
// object "Test" {
// code {
// let dad := 0x0000000000000000000000000000000000000dad
// if eq(balance(dad), 0) {
// revert(0, 0)
// }
// let slot := sload(0)
// mstore(0, slot)
// return(0, 32)
// }
// }
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
},
overrides: StateOverride{
dad: OverrideAccount{
State: map[common.Hash]common.Hash{},
},
},
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
},
}
for _, tc := range testSuite {
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
if tc.expectErr != nil {
if err == nil {
t.Errorf("test %s: want error %v, have nothing", tc.name, tc.expectErr)
continue
}
if !errors.Is(err, tc.expectErr) {
// Second try
if !reflect.DeepEqual(err, tc.expectErr) {
t.Errorf("test %s: error mismatch, want %v, have %v", tc.name, tc.expectErr, err)
}
}
continue
}
if err != nil {
t.Errorf("test %s: want no error, have %v", tc.name, err)
continue
}
if !reflect.DeepEqual(result.String(), tc.want) {
t.Errorf("test %s, result mismatch, have\n%v\n, want\n%v\n", tc.name, result.String(), tc.want)
}
}
}
func TestSimulateV1(t *testing.T) {
t.Parallel()
// Initialize test accounts
var (
accounts = newAccounts(3)
fixedAccount = newTestAccount()
genBlocks = 10
signer = types.HomesteadSigner{}
cac = common.HexToAddress("0x0000000000000000000000000000000000000cac")
bab = common.HexToAddress("0x0000000000000000000000000000000000000bab")
coinbase = "0x000000000000000000000000000000000000ffff"
genesis = &core.Genesis{
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
// Yul:
// object "Test" {
// code {
// let dad := 0x0000000000000000000000000000000000000dad
// selfdestruct(dad)
// }
// }
cac: {Balance: big.NewInt(params.Ether), Code: common.Hex2Bytes("610dad80ff")},
bab: {
Balance: big.NewInt(1),
// object "Test" {
// code {
// let value1 := sload(1)
// let value2 := sload(2)
//
// // Shift value1 by 128 bits to the left by multiplying it with 2^128
// value1 := mul(value1, 0x100000000000000000000000000000000)
//
// // Concatenate value1 and value2
// let concatenatedValue := add(value1, value2)
//
// // Store the result in memory and return it
// mstore(0, concatenatedValue)
// return(0, 0x20)
// }
// }
Code: common.FromHex("0x600154600254700100000000000000000000000000000000820291508082018060005260206000f3"),
Storage: map[common.Hash]common.Hash{
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(10)),
common.BigToHash(big.NewInt(2)): common.BigToHash(big.NewInt(12)),
},
},
},
}
sha256Address = common.BytesToAddress([]byte{0x02})
)
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) {
b.SetCoinbase(common.HexToAddress(coinbase))
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: uint64(i),
To: &accounts[1].addr,
Value: big.NewInt(1000),
Gas: params.TxGas,
GasPrice: b.BaseFee(),
Data: nil,
}), signer, accounts[0].key)
b.AddTx(tx)
}))
var (
randomAccounts = newAccounts(4)
latest = rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
includeTransfers = true
validation = true
)
type log struct {
Address common.Address `json:"address"`
Topics []common.Hash `json:"topics"`
Data hexutil.Bytes `json:"data"`
BlockNumber hexutil.Uint64 `json:"blockNumber"`
// Skip txHash
//TxHash common.Hash `json:"transactionHash" gencodec:"required"`
TxIndex hexutil.Uint `json:"transactionIndex"`
//BlockHash common.Hash `json:"blockHash"`
Index hexutil.Uint `json:"logIndex"`
}
type callErr struct {
Message string
Code int
}
type callRes struct {
ReturnValue string `json:"returnData"`
Error callErr
Logs []log
GasUsed string
Status string
}
type blockRes struct {
Number string
//Hash string
// Ignore timestamp
GasLimit string
GasUsed string
Miner string
BaseFeePerGas string
Calls []callRes
}
var testSuite = []struct {
name string
blocks []simBlock
tag rpc.BlockNumberOrHash
includeTransfers *bool
validation *bool
expectErr error
want []blockRes
}{
// State build-up over calls:
// First value transfer OK after state override.
// Second one should succeed because of first transfer.
{
name: "simple",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
}, {
From: &randomAccounts[1].addr,
To: &randomAccounts[2].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
}, {
To: &randomAccounts[3].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xf618",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[0].addr.Bytes()),
common.BytesToHash(randomAccounts[1].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8},
BlockNumber: 11,
TxIndex: 0,
Index: 0,
},
},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[1].addr.Bytes()),
common.BytesToHash(randomAccounts[2].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8},
BlockNumber: 11,
TxIndex: 1,
Index: 1,
},
},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{},
Status: "0x1",
}},
}},
}, {
// State build-up over blocks.
name: "simple-multi-block",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))},
},
Calls: []TransactionArgs{
{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
}, {
From: &randomAccounts[0].addr,
To: &randomAccounts[3].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
},
}, {
StateOverrides: &StateOverride{
randomAccounts[3].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))},
},
Calls: []TransactionArgs{
{
From: &randomAccounts[1].addr,
To: &randomAccounts[2].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xa410",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[0].addr.Bytes()),
common.BytesToHash(randomAccounts[1].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8},
BlockNumber: 11,
TxIndex: 0,
Index: 0,
},
},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[0].addr.Bytes()),
common.BytesToHash(randomAccounts[3].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8},
BlockNumber: 11,
TxIndex: 1,
Index: 1,
},
},
Status: "0x1",
}},
}, {
Number: "0xc",
GasLimit: "0x47e7c4",
GasUsed: "0x5208",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[1].addr.Bytes()),
common.BytesToHash(randomAccounts[2].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8},
BlockNumber: 12,
TxIndex: 0,
Index: 0,
},
},
Status: "0x1",
}},
}},
}, {
// insufficient funds
name: "insufficient-funds",
tag: latest,
blocks: []simBlock{{
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
}},
}},
want: nil,
expectErr: &invalidTxError{Message: fmt.Sprintf("err: insufficient funds for gas * price + value: address %s have 0 want 1000 (supplied gas 4712388)", randomAccounts[0].addr.String()), Code: errCodeInsufficientFunds},
}, {
// EVM error
name: "evm-error",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{Code: hex2Bytes("f3")},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x47e7c4",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
Error: callErr{Message: "stack underflow (0 <=> 2)", Code: errCodeVMError},
GasUsed: "0x47e7c4",
Logs: []log{},
Status: "0x0",
}},
}},
}, {
// Block overrides should work, each call is simulated on a different block number
name: "block-overrides",
tag: latest,
blocks: []simBlock{{
BlockOverrides: &BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(11)),
FeeRecipient: &cac,
},
Calls: []TransactionArgs{
{
From: &accounts[0].addr,
Input: &hexutil.Bytes{
0x43, // NUMBER
0x60, 0x00, 0x52, // MSTORE offset 0
0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN
},
},
},
}, {
BlockOverrides: &BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(12)),
},
Calls: []TransactionArgs{{
From: &accounts[1].addr,
Input: &hexutil.Bytes{
0x43, // NUMBER
0x60, 0x00, 0x52, // MSTORE offset 0
0x60, 0x20, 0x60, 0x00, 0xf3,
},
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xe891",
Miner: strings.ToLower(cac.String()),
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000b",
GasUsed: "0xe891",
Logs: []log{},
Status: "0x1",
}},
}, {
Number: "0xc",
GasLimit: "0x47e7c4",
GasUsed: "0xe891",
Miner: strings.ToLower(cac.String()),
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x000000000000000000000000000000000000000000000000000000000000000c",
GasUsed: "0xe891",
Logs: []log{},
Status: "0x1",
}},
}},
},
// Block numbers must be in order.
{
name: "block-number-order",
tag: latest,
blocks: []simBlock{{
BlockOverrides: &BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(12)),
},
Calls: []TransactionArgs{{
From: &accounts[1].addr,
Input: &hexutil.Bytes{
0x43, // NUMBER
0x60, 0x00, 0x52, // MSTORE offset 0
0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN
},
}},
}, {
BlockOverrides: &BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(11)),
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
Input: &hexutil.Bytes{
0x43, // NUMBER
0x60, 0x00, 0x52, // MSTORE offset 0
0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN
},
}},
}},
want: []blockRes{},
expectErr: &invalidBlockNumberError{message: "block numbers must be in order: 11 <= 12"},
},
// Test on solidity storage example. Set value in one call, read in next.
{
name: "storage-contract",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{
Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"),
},
},
Calls: []TransactionArgs{{
// Set value to 5
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
Input: hex2Bytes("6057361d0000000000000000000000000000000000000000000000000000000000000005"),
}, {
// Read value
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
Input: hex2Bytes("2e64cec1"),
},
},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x10683",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0xaacc",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x0000000000000000000000000000000000000000000000000000000000000005",
GasUsed: "0x5bb7",
Logs: []log{},
Status: "0x1",
}},
}},
},
// Test logs output.
{
name: "logs",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{
// Yul code:
// object "Test" {
// code {
// let hash:u256 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
// log1(0, 0, hash)
// return (0, 0)
// }
// }
Code: hex2Bytes("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80600080a1600080f3"),
},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x5508",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
Logs: []log{{
Address: randomAccounts[2].addr,
Topics: []common.Hash{common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")},
BlockNumber: hexutil.Uint64(11),
Data: hexutil.Bytes{},
}},
GasUsed: "0x5508",
Status: "0x1",
}},
}},
},
// Test ecrecover override
{
name: "ecrecover-override",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{
// Yul code that returns ecrecover(0, 0, 0, 0).
// object "Test" {
// code {
// // Free memory pointer
// let free_ptr := mload(0x40)
//
// // Initialize inputs with zeros
// mstore(free_ptr, 0) // Hash
// mstore(add(free_ptr, 0x20), 0) // v
// mstore(add(free_ptr, 0x40), 0) // r
// mstore(add(free_ptr, 0x60), 0) // s
//
// // Call ecrecover precompile (at address 1) with all 0 inputs
// let success := staticcall(gas(), 1, free_ptr, 0x80, free_ptr, 0x20)
//
// // Check if the call was successful
// if eq(success, 0) {
// revert(0, 0)
// }
//
// // Return the recovered address
// return(free_ptr, 0x14)
// }
// }
Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"),
},
common.BytesToAddress([]byte{0x01}): OverrideAccount{
// Yul code that returns the address of the caller.
// object "Test" {
// code {
// let c := caller()
// mstore(0, c)
// return(0xc, 0x14)
// }
// }
Code: hex2Bytes("33806000526014600cf3"),
},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x52f6",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
// Caller is in this case the contract that invokes ecrecover.
ReturnValue: strings.ToLower(randomAccounts[2].addr.String()),
GasUsed: "0x52f6",
Logs: []log{},
Status: "0x1",
}},
}},
},
// Test moving the sha256 precompile.
{
name: "precompile-move",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
sha256Address: OverrideAccount{
// Yul code that returns the calldata.
// object "Test" {
// code {
// let size := calldatasize() // Get the size of the calldata
//
// // Allocate memory to store the calldata
// let memPtr := msize()
//
// // Copy calldata to memory
// calldatacopy(memPtr, 0, size)
//
// // Return the calldata from memory
// return(memPtr, size)
// }
// }
Code: hex2Bytes("365981600082378181f3"),
MovePrecompileTo: &randomAccounts[2].addr,
},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
Input: hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
}, {
From: &randomAccounts[0].addr,
To: &sha256Address,
Input: hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xa58c",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0xec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5",
GasUsed: "0x52dc",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x0000000000000000000000000000000000000000000000000000000000000001",
GasUsed: "0x52b0",
Logs: []log{},
Status: "0x1",
}},
}},
},
// Test ether transfers.
{
name: "transfer-logs",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[0].addr: OverrideAccount{
Balance: newRPCBalance(big.NewInt(100)),
// Yul code that transfers 100 wei to address passed in calldata:
// object "Test" {
// code {
// let recipient := shr(96, calldataload(0))
// let value := 100
// let success := call(gas(), recipient, value, 0, 0, 0, 0)
// if eq(success, 0) {
// revert(0, 0)
// }
// }
// }
Code: hex2Bytes("60003560601c606460008060008084865af160008103601d57600080fd5b505050"),
},
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[0].addr,
Value: (*hexutil.Big)(big.NewInt(50)),
Input: hex2Bytes(strings.TrimPrefix(fixedAccount.addr.String(), "0x")),
}},
}},
includeTransfers: &includeTransfers,
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xd984",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0xd984",
Logs: []log{
{
Address: transferAddress,
Topics: []common.Hash{
transferTopic,
addressToHash(accounts[0].addr),
addressToHash(randomAccounts[0].addr),
},
Data: hexutil.Bytes(common.BigToHash(big.NewInt(50)).Bytes()),
BlockNumber: hexutil.Uint64(11),
},
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(accounts[0].addr.Bytes()),
common.BytesToHash(randomAccounts[0].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x77, 0x0d, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x77, 0x0d, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96},
BlockNumber: 11,
TxIndex: 0,
Index: 1,
},
{
Address: transferAddress,
Topics: []common.Hash{
transferTopic,
addressToHash(randomAccounts[0].addr),
addressToHash(fixedAccount.addr),
},
Data: hexutil.Bytes(common.BigToHash(big.NewInt(100)).Bytes()),
BlockNumber: hexutil.Uint64(11),
Index: hexutil.Uint(2),
},
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[0].addr.Bytes()),
common.BytesToHash(fixedAccount.addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64},
BlockNumber: 11,
TxIndex: 0,
Index: 3,
},
},
Status: "0x1",
}},
}},
},
// Tests selfdestructed contract.
{
name: "selfdestruct",
tag: latest,
blocks: []simBlock{{
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &cac,
}, {
From: &accounts[0].addr,
// Check that cac is selfdestructed and balance transferred to dad.
// object "Test" {
// code {
// let cac := 0x0000000000000000000000000000000000000cac
// let dad := 0x0000000000000000000000000000000000000dad
// if gt(balance(cac), 0) {
// revert(0, 0)
// }
// if gt(extcodesize(cac), 0) {
// revert(0, 0)
// }
// if eq(balance(dad), 0) {
// revert(0, 0)
// }
// }
// }
Input: hex2Bytes("610cac610dad600082311115601357600080fd5b6000823b1115602157600080fd5b6000813103602e57600080fd5b5050"),
}},
}, {
Calls: []TransactionArgs{{
From: &accounts[0].addr,
Input: hex2Bytes("610cac610dad600082311115601357600080fd5b6000823b1115602157600080fd5b6000813103602e57600080fd5b5050"),
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x1b83f",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0xd166",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0xe6d9",
Logs: []log{},
Status: "0x1",
}},
}, {
Number: "0xc",
GasLimit: "0x47e7c4",
GasUsed: "0xe6d9",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0xe6d9",
Logs: []log{},
Status: "0x1",
}},
}},
},
// Enable validation checks.
{
name: "validation-checks",
tag: latest,
blocks: []simBlock{{
Calls: []TransactionArgs{{
From: &accounts[2].addr,
To: &cac,
Nonce: newUint64(2),
}},
}},
validation: &validation,
want: nil,
expectErr: &invalidTxError{Message: fmt.Sprintf("err: nonce too high: address %s, tx: 2 state: 0 (supplied gas 4712388)", accounts[2].addr), Code: errCodeNonceTooHigh},
},
// Contract sends tx in validation mode.
{
name: "validation-checks-from-contract",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: OverrideAccount{
Balance: newRPCBalance(big.NewInt(2098640803896784)),
Code: hex2Bytes("00"),
Nonce: newUint64(1),
},
},
Calls: []TransactionArgs{{
From: &randomAccounts[2].addr,
To: &cac,
Nonce: newUint64(1),
MaxFeePerGas: newInt(233138868),
MaxPriorityFeePerGas: newInt(1),
}},
}},
validation: &validation,
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xd166",
Miner: coinbase,
BaseFeePerGas: "0xde56ab3",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0xd166",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{0x4d, 0xfe, 0x1b, 0xbb, 0xcf, 0x07, 0x7d, 0xdc, 0x3e, 0x01, 0x29, 0x1e, 0xea, 0x2d, 0x5c, 0x70, 0xc2, 0xb4, 0x22, 0xb4, 0x15, 0xd9, 0x56, 0x45, 0xb9, 0xad, 0xcf, 0xd6, 0x78, 0xcb, 0x1d, 0x63}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[2].addr.Bytes()),
common.BytesToHash(common.HexToAddress("0x000000000000000000000000000000000000ffff").Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x74, 0xb3, 0xe3, 0xa0, 0x9d, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x74, 0xb3, 0xe3, 0x9f, 0xcc, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0xd1, 0x66},
BlockNumber: 11,
TxIndex: 0,
Index: 0,
},
},
Status: "0x1",
}},
}},
},
// Successful validation
{
name: "validation-checks-success",
tag: latest,
blocks: []simBlock{{
BlockOverrides: &BlockOverrides{
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
},
StateOverrides: &StateOverride{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))},
},
Calls: []TransactionArgs{{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
MaxFeePerGas: (*hexutil.Big)(big.NewInt(2)),
}},
}},
validation: &validation,
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x5208",
Miner: coinbase,
BaseFeePerGas: "0x1",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x5208",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{
230, 73, 126, 62, 229, 72, 163, 55, 33, 54, 175, 47, 203, 6, 150, 219,
49, 252, 108, 242, 2, 96, 112, 118, 69, 6, 139, 211, 254, 151, 243, 196,
}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(randomAccounts[0].addr.Bytes()),
common.BytesToHash(randomAccounts[1].addr.Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xae, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xaa, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8},
BlockNumber: 11,
TxIndex: 0,
Index: 0,
},
},
Status: "0x1",
}},
}},
},
// Clear storage.
{
name: "clear-storage",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: {
Code: newBytes(genesis.Alloc[bab].Code),
StateDiff: map[common.Hash]common.Hash{
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(2)),
common.BigToHash(big.NewInt(2)): common.BigToHash(big.NewInt(3)),
},
},
bab: {
State: map[common.Hash]common.Hash{
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(1)),
},
},
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
}, {
From: &accounts[0].addr,
To: &bab,
}},
}, {
StateOverrides: &StateOverride{
randomAccounts[2].addr: {
State: map[common.Hash]common.Hash{
common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(5)),
},
},
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xc542",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x0000000000000000000000000000000200000000000000000000000000000003",
GasUsed: "0x62a1",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x0000000000000000000000000000000100000000000000000000000000000000",
GasUsed: "0x62a1",
Logs: []log{},
Status: "0x1",
}},
}, {
Number: "0xc",
GasLimit: "0x47e7c4",
GasUsed: "0x62a1",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x0000000000000000000000000000000500000000000000000000000000000000",
GasUsed: "0x62a1",
Logs: []log{},
Status: "0x1",
}},
}},
},
{
name: "blockhash-opcode",
tag: latest,
blocks: []simBlock{{
BlockOverrides: &BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(12)),
},
StateOverrides: &StateOverride{
randomAccounts[2].addr: {
Code: hex2Bytes("600035804060008103601057600080fd5b5050"),
},
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// Phantom block after base.
Input: uint256ToBytes(uint256.NewInt(11)),
}, {
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// Canonical block.
Input: uint256ToBytes(uint256.NewInt(8)),
}, {
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// base block.
Input: uint256ToBytes(uint256.NewInt(10)),
}},
}, {
BlockOverrides: &BlockOverrides{
Number: (*hexutil.Big)(big.NewInt(16)),
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// blocks[0]
Input: uint256ToBytes(uint256.NewInt(12)),
}, {
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// Phantom after blocks[0]
Input: uint256ToBytes(uint256.NewInt(13)),
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x0",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{},
}, {
Number: "0xc",
GasLimit: "0x47e7c4",
GasUsed: "0xf864",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x52cc",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0x52cc",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0x52cc",
Logs: []log{},
Status: "0x1",
}},
}, {
Number: "0xd",
GasLimit: "0x47e7c4",
GasUsed: "0x0",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{},
}, {
Number: "0xe",
GasLimit: "0x47e7c4",
GasUsed: "0x0",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{},
}, {
Number: "0xf",
GasLimit: "0x47e7c4",
GasUsed: "0x0",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{},
}, {
Number: "0x10",
GasLimit: "0x47e7c4",
GasUsed: "0xa598",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x",
GasUsed: "0x52cc",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x",
GasUsed: "0x52cc",
Logs: []log{},
Status: "0x1",
}},
}},
},
{
name: "basefee-non-validation",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: {
// Yul code:
// object "Test" {
// code {
// // Get the gas price from the transaction
// let gasPrice := gasprice()
//
// // Get the base fee from the block
// let baseFee := basefee()
//
// // Store gasPrice and baseFee in memory
// mstore(0x0, gasPrice)
// mstore(0x20, baseFee)
//
// // Return the data
// return(0x0, 0x40)
// }
// }
Code: hex2Bytes("3a489060005260205260406000f3"),
},
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// 0 gas price
}, {
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// non-zero gas price
MaxPriorityFeePerGas: newInt(1),
MaxFeePerGas: newInt(2),
},
},
}, {
BlockOverrides: &BlockOverrides{
BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)),
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// 0 gas price
}, {
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
// non-zero gas price
MaxPriorityFeePerGas: newInt(1),
MaxFeePerGas: newInt(2),
},
},
}, {
// Base fee should be 0 to zero even if it was set in previous block.
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
}},
}},
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0xa44e",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
GasUsed: "0x5227",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000",
GasUsed: "0x5227",
Logs: []log{{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{0x4d, 0xfe, 0x1b, 0xbb, 0xcf, 0x07, 0x7d, 0xdc, 0x3e, 0x01, 0x29, 0x1e, 0xea, 0x2d, 0x5c, 0x70, 0xc2, 0xb4, 0x22, 0xb4, 0x15, 0xd9, 0x56, 0x45, 0xb9, 0xad, 0xcf, 0xd6, 0x78, 0xcb, 0x1d, 0x63}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(accounts[0].addr.Bytes()),
common.BytesToHash(common.HexToAddress("0x000000000000000000000000000000000000ffff").Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x77, 0x0d, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x76, 0xbb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x52, 0x27},
BlockNumber: 11,
TxIndex: 1,
Index: 0,
},
},
Status: "0x1",
}},
}, {
Number: "0xc",
GasLimit: "0x47e7c4",
GasUsed: "0xa44e",
Miner: coinbase,
BaseFeePerGas: "0x1",
Calls: []callRes{{
ReturnValue: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
GasUsed: "0x5227",
Logs: []log{},
Status: "0x1",
}, {
ReturnValue: "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001",
GasUsed: "0x5227",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{0x4d, 0xfe, 0x1b, 0xbb, 0xcf, 0x07, 0x7d, 0xdc, 0x3e, 0x01, 0x29, 0x1e, 0xea, 0x2d, 0x5c, 0x70, 0xc2, 0xb4, 0x22, 0xb4, 0x15, 0xd9, 0x56, 0x45, 0xb9, 0xad, 0xcf, 0xd6, 0x78, 0xcb, 0x1d, 0x63}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(accounts[0].addr.Bytes()),
common.BytesToHash(common.HexToAddress("0x000000000000000000000000000000000000ffff").Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x76, 0xbb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0xa4, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x76, 0x69, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0xf6, 0x75},
BlockNumber: 12,
TxIndex: 1,
Index: 0,
},
},
Status: "0x1",
}},
}, {
Number: "0xd",
GasLimit: "0x47e7c4",
GasUsed: "0x5227",
Miner: coinbase,
BaseFeePerGas: "0x0",
Calls: []callRes{{
ReturnValue: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
GasUsed: "0x5227",
Logs: []log{},
Status: "0x1",
}},
}},
}, {
name: "basefee-validation-mode",
tag: latest,
blocks: []simBlock{{
StateOverrides: &StateOverride{
randomAccounts[2].addr: {
// Yul code:
// object "Test" {
// code {
// // Get the gas price from the transaction
// let gasPrice := gasprice()
//
// // Get the base fee from the block
// let baseFee := basefee()
//
// // Store gasPrice and baseFee in memory
// mstore(0x0, gasPrice)
// mstore(0x20, baseFee)
//
// // Return the data
// return(0x0, 0x40)
// }
// }
Code: hex2Bytes("3a489060005260205260406000f3"),
},
},
Calls: []TransactionArgs{{
From: &accounts[0].addr,
To: &randomAccounts[2].addr,
MaxFeePerGas: newInt(233138868),
MaxPriorityFeePerGas: newInt(1),
}},
}},
validation: &validation,
want: []blockRes{{
Number: "0xb",
GasLimit: "0x47e7c4",
GasUsed: "0x5227",
Miner: coinbase,
BaseFeePerGas: "0xde56ab3",
Calls: []callRes{{
ReturnValue: "0x000000000000000000000000000000000000000000000000000000000de56ab4000000000000000000000000000000000000000000000000000000000de56ab3",
GasUsed: "0x5227",
Logs: []log{
{
Address: core.GetFeeAddress(),
Topics: []common.Hash{
common.Hash([32]byte{0x4d, 0xfe, 0x1b, 0xbb, 0xcf, 0x07, 0x7d, 0xdc, 0x3e, 0x01, 0x29, 0x1e, 0xea, 0x2d, 0x5c, 0x70, 0xc2, 0xb4, 0x22, 0xb4, 0x15, 0xd9, 0x56, 0x45, 0xb9, 0xad, 0xcf, 0xd6, 0x78, 0xcb, 0x1d, 0x63}),
common.BytesToHash(core.GetFeeAddress().Bytes()),
common.BytesToHash(accounts[0].addr.Bytes()),
common.BytesToHash(common.HexToAddress("0x000000000000000000000000000000000000ffff").Bytes()),
},
Data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x77, 0x0d, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0x53, 0xbf, 0x1f, 0x76, 0xbb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x52, 0x27},
BlockNumber: 11,
TxIndex: 0,
Index: 0,
},
},
Status: "0x1",
}},
}},
},
}
for _, tc := range testSuite {
t.Run(tc.name, func(t *testing.T) {
opts := simOpts{BlockStateCalls: tc.blocks}
if tc.includeTransfers != nil && *tc.includeTransfers {
opts.TraceTransfers = true
}
if tc.validation != nil && *tc.validation {
opts.Validation = true
}
result, err := api.SimulateV1(context.Background(), opts, &tc.tag)
if tc.expectErr != nil {
if err == nil {
t.Fatalf("test %s: want error %v, have nothing", tc.name, tc.expectErr)
}
if !errors.Is(err, tc.expectErr) {
// Second try
if !reflect.DeepEqual(err, tc.expectErr) {
t.Errorf("test %s: error mismatch, want %v, have %v", tc.name, tc.expectErr, err)
}
}
return
}
if err != nil {
t.Fatalf("test %s: want no error, have %v", tc.name, err)
}
// Turn result into res-struct
var have []blockRes
resBytes, _ := json.Marshal(result)
if err := json.Unmarshal(resBytes, &have); err != nil {
t.Fatalf("failed to unmarshal result: %v", err)
}
if !reflect.DeepEqual(have, tc.want) {
t.Log(string(resBytes))
t.Errorf("test %s, result mismatch, have\n%v\n, want\n%v\n", tc.name, have, tc.want)
}
})
}
}
func TestSignTransaction(t *testing.T) {
t.Parallel()
// Initialize test accounts
var (
key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
to = crypto.PubkeyToAddress(key.PublicKey)
genesis = &core.Genesis{
Config: params.MergedTestChainConfig,
Alloc: types.GenesisAlloc{},
}
)
_, acc := newTestAccountManager(t)
genesis.Alloc[acc.Address] = types.Account{Balance: big.NewInt(params.Ether)}
b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
b.SetPoS()
})
api := NewTransactionAPI(b, nil)
res, err := api.FillTransaction(context.Background(), TransactionArgs{
From: &b.acc.Address,
To: &to,
Value: (*hexutil.Big)(big.NewInt(1)),
})
if err != nil {
t.Fatalf("failed to fill tx defaults: %v\n", err)
}
res, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
if err != nil {
t.Fatalf("failed to sign tx: %v\n", err)
}
tx, err := json.Marshal(res.Tx)
if err != nil {
t.Fatal(err)
}
expect := `{"type":"0x2","chainId":"0x1","nonce":"0x0","to":"0x703c4b2bd70c169f5717101caee543299fc946c7","gas":"0x5208","gasPrice":null,"maxPriorityFeePerGas":"0x0","maxFeePerGas":"0x684ee180","value":"0x1","input":"0x","accessList":[],"v":"0x0","r":"0x8fabeb142d585dd9247f459f7e6fe77e2520c88d50ba5d220da1533cea8b34e1","s":"0x582dd68b21aef36ba23f34e49607329c20d981d30404daf749077f5606785ce7","yParity":"0x0","hash":"0x93927839207cfbec395da84b8a2bc38b7b65d2cb2819e9fef1f091f5b1d4cc8f"}`
if !bytes.Equal(tx, []byte(expect)) {
t.Errorf("result mismatch. Have:\n%s\nWant:\n%s\n", tx, expect)
}
}
// func TestSignBlobTransaction(t *testing.T) {
// t.Parallel()
// // Initialize test accounts
// var (
// key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
// to = crypto.PubkeyToAddress(key.PublicKey)
// genesis = &core.Genesis{
// Config: params.MergedTestChainConfig,
// Alloc: types.GenesisAlloc{},
// }
// )
// b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// b.SetPoS()
// })
// api := NewTransactionAPI(b, nil)
// res, err := api.FillTransaction(context.Background(), TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// BlobHashes: []common.Hash{{0x01, 0x22}},
// })
// if err != nil {
// t.Fatalf("failed to fill tx defaults: %v\n", err)
// }
// _, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
// if err != nil {
// t.Fatalf("should not fail on blob transaction")
// }
// }
// func TestSendBlobTransaction(t *testing.T) {
// t.Parallel()
// // Initialize test accounts
// var (
// key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
// to = crypto.PubkeyToAddress(key.PublicKey)
// genesis = &core.Genesis{
// Config: params.MergedTestChainConfig,
// Alloc: types.GenesisAlloc{},
// }
// )
// b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// b.SetPoS()
// })
// api := NewTransactionAPI(b, nil)
// res, err := api.FillTransaction(context.Background(), TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// BlobHashes: []common.Hash{{0x01, 0x22}},
// })
// if err != nil {
// t.Fatalf("failed to fill tx defaults: %v\n", err)
// }
// _, err = api.SendTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
// if err == nil {
// t.Errorf("sending tx should have failed")
// } else if !errors.Is(err, errBlobTxNotSupported) {
// t.Errorf("unexpected error. Have %v, want %v\n", err, errBlobTxNotSupported)
// }
// }
// func TestFillBlobTransaction(t *testing.T) {
// t.Parallel()
// // Initialize test accounts
// var (
// key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
// to = crypto.PubkeyToAddress(key.PublicKey)
// genesis = &core.Genesis{
// Config: params.MergedTestChainConfig,
// Alloc: types.GenesisAlloc{},
// }
// emptyBlob = new(kzg4844.Blob)
// emptyBlobs = []kzg4844.Blob{*emptyBlob}
// emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
// emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
// emptyBlobHash common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
// )
// b := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// b.SetPoS()
// })
// api := NewTransactionAPI(b, nil)
// type result struct {
// Hashes []common.Hash
// Sidecar *types.BlobTxSidecar
// }
// suite := []struct {
// name string
// args TransactionArgs
// err string
// want *result
// }{
// {
// name: "TestInvalidParamsCombination1",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: []kzg4844.Blob{{}},
// Proofs: []kzg4844.Proof{{}},
// },
// err: `blob proofs provided while commitments were not`,
// },
// {
// name: "TestInvalidParamsCombination2",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: []kzg4844.Blob{{}},
// Commitments: []kzg4844.Commitment{{}},
// },
// err: `blob commitments provided while proofs were not`,
// },
// {
// name: "TestInvalidParamsCount1",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: []kzg4844.Blob{{}},
// Commitments: []kzg4844.Commitment{{}, {}},
// Proofs: []kzg4844.Proof{{}, {}},
// },
// err: `number of blobs and commitments mismatch (have=2, want=1)`,
// },
// {
// name: "TestInvalidParamsCount2",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: []kzg4844.Blob{{}, {}},
// Commitments: []kzg4844.Commitment{{}, {}},
// Proofs: []kzg4844.Proof{{}},
// },
// err: `number of blobs and proofs mismatch (have=1, want=2)`,
// },
// {
// name: "TestInvalidProofVerification",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: []kzg4844.Blob{{}, {}},
// Commitments: []kzg4844.Commitment{{}, {}},
// Proofs: []kzg4844.Proof{{}, {}},
// },
// err: `failed to verify blob proof: short buffer`,
// },
// {
// name: "TestGenerateBlobHashes",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: emptyBlobs,
// Commitments: []kzg4844.Commitment{emptyBlobCommit},
// Proofs: []kzg4844.Proof{emptyBlobProof},
// },
// want: &result{
// Hashes: []common.Hash{emptyBlobHash},
// Sidecar: &types.BlobTxSidecar{
// Blobs: emptyBlobs,
// Commitments: []kzg4844.Commitment{emptyBlobCommit},
// Proofs: []kzg4844.Proof{emptyBlobProof},
// },
// },
// },
// {
// name: "TestValidBlobHashes",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// BlobHashes: []common.Hash{emptyBlobHash},
// Blobs: emptyBlobs,
// Commitments: []kzg4844.Commitment{emptyBlobCommit},
// Proofs: []kzg4844.Proof{emptyBlobProof},
// },
// want: &result{
// Hashes: []common.Hash{emptyBlobHash},
// Sidecar: &types.BlobTxSidecar{
// Blobs: emptyBlobs,
// Commitments: []kzg4844.Commitment{emptyBlobCommit},
// Proofs: []kzg4844.Proof{emptyBlobProof},
// },
// },
// },
// {
// name: "TestInvalidBlobHashes",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// BlobHashes: []common.Hash{{0x01, 0x22}},
// Blobs: emptyBlobs,
// Commitments: []kzg4844.Commitment{emptyBlobCommit},
// Proofs: []kzg4844.Proof{emptyBlobProof},
// },
// err: fmt.Sprintf("blob hash verification failed (have=%s, want=%s)", common.Hash{0x01, 0x22}, emptyBlobHash),
// },
// {
// name: "TestGenerateBlobProofs",
// args: TransactionArgs{
// From: &b.acc.Address,
// To: &to,
// Value: (*hexutil.Big)(big.NewInt(1)),
// Blobs: emptyBlobs,
// },
// want: &result{
// Hashes: []common.Hash{emptyBlobHash},
// Sidecar: &types.BlobTxSidecar{
// Blobs: emptyBlobs,
// Commitments: []kzg4844.Commitment{emptyBlobCommit},
// Proofs: []kzg4844.Proof{emptyBlobProof},
// },
// },
// },
// }
// for _, tc := range suite {
// t.Run(tc.name, func(t *testing.T) {
// res, err := api.FillTransaction(context.Background(), tc.args)
// if len(tc.err) > 0 {
// if err == nil {
// t.Fatalf("missing error. want: %s", tc.err)
// } else if err.Error() != tc.err {
// t.Fatalf("error mismatch. want: %s, have: %s", tc.err, err.Error())
// }
// return
// }
// if err != nil && len(tc.err) == 0 {
// t.Fatalf("expected no error. have: %s", err)
// }
// if res == nil {
// t.Fatal("result missing")
// }
// want, err := json.Marshal(tc.want)
// if err != nil {
// t.Fatalf("failed to encode expected: %v", err)
// }
// have, err := json.Marshal(result{Hashes: res.Tx.BlobHashes(), Sidecar: res.Tx.BlobTxSidecar()})
// if err != nil {
// t.Fatalf("failed to encode computed sidecar: %v", err)
// }
// if !bytes.Equal(have, want) {
// t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want)
// }
// })
// }
// }
func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs {
var (
gas = tx.Gas()
nonce = tx.Nonce()
input = tx.Data()
accessList *types.AccessList
)
if acl := tx.AccessList(); acl != nil {
accessList = &acl
}
return TransactionArgs{
From: &from,
To: tx.To(),
Gas: (*hexutil.Uint64)(&gas),
MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()),
MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()),
Value: (*hexutil.Big)(tx.Value()),
Nonce: (*hexutil.Uint64)(&nonce),
Input: (*hexutil.Bytes)(&input),
ChainID: (*hexutil.Big)(tx.ChainId()),
AccessList: accessList,
BlobFeeCap: (*hexutil.Big)(tx.BlobGasFeeCap()),
BlobHashes: tx.BlobHashes(),
}
}
type account struct {
key *ecdsa.PrivateKey
addr common.Address
}
func newAccounts(n int) (accounts []account) {
for i := 0; i < n; i++ {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
accounts = append(accounts, account{key: key, addr: addr})
}
slices.SortFunc(accounts, func(a, b account) int { return a.addr.Cmp(b.addr) })
return accounts
}
func newTestAccount() account {
// testKey is a private key to use for funding a tester account.
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// testAddr is the Ethereum address of the tester account.
addr := crypto.PubkeyToAddress(key.PublicKey)
return account{key: key, addr: addr}
}
func newRPCBalance(balance *big.Int) *hexutil.Big {
rpcBalance := (*hexutil.Big)(balance)
return rpcBalance
}
func hex2Bytes(str string) *hexutil.Bytes {
rpcBytes := hexutil.Bytes(common.FromHex(str))
return &rpcBytes
}
func newUint64(v uint64) *hexutil.Uint64 {
rpcUint64 := hexutil.Uint64(v)
return &rpcUint64
}
func newBytes(b []byte) *hexutil.Bytes {
rpcBytes := hexutil.Bytes(b)
return &rpcBytes
}
func uint256ToBytes(v *uint256.Int) *hexutil.Bytes {
b := v.Bytes32()
r := hexutil.Bytes(b[:])
return &r
}
func TestRPCMarshalBlock(t *testing.T) {
t.Parallel()
var (
txs []*types.Transaction
to = common.BytesToAddress([]byte{0x11})
)
for i := uint64(1); i <= 4; i++ {
var tx *types.Transaction
if i%2 == 0 {
tx = types.NewTx(&types.LegacyTx{
Nonce: i,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &to,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
} else {
tx = types.NewTx(&types.AccessListTx{
ChainID: big.NewInt(1337),
Nonce: i,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &to,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
}
txs = append(txs, tx)
}
block := types.NewBlock(&types.Header{Number: big.NewInt(100)}, &types.Body{Transactions: txs}, nil, blocktest.NewHasher())
var testSuite = []struct {
inclTx bool
fullTx bool
want string
}{
// without txs
{
inclTx: false,
fullTx: false,
want: `{
"difficulty": "0x0",
"extraData": "0x",
"gasLimit": "0x0",
"gasUsed": "0x0",
"hash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x64",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x296",
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x0",
"transactionsRoot": "0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e",
"uncles": []
}`,
},
// only tx hashes
{
inclTx: true,
fullTx: false,
want: `{
"difficulty": "0x0",
"extraData": "0x",
"gasLimit": "0x0",
"gasUsed": "0x0",
"hash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x64",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x296",
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x0",
"transactions": [
"0x7d39df979e34172322c64983a9ad48302c2b889e55bda35324afecf043a77605",
"0x9bba4c34e57c875ff57ac8d172805a26ae912006985395dc1bdf8f44140a7bf4",
"0x98909ea1ff040da6be56bc4231d484de1414b3c1dac372d69293a4beb9032cb5",
"0x12e1f81207b40c3bdcc13c0ee18f5f86af6d31754d57a0ea1b0d4cfef21abef1"
],
"transactionsRoot": "0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e",
"uncles": []
}`,
},
// full tx details
{
inclTx: true,
fullTx: true,
want: `{
"difficulty": "0x0",
"extraData": "0x",
"gasLimit": "0x0",
"gasUsed": "0x0",
"hash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x64",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x296",
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x0",
"transactions": [
{
"blockHash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"blockNumber": "0x64",
"from": "0x0000000000000000000000000000000000000000",
"gas": "0x457",
"gasPrice": "0x2b67",
"hash": "0x7d39df979e34172322c64983a9ad48302c2b889e55bda35324afecf043a77605",
"input": "0x111111",
"nonce": "0x1",
"to": "0x0000000000000000000000000000000000000011",
"transactionIndex": "0x0",
"value": "0x6f",
"type": "0x1",
"accessList": [],
"chainId": "0x539",
"v": "0x0",
"r": "0x0",
"s": "0x0",
"yParity": "0x0"
},
{
"blockHash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"blockNumber": "0x64",
"from": "0x0000000000000000000000000000000000000000",
"gas": "0x457",
"gasPrice": "0x2b67",
"hash": "0x9bba4c34e57c875ff57ac8d172805a26ae912006985395dc1bdf8f44140a7bf4",
"input": "0x111111",
"nonce": "0x2",
"to": "0x0000000000000000000000000000000000000011",
"transactionIndex": "0x1",
"value": "0x6f",
"type": "0x0",
"chainId": "0x7fffffffffffffee",
"v": "0x0",
"r": "0x0",
"s": "0x0"
},
{
"blockHash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"blockNumber": "0x64",
"from": "0x0000000000000000000000000000000000000000",
"gas": "0x457",
"gasPrice": "0x2b67",
"hash": "0x98909ea1ff040da6be56bc4231d484de1414b3c1dac372d69293a4beb9032cb5",
"input": "0x111111",
"nonce": "0x3",
"to": "0x0000000000000000000000000000000000000011",
"transactionIndex": "0x2",
"value": "0x6f",
"type": "0x1",
"accessList": [],
"chainId": "0x539",
"v": "0x0",
"r": "0x0",
"s": "0x0",
"yParity": "0x0"
},
{
"blockHash": "0x9b73c83b25d0faf7eab854e3684c7e394336d6e135625aafa5c183f27baa8fee",
"blockNumber": "0x64",
"from": "0x0000000000000000000000000000000000000000",
"gas": "0x457",
"gasPrice": "0x2b67",
"hash": "0x12e1f81207b40c3bdcc13c0ee18f5f86af6d31754d57a0ea1b0d4cfef21abef1",
"input": "0x111111",
"nonce": "0x4",
"to": "0x0000000000000000000000000000000000000011",
"transactionIndex": "0x3",
"value": "0x6f",
"type": "0x0",
"chainId": "0x7fffffffffffffee",
"v": "0x0",
"r": "0x0",
"s": "0x0"
}
],
"transactionsRoot": "0x661a9febcfa8f1890af549b874faf9fa274aede26ef489d9db0b25daa569450e",
"uncles": []
}`,
},
}
for i, tc := range testSuite {
resp := RPCMarshalBlock(block, tc.inclTx, tc.fullTx, params.MainnetChainConfig, nil)
out, err := json.Marshal(resp)
if err != nil {
t.Errorf("test %d: json marshal error: %v", i, err)
continue
}
require.JSONEqf(t, tc.want, string(out), "test %d", i)
}
}
func TestRPCGetBlockOrHeader(t *testing.T) {
// Note: Upstream (geth) tests have a different genesis hash as it has a different
// state root hash due to allocating balance separately in test backend. Because
// that is commented out in bor, we use the old genesis hash in the test files.
t.Parallel()
// Initialize test accounts
var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
genesis = &core.Genesis{
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{
acc1Addr: {Balance: big.NewInt(params.Ether)},
acc2Addr: {Balance: big.NewInt(params.Ether)},
},
}
genBlocks = 10
signer = types.HomesteadSigner{}
tx = types.NewTx(&types.LegacyTx{
Nonce: 11,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &acc2Addr,
Value: big.NewInt(111),
Data: []byte{0x11, 0x11, 0x11},
})
withdrawal = &types.Withdrawal{
Index: 0,
Validator: 1,
Address: common.Address{0x12, 0x34},
Amount: 10,
}
pending = types.NewBlock(&types.Header{Number: big.NewInt(11), Time: 42}, &types.Body{Transactions: types.Transactions{tx}, Withdrawals: types.Withdrawals{withdrawal}}, nil, blocktest.NewHasher())
)
backend := newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &acc2Addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, acc1Key)
b.AddTx(tx)
})
backend.setPendingBlock(pending)
api := NewBlockChainAPI(backend)
blockHashes := make([]common.Hash, genBlocks+1)
ctx := context.Background()
for i := 0; i <= genBlocks; i++ {
header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
if err != nil {
t.Errorf("failed to get block: %d err: %v", i, err)
}
blockHashes[i] = header.Hash()
}
pendingHash := pending.Hash()
var testSuite = []struct {
blockNumber rpc.BlockNumber
blockHash *common.Hash
fullTx bool
reqHeader bool
file string
expectErr error
}{
// 0. latest header
{
blockNumber: rpc.LatestBlockNumber,
reqHeader: true,
file: "tag-latest",
},
// 1. genesis header
{
blockNumber: rpc.BlockNumber(0),
reqHeader: true,
file: "number-0",
},
// 2. #1 header
{
blockNumber: rpc.BlockNumber(1),
reqHeader: true,
file: "number-1",
},
// 3. latest-1 header
{
blockNumber: rpc.BlockNumber(9),
reqHeader: true,
file: "number-latest-1",
},
// 4. latest+1 header
{
blockNumber: rpc.BlockNumber(11),
reqHeader: true,
file: "number-latest+1",
},
// 5. pending header
{
blockNumber: rpc.PendingBlockNumber,
reqHeader: true,
file: "tag-pending",
},
// 6. latest block
{
blockNumber: rpc.LatestBlockNumber,
file: "tag-latest",
},
// 7. genesis block
{
blockNumber: rpc.BlockNumber(0),
file: "number-0",
},
// 8. #1 block
{
blockNumber: rpc.BlockNumber(1),
file: "number-1",
},
// 9. latest-1 block
{
blockNumber: rpc.BlockNumber(9),
fullTx: true,
file: "number-latest-1",
},
// 10. latest+1 block
{
blockNumber: rpc.BlockNumber(11),
fullTx: true,
file: "number-latest+1",
},
// 11. pending block
{
blockNumber: rpc.PendingBlockNumber,
file: "tag-pending",
},
// 12. pending block + fullTx
{
blockNumber: rpc.PendingBlockNumber,
fullTx: true,
file: "tag-pending-fullTx",
},
// 13. latest header by hash
{
blockHash: &blockHashes[len(blockHashes)-1],
reqHeader: true,
file: "hash-latest",
},
// 14. genesis header by hash
{
blockHash: &blockHashes[0],
reqHeader: true,
file: "hash-0",
},
// 15. #1 header
{
blockHash: &blockHashes[1],
reqHeader: true,
file: "hash-1",
},
// 16. latest-1 header
{
blockHash: &blockHashes[len(blockHashes)-2],
reqHeader: true,
file: "hash-latest-1",
},
// 17. empty hash
{
blockHash: &common.Hash{},
reqHeader: true,
file: "hash-empty",
},
// 18. pending hash
{
blockHash: &pendingHash,
reqHeader: true,
file: `hash-pending`,
},
// 19. latest block
{
blockHash: &blockHashes[len(blockHashes)-1],
file: "hash-latest",
},
// 20. genesis block
{
blockHash: &blockHashes[0],
file: "hash-genesis",
},
// 21. #1 block
{
blockHash: &blockHashes[1],
file: "hash-1",
},
// 22. latest-1 block
{
blockHash: &blockHashes[len(blockHashes)-2],
fullTx: true,
file: "hash-latest-1-fullTx",
},
// 23. empty hash + body
{
blockHash: &common.Hash{},
fullTx: true,
file: "hash-empty-fullTx",
},
// 24. pending block
{
blockHash: &pendingHash,
file: `hash-pending`,
},
// 25. pending block + fullTx
{
blockHash: &pendingHash,
fullTx: true,
file: "hash-pending-fullTx",
},
// 26. safe block
{
blockNumber: rpc.SafeBlockNumber,
file: "tag-safe",
},
}
for i, tt := range testSuite {
var (
result map[string]interface{}
err error
rpc string
)
if tt.blockHash != nil {
if tt.reqHeader {
result = api.GetHeaderByHash(context.Background(), *tt.blockHash)
rpc = "eth_getHeaderByHash"
} else {
result, err = api.GetBlockByHash(context.Background(), *tt.blockHash, tt.fullTx)
rpc = "eth_getBlockByHash"
}
} else {
if tt.reqHeader {
result, err = api.GetHeaderByNumber(context.Background(), tt.blockNumber)
rpc = "eth_getHeaderByNumber"
} else {
result, err = api.GetBlockByNumber(context.Background(), tt.blockNumber, tt.fullTx)
rpc = "eth_getBlockByNumber"
}
}
if tt.expectErr != nil {
if err == nil {
t.Errorf("test %d: want error %v, have nothing", i, tt.expectErr)
continue
}
if !errors.Is(err, tt.expectErr) {
t.Errorf("test %d: error mismatch, want %v, have %v", i, tt.expectErr, err)
}
continue
}
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
testRPCResponseWithFile(t, i, result, rpc, tt.file)
}
}
func setupTransactionsToApiTest(t *testing.T) (*TransactionAPI, []common.Hash, []struct {
txHash common.Hash
file string
}) {
config := *params.TestChainConfig
genBlocks := 5
config.ShanghaiBlock = big.NewInt(0)
config.CancunBlock = big.NewInt(0)
var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
contract = common.HexToAddress("0000000000000000000000000000000000031ec7")
genesis = &core.Genesis{
Config: &config,
ExcessBlobGas: new(uint64),
BlobGasUsed: new(uint64),
Alloc: types.GenesisAlloc{
acc1Addr: {Balance: big.NewInt(params.Ether)},
acc2Addr: {Balance: big.NewInt(params.Ether)},
// // SPDX-License-Identifier: GPL-3.0
// pragma solidity >=0.7.0 <0.9.0;
//
// contract Token {
// event Transfer(address indexed from, address indexed to, uint256 value);
// function transfer(address to, uint256 value) public returns (bool) {
// emit Transfer(msg.sender, to, value);
// return true;
// }
// }
contract: {Balance: big.NewInt(params.Ether), Code: common.FromHex("0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a9059cbb14610030575b600080fd5b61004a6004803603810190610045919061016a565b610060565b60405161005791906101c5565b60405180910390f35b60008273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516100bf91906101ef565b60405180910390a36001905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610101826100d6565b9050919050565b610111816100f6565b811461011c57600080fd5b50565b60008135905061012e81610108565b92915050565b6000819050919050565b61014781610134565b811461015257600080fd5b50565b6000813590506101648161013e565b92915050565b60008060408385031215610181576101806100d1565b5b600061018f8582860161011f565b92505060206101a085828601610155565b9150509250929050565b60008115159050919050565b6101bf816101aa565b82525050565b60006020820190506101da60008301846101b6565b92915050565b6101e981610134565b82525050565b600060208201905061020460008301846101e0565b9291505056fea2646970667358221220b469033f4b77b9565ee84e0a2f04d496b18160d26034d54f9487e57788fd36d564736f6c63430008120033")},
},
}
signer = types.LatestSignerForChainID(params.TestChainConfig.ChainID)
txHashes = make([]common.Hash, genBlocks+1)
)
// Set the terminal total difficulty in the config
genesis.Config.TerminalTotalDifficulty = big.NewInt(0)
genesis.Config.TerminalTotalDifficultyPassed = true
backend := newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
var (
tx *types.Transaction
err error
)
b.SetPoS()
switch i {
case 0:
// transfer 1000wei
tx, err = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &acc2Addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), types.HomesteadSigner{}, acc1Key)
case 1:
// create contract
tx, err = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: nil, Gas: 53100, GasPrice: b.BaseFee(), Data: common.FromHex("0x60806040")}), signer, acc1Key)
case 2:
// with logs
// transfer(address to, uint256 value)
data := fmt.Sprintf("0xa9059cbb%s%s", common.HexToHash(common.BigToAddress(big.NewInt(int64(i + 1))).Hex()).String()[2:], common.BytesToHash([]byte{byte(i + 11)}).String()[2:])
tx, err = types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &contract, Gas: 60000, GasPrice: b.BaseFee(), Data: common.FromHex(data)}), signer, acc1Key)
case 3:
// dynamic fee with logs
// transfer(address to, uint256 value)
data := fmt.Sprintf("0xa9059cbb%s%s", common.HexToHash(common.BigToAddress(big.NewInt(int64(i + 1))).Hex()).String()[2:], common.BytesToHash([]byte{byte(i + 11)}).String()[2:])
fee := big.NewInt(500)
fee.Add(fee, b.BaseFee())
tx, err = types.SignTx(types.NewTx(&types.DynamicFeeTx{Nonce: uint64(i), To: &contract, Gas: 60000, Value: big.NewInt(1), GasTipCap: big.NewInt(500), GasFeeCap: fee, Data: common.FromHex(data)}), signer, acc1Key)
case 4:
// access list with contract create
accessList := types.AccessList{{
Address: contract,
StorageKeys: []common.Hash{{0}},
}}
tx, err = types.SignTx(types.NewTx(&types.AccessListTx{Nonce: uint64(i), To: nil, Gas: 58100, GasPrice: b.BaseFee(), Data: common.FromHex("0x60806040"), AccessList: accessList}), signer, acc1Key)
}
if err != nil {
t.Errorf("failed to sign tx: %v", err)
}
if tx != nil {
b.AddTx(tx)
txHashes[i] = tx.Hash()
}
})
txHashes[genBlocks] = mockStateSyncTxOnCurrentBlock(t, backend)
var testSuite = []struct {
txHash common.Hash
file string
}{
// 0. normal success
{
txHash: txHashes[0],
file: "normal-transfer-tx",
},
// 1. create contract
{
txHash: txHashes[1],
file: "create-contract-tx",
},
// 2. with logs success
{
txHash: txHashes[2],
file: "with-logs",
},
// 3. dynamic tx with logs success
{
txHash: txHashes[3],
file: `dynamic-tx-with-logs`,
},
// 4. access list tx with create contract
{
txHash: txHashes[4],
file: "create-contract-with-access-list",
},
// 5. txhash empty
{
txHash: common.Hash{},
file: "txhash-empty",
},
// 6. txhash not found
{
txHash: common.HexToHash("deadbeef"),
file: "txhash-notfound",
},
// 7. state sync tx found
{
txHash: txHashes[5],
file: "state-sync-tx",
},
}
// map sprint 0 to block 6
backend.ChainConfig().Bor.Sprint["0"] = uint64(genBlocks)
api := NewTransactionAPI(backend, new(AddrLocker))
return api, txHashes, testSuite
}
func mockStateSyncTxOnCurrentBlock(t *testing.T, backend *testBackend) common.Hash {
// State Sync Tx Setup
var stateSyncLogs []*types.Log
block, err := backend.BlockByHash(context.Background(), backend.CurrentBlock().Hash())
if err != nil {
t.Errorf("failed to get current block: %v", err)
}
types.DeriveFieldsForBorLogs(stateSyncLogs, block.Hash(), block.NumberU64(), 0, 0)
// Write bor receipt
rawdb.WriteBorReceipt(backend.ChainDb(), block.Hash(), block.NumberU64(), &types.ReceiptForStorage{
Status: types.ReceiptStatusSuccessful, // make receipt status successful
Logs: stateSyncLogs,
})
// Write bor tx reverse lookup
rawdb.WriteBorTxLookupEntry(backend.ChainDb(), block.Hash(), block.NumberU64())
return types.GetDerivedBorTxHash(types.BorReceiptKey(block.NumberU64(), block.Hash()))
}
func TestRPCGetTransactionReceipt(t *testing.T) {
var (
api, _, testSuite = setupTransactionsToApiTest(t)
)
for i, tt := range testSuite {
var (
result interface{}
err error
)
result, err = api.GetTransactionReceipt(context.Background(), tt.txHash)
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
testRPCResponseWithFile(t, i, result, "eth_getTransactionReceipt", tt.file)
}
}
func TestRPCGetTransactionByHash(t *testing.T) {
var (
api, _, testSuite = setupTransactionsToApiTest(t)
)
for i, tt := range testSuite {
var (
result interface{}
err error
)
result, err = api.GetTransactionByHash(context.Background(), tt.txHash)
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
testRPCResponseWithFile(t, i, result, "eth_getTransactionByHash", tt.file)
}
}
func TestRPCGetBlockTransactionCountByHash(t *testing.T) {
var (
api, _, _ = setupTransactionsToApiTest(t)
)
cnt := api.GetBlockTransactionCountByHash(context.Background(), api.b.CurrentBlock().Hash())
// 2 txs: create-contract-with-access-list + state sync tx
expected := hexutil.Uint(2)
require.Equal(t, expected, *cnt)
}
func TestRPCGetTransactionByBlockHashAndIndex(t *testing.T) {
var (
api, _, _ = setupTransactionsToApiTest(t)
)
createContractWithAccessList := api.GetTransactionByBlockHashAndIndex(context.Background(), api.b.CurrentBlock().Hash(), 0)
stateSyncTx := api.GetTransactionByBlockHashAndIndex(context.Background(), api.b.CurrentBlock().Hash(), 1)
testRPCResponseWithFile(t, 0, createContractWithAccessList, "eth_getTransactionByBlockHashAndIndex", "create-contract-with-access-list")
testRPCResponseWithFile(t, 1, stateSyncTx, "eth_getTransactionByBlockHashAndIndex", "state-sync-tx")
}
type precompileContract struct{}
func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 }
func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil }
func TestStateOverrideMovePrecompile(t *testing.T) {
db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
statedb, err := state.New(common.Hash{}, db)
if err != nil {
t.Fatalf("failed to create statedb: %v", err)
}
precompiles := map[common.Address]vm.PrecompiledContract{
common.BytesToAddress([]byte{0x1}): &precompileContract{},
common.BytesToAddress([]byte{0x2}): &precompileContract{},
}
bytes2Addr := func(b []byte) *common.Address {
a := common.BytesToAddress(b)
return &a
}
var testSuite = []struct {
overrides StateOverride
expectedPrecompiles map[common.Address]struct{}
fail bool
}{
{
overrides: StateOverride{
common.BytesToAddress([]byte{0x1}): {
Code: hex2Bytes("0xff"),
MovePrecompileTo: bytes2Addr([]byte{0x2}),
},
common.BytesToAddress([]byte{0x2}): {
Code: hex2Bytes("0x00"),
},
},
// 0x2 has already been touched by the moveTo.
fail: true,
}, {
overrides: StateOverride{
common.BytesToAddress([]byte{0x1}): {
Code: hex2Bytes("0xff"),
MovePrecompileTo: bytes2Addr([]byte{0xff}),
},
common.BytesToAddress([]byte{0x3}): {
Code: hex2Bytes("0x00"),
MovePrecompileTo: bytes2Addr([]byte{0xfe}),
},
},
// 0x3 is not a precompile.
fail: true,
}, {
overrides: StateOverride{
common.BytesToAddress([]byte{0x1}): {
Code: hex2Bytes("0xff"),
MovePrecompileTo: bytes2Addr([]byte{0xff}),
},
common.BytesToAddress([]byte{0x2}): {
Code: hex2Bytes("0x00"),
MovePrecompileTo: bytes2Addr([]byte{0xfe}),
},
},
expectedPrecompiles: map[common.Address]struct{}{common.BytesToAddress([]byte{0xfe}): {}, common.BytesToAddress([]byte{0xff}): {}},
},
}
for i, tt := range testSuite {
cpy := maps.Clone(precompiles)
// Apply overrides
err := tt.overrides.Apply(statedb, cpy)
if tt.fail {
if err == nil {
t.Errorf("test %d: want error, have nothing", i)
}
continue
}
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
// Precompile keys
if len(cpy) != len(tt.expectedPrecompiles) {
t.Errorf("test %d: precompile mismatch, want %d, have %d", i, len(tt.expectedPrecompiles), len(cpy))
}
for k := range tt.expectedPrecompiles {
if _, ok := cpy[k]; !ok {
t.Errorf("test %d: precompile not found: %s", i, k.String())
}
}
}
}
func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc string, file string) {
data, err := json.MarshalIndent(result, "", " ")
if err != nil {
t.Errorf("test %d: json marshal error", testid)
return
}
outputFile := filepath.Join("testdata", fmt.Sprintf("%s-%s.json", rpc, file))
if os.Getenv("WRITE_TEST_FILES") != "" {
os.WriteFile(outputFile, data, 0644)
}
want, err := os.ReadFile(outputFile)
if err != nil {
t.Fatalf("error reading expected test file: %s output: %v", outputFile, err)
}
require.JSONEqf(t, string(want), string(data), "test %d: json not match, want: %s, have: %s", testid, string(want), string(data))
}
func TestRPCGetTransactionReceiptsByBlock(t *testing.T) {
api, blockNrOrHash, testSuite := setupBlocksToApiTest(t)
receipts, err := api.GetTransactionReceiptsByBlock(context.Background(), blockNrOrHash)
if err != nil {
t.Fatal("api error")
}
for i, tt := range testSuite {
data, err := json.Marshal(receipts[i])
if err != nil {
t.Errorf("test %d: json marshal error", i)
continue
}
want, have := tt.want, string(data)
require.JSONEqf(t, want, have, "test %d: json not match, want: %s, have: %s", i, want, have)
}
}
func TestRPCGetBlockReceipts(t *testing.T) {
api, blockNrOrHash, testSuite := setupBlocksToApiTest(t)
receipts, err := api.GetBlockReceipts(context.Background(), blockNrOrHash)
if err != nil {
t.Fatal("api error")
}
for i, tt := range testSuite {
data, err := json.Marshal(receipts[i])
if err != nil {
t.Errorf("test %d: json marshal error", i)
continue
}
want, have := tt.want, string(data)
require.JSONEqf(t, want, have, "test %d: json not match, want: %s, have: %s", i, want, have)
}
}
func setupBlocksToApiTest(t *testing.T) (*BlockChainAPI, rpc.BlockNumberOrHash, []struct {
txHash common.Hash
want string
}) {
// Initialize test accounts
var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
contract = common.HexToAddress("0000000000000000000000000000000000031ec7")
genesis = &core.Genesis{
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{
acc1Addr: {Balance: big.NewInt(params.Ether)},
acc2Addr: {Balance: big.NewInt(params.Ether)},
contract: {Balance: big.NewInt(params.Ether), Code: common.FromHex("0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a9059cbb14610030575b600080fd5b61004a6004803603810190610045919061016a565b610060565b60405161005791906101c5565b60405180910390f35b60008273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516100bf91906101ef565b60405180910390a36001905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610101826100d6565b9050919050565b610111816100f6565b811461011c57600080fd5b50565b60008135905061012e81610108565b92915050565b6000819050919050565b61014781610134565b811461015257600080fd5b50565b6000813590506101648161013e565b92915050565b60008060408385031215610181576101806100d1565b5b600061018f8582860161011f565b92505060206101a085828601610155565b9150509250929050565b60008115159050919050565b6101bf816101aa565b82525050565b60006020820190506101da60008301846101b6565b92915050565b6101e981610134565b82525050565b600060208201905061020460008301846101e0565b9291505056fea2646970667358221220b469033f4b77b9565ee84e0a2f04d496b18160d26034d54f9487e57788fd36d564736f6c63430008120033")},
},
}
genTxs = 6
genBlocks = 1
signer = types.LatestSignerForChainID(params.TestChainConfig.ChainID)
txHashes = make([]common.Hash, 0, genTxs)
)
backend := newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) {
switch i {
case 0:
// transfer 1000wei
tx1, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(0), To: &acc2Addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), types.HomesteadSigner{}, acc1Key)
b.AddTx(tx1)
txHashes = append(txHashes, tx1.Hash())
// create contract
tx2, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(1), To: nil, Gas: 53100, GasPrice: b.BaseFee(), Data: common.FromHex("0x60806040")}), signer, acc1Key)
b.AddTx(tx2)
txHashes = append(txHashes, tx2.Hash())
// with logs
// transfer(address to, uint256 value)
data3 := fmt.Sprintf("0xa9059cbb%s%s", common.HexToHash(common.BigToAddress(big.NewInt(int64(i + 1))).Hex()).String()[2:], common.BytesToHash([]byte{byte(i + 11)}).String()[2:])
tx3, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(2), To: &contract, Gas: 60000, GasPrice: b.BaseFee(), Data: common.FromHex(data3)}), signer, acc1Key)
b.AddTx(tx3)
txHashes = append(txHashes, tx3.Hash())
// dynamic fee with logs
// transfer(address to, uint256 value)
data4 := fmt.Sprintf("0xa9059cbb%s%s", common.HexToHash(common.BigToAddress(big.NewInt(int64(i + 1))).Hex()).String()[2:], common.BytesToHash([]byte{byte(i + 11)}).String()[2:])
fee := big.NewInt(500)
fee.Add(fee, b.BaseFee())
tx4, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{Nonce: uint64(3), To: &contract, Gas: 60000, Value: big.NewInt(1), GasTipCap: big.NewInt(500), GasFeeCap: fee, Data: common.FromHex(data4)}), signer, acc1Key)
b.AddTx(tx4)
txHashes = append(txHashes, tx4.Hash())
// access list with contract create
accessList := types.AccessList{{
Address: contract,
StorageKeys: []common.Hash{{0}},
}}
tx5, _ := types.SignTx(types.NewTx(&types.AccessListTx{Nonce: uint64(4), To: nil, Gas: 58100, GasPrice: b.BaseFee(), Data: common.FromHex("0x60806040"), AccessList: accessList}), signer, acc1Key)
b.AddTx(tx5)
txHashes = append(txHashes, tx5.Hash())
}
})
txHashes = append(txHashes, mockStateSyncTxOnCurrentBlock(t, backend))
// map sprint 0 to block 1
backend.ChainConfig().Bor.Sprint["0"] = 1
api := NewBlockChainAPI(backend)
blockHashes := make([]common.Hash, genBlocks+1)
ctx := context.Background()
for i := 0; i <= genBlocks; i++ {
header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
if err != nil {
t.Errorf("failed to get block: %d err: %v", i, err)
}
blockHashes[i] = header.Hash()
}
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHashes[1], true)
var testSuite = []struct {
txHash common.Hash
want string
}{
// 0. normal success
{
txHash: txHashes[0],
want: `{
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
"effectiveGasPrice": "0x342770c0",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5208",
"logs": [
{
"address": "0x0000000000000000000000000000000000001010",
"topics": [
"0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4",
"0x0000000000000000000000000000000000000000000000000000000000001010",
"0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7",
"0x0000000000000000000000000d3ab14bbad3d99f4203bd7a11acb94882050e7e"
],
"data": "0x00000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000de0a5fd640afa000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0a5fd640af6180000000000000000000000000000000000000000000000000de0b6b3a76403e8",
"blockNumber": "0x1",
"transactionHash": "0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e",
"transactionIndex": "0x0",
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"logIndex": "0x0",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000808000000000000000000000000000000000000000000000000000000000800000000000000000000100000020000000000000000000000000000000000802000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000800000000000000000000000800000108000000000000000000000000000000000000000000000000020000000000000000000100000",
"status": "0x1",
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
"transactionHash": "0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e",
"transactionIndex": "0x0",
"type": "0x0"
}`,
},
// 1. create contract
{
txHash: txHashes[1],
want: `{
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"blockNumber": "0x1",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0x12156",
"effectiveGasPrice": "0x342770c0",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0xcf4e",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": null,
"transactionHash": "0x705a7fca1d214002ee90d4e1c651b53e3506e6d5e3a539e9a7f7bf05b49add91",
"transactionIndex": "0x1",
"type": "0x0"
}`,
},
// 2. with logs success
{
txHash: txHashes[2],
want: `{
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x17f7e",
"effectiveGasPrice": "0x342770c0",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x5e28",
"logs": [
{
"address": "0x0000000000000000000000000000000000031ec7",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7",
"0x0000000000000000000000000000000000000000000000000000000000000001"
],
"data": "0x000000000000000000000000000000000000000000000000000000000000000b",
"blockNumber": "0x1",
"transactionHash": "0xa228af0975b99799bd28331085a6966aba2fb5814a8d89aabc342462aa40429a",
"transactionIndex": "0x2",
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"logIndex": "0x1",
"removed": false
}
],
"logsBloom": "0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000800000000000000008000000000000000000040000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000800000000000000000000000000000000000000040000000000000000000000000000000000000000020000000000000000000000000",
"status": "0x1",
"to": "0x0000000000000000000000000000000000031ec7",
"transactionHash": "0xa228af0975b99799bd28331085a6966aba2fb5814a8d89aabc342462aa40429a",
"transactionIndex": "0x2",
"type": "0x0"
}`,
},
// 3. dynamic tx with logs success
{
txHash: txHashes[3],
want: `{
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x1d30b",
"effectiveGasPrice": "0x342772b4",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0x538d",
"logs": [
{
"address": "0x0000000000000000000000000000000000001010",
"topics": [
"0x4dfe1bbbcf077ddc3e01291eea2d5c70c2b422b415d95645b9adcfd678cb1d63",
"0x0000000000000000000000000000000000000000000000000000000000001010",
"0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000a32f640000000000000000000000000000000000000000000000000de06892fa4b3d9800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de06892f9a80e340000000000000000000000000000000000000000000000000000000000a32f64",
"blockNumber": "0x1",
"transactionHash": "0xc2cc458a65bc96f642d4a2063cce162b0da642613d801271bdbc4aa7e775f3ed",
"transactionIndex": "0x3",
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"logIndex": "0x2",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000800000000000000000000100000020000000000000020000000000000000000800000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004000000000000000000001800000000000000000000000000000100000000020000000000000000000000000000000000000000020000000000000000000100000",
"status": "0x0",
"to": "0x0000000000000000000000000000000000031ec7",
"transactionHash": "0xc2cc458a65bc96f642d4a2063cce162b0da642613d801271bdbc4aa7e775f3ed",
"transactionIndex": "0x3",
"type": "0x2"
}`,
},
// 4. access list tx with create contract
{
txHash: txHashes[4],
want: `{
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"blockNumber": "0x1",
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
"cumulativeGasUsed": "0x2b325",
"effectiveGasPrice": "0x342770c0",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gasUsed": "0xe01a",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": null,
"transactionHash": "0x1e1161cf3fd01a02fc9c5ee66fc45a4805b3828bf41edd54213c20d97fc12b1d",
"transactionIndex": "0x4",
"type": "0x1"
}`,
},
// 5. state sync tx
{
txHash: txHashes[5],
want: `{
"blockHash": "0x1728b788dfe51e507d25f14f01414b5a17f807953c13833811d2afae1982b53b",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x0",
"effectiveGasPrice": "0x0",
"from": "0x0000000000000000000000000000000000000000",
"gasUsed": "0x0",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": "0x0000000000000000000000000000000000000000",
"transactionHash": "0xba46f68d5c3729ac3fb672fec579fc2cad543bc9edf5b2d47d7c6636ac2fbec9",
"transactionIndex": "0x5",
"type": "0x0"
}`,
},
}
return api, blockNrOrHash, testSuite
}
func addressToHash(a common.Address) common.Hash {
return common.BytesToHash(a.Bytes())
}