// 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"
"crypto/sha256"
"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/crypto/kzg4844"
"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 . ShanghaiTime = new ( uint64 )
config . CancunTime = new ( uint64 )
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 ( & accounts . Config { InsecureUnlockAllowed : true } )
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 { } , & txlookupLimit )
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 )
return true , tx , blockHash , blockNumber , index , nil
}
func ( b testBackend ) GetPoolTransactions ( ) ( types . Transactions , error ) { panic ( "implement me" ) }
func ( b testBackend ) GetPoolTransaction ( txHash common . Hash ) * types . Transaction { panic ( "implement me" ) }
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 ) BloomStatus ( ) ( uint64 , uint64 ) { panic ( "implement me" ) }
func ( b testBackend ) ServiceFilter ( ctx context . Context , session * bloombits . MatcherSession ) {
panic ( "implement me" )
}
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 ,
} ,
// BLOBHASH opcode
{
name : "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
{
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 { } ,
Status : "0x1" ,
} , {
ReturnValue : "0x" ,
GasUsed : "0x5208" ,
Logs : [ ] log { } ,
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 { } ,
Status : "0x1" ,
} , {
ReturnValue : "0x" ,
GasUsed : "0x5208" ,
Logs : [ ] log { } ,
Status : "0x1" ,
} } ,
} , {
Number : "0xc" ,
GasLimit : "0x47e7c4" ,
GasUsed : "0x5208" ,
Miner : coinbase ,
BaseFeePerGas : "0x0" ,
Calls : [ ] callRes { {
ReturnValue : "0x" ,
GasUsed : "0x5208" ,
Logs : [ ] log { } ,
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 : "0x77dc" ,
Miner : coinbase ,
BaseFeePerGas : "0x0" ,
Calls : [ ] callRes { {
ReturnValue : "0x" ,
GasUsed : "0x77dc" ,
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 : 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 ( 1 ) ,
} } ,
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 { } ,
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 { } ,
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 { } ,
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 { } ,
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 { } ,
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 . 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 { } ,
}
)
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 ) {
t . Parallel ( )
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 )
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 ) {
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" ,
} ,
}
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 setupReceiptBackend ( t * testing . T , genBlocks int ) ( * testBackend , [ ] common . Hash ) {
config := * params . MergedTestChainConfig
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 )
)
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 )
case 5 :
// blob tx
fee := big . NewInt ( 500 )
fee . Add ( fee , b . BaseFee ( ) )
tx , err = types . SignTx ( types . NewTx ( & types . BlobTx {
Nonce : uint64 ( i ) ,
GasTipCap : uint256 . NewInt ( 1 ) ,
GasFeeCap : uint256 . MustFromBig ( fee ) ,
Gas : params . TxGas ,
To : acc2Addr ,
BlobFeeCap : uint256 . NewInt ( 1 ) ,
BlobHashes : [ ] common . Hash { { 1 } } ,
Value : new ( uint256 . Int ) ,
} ) , signer , acc1Key )
}
if err != nil {
t . Errorf ( "failed to sign tx: %v" , err )
}
if tx != nil {
b . AddTx ( tx )
txHashes [ i ] = tx . Hash ( )
}
} )
return backend , txHashes
}
func TestRPCGetTransactionReceipt ( t * testing . T ) {
t . Parallel ( )
var (
backend , txHashes = setupReceiptBackend ( t , 6 )
api = NewTransactionAPI ( backend , new ( AddrLocker ) )
)
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. blob tx
{
txHash : txHashes [ 5 ] ,
file : "blob-tx" ,
} ,
}
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 TestRPCGetBlockReceipts ( t * testing . T ) {
t . Parallel ( )
var (
genBlocks = 6
backend , _ = setupReceiptBackend ( t , genBlocks )
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 ( )
}
var testSuite = [ ] struct {
test rpc . BlockNumberOrHash
file string
} {
// 0. block without any txs(hash)
{
test : rpc . BlockNumberOrHashWithHash ( blockHashes [ 0 ] , false ) ,
file : "number-0" ,
} ,
// 1. block without any txs(number)
{
test : rpc . BlockNumberOrHashWithNumber ( 0 ) ,
file : "number-1" ,
} ,
// 2. earliest tag
{
test : rpc . BlockNumberOrHashWithNumber ( rpc . EarliestBlockNumber ) ,
file : "tag-earliest" ,
} ,
// 3. latest tag
{
test : rpc . BlockNumberOrHashWithNumber ( rpc . LatestBlockNumber ) ,
file : "tag-latest" ,
} ,
// 4. block with legacy transfer tx(hash)
{
test : rpc . BlockNumberOrHashWithHash ( blockHashes [ 1 ] , false ) ,
file : "block-with-legacy-transfer-tx" ,
} ,
// 5. block with contract create tx(number)
{
test : rpc . BlockNumberOrHashWithNumber ( rpc . BlockNumber ( 2 ) ) ,
file : "block-with-contract-create-tx" ,
} ,
// 6. block with legacy contract call tx(hash)
{
test : rpc . BlockNumberOrHashWithHash ( blockHashes [ 3 ] , false ) ,
file : "block-with-legacy-contract-call-tx" ,
} ,
// 7. block with dynamic fee tx(number)
{
test : rpc . BlockNumberOrHashWithNumber ( rpc . BlockNumber ( 4 ) ) ,
file : "block-with-dynamic-fee-tx" ,
} ,
// 8. block is empty
{
test : rpc . BlockNumberOrHashWithHash ( common . Hash { } , false ) ,
file : "hash-empty" ,
} ,
// 9. block is not found
{
test : rpc . BlockNumberOrHashWithHash ( common . HexToHash ( "deadbeef" ) , false ) ,
file : "hash-notfound" ,
} ,
// 10. block is not found
{
test : rpc . BlockNumberOrHashWithNumber ( rpc . BlockNumber ( genBlocks + 1 ) ) ,
file : "block-notfound" ,
} ,
// 11. block with blob tx
{
test : rpc . BlockNumberOrHashWithNumber ( rpc . BlockNumber ( 6 ) ) ,
file : "block-with-blob-tx" ,
} ,
}
for i , tt := range testSuite {
var (
result interface { }
err error
)
result , err = api . GetBlockReceipts ( context . Background ( ) , tt . test )
if err != nil {
t . Errorf ( "test %d: want no error, have %v" , i , err )
continue
}
testRPCResponseWithFile ( t , i , result , "eth_getBlockReceipts" , tt . file )
}
}
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 addressToHash ( a common . Address ) common . Hash {
return common . BytesToHash ( a . Bytes ( ) )
}