From c113e3b5b12619c80b962a3b6e895a14a13eaa75 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 17 Feb 2025 09:47:12 +0100 Subject: [PATCH 01/10] p2p: fix marshaling of NAT in TOML (#31192) This fixes an issue where a nat.Interface unmarshaled from the TOML config file could not be re-marshaled to TOML correctly. Fixes #31183 --- p2p/config.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/p2p/config.go b/p2p/config.go index 5ea62e12f5..68a9c0bb5f 100644 --- a/p2p/config.go +++ b/p2p/config.go @@ -18,6 +18,7 @@ package p2p import ( "crypto/ecdsa" + "encoding" "fmt" "github.com/ethereum/go-ethereum/common/mclock" @@ -135,6 +136,13 @@ type configNAT struct { nat.Interface } +func (w *configNAT) MarshalText() ([]byte, error) { + if tm, ok := w.Interface.(encoding.TextMarshaler); ok { + return tm.MarshalText() + } + return nil, fmt.Errorf("NAT specification %#v cannot be marshaled", w.Interface) +} + func (w *configNAT) UnmarshalText(input []byte) error { n, err := nat.Parse(string(input)) if err != nil { From d37a0b8cd0ad799dd696c25249987d89394a1409 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 17 Feb 2025 10:12:03 +0100 Subject: [PATCH 02/10] eth/protocols/eth: add discovery iterator to protocol (#31185) We somehow forgot to add this in #30302, so discv5 and DNS have actually been disabled since then. Fixes #31168 --- eth/protocols/eth/handler.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index d6cd83373b..a0848137fa 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -89,7 +89,7 @@ type TxPool interface { } // MakeProtocols constructs the P2P protocol definitions for `eth`. -func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol { +func MakeProtocols(backend Backend, network uint64, disc enode.Iterator) []p2p.Protocol { protocols := make([]p2p.Protocol, 0, len(ProtocolVersions)) for _, version := range ProtocolVersions { protocols = append(protocols, p2p.Protocol{ @@ -110,7 +110,8 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2 PeerInfo: func(id enode.ID) interface{} { return backend.PeerInfo(id) }, - Attributes: []enr.Entry{currentENREntry(backend.Chain())}, + DialCandidates: disc, + Attributes: []enr.Entry{currentENREntry(backend.Chain())}, }) } return protocols From c8c62dafc78b0edadfa9406503cef2d957299f8b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 17 Feb 2025 11:59:48 +0100 Subject: [PATCH 03/10] version: release go-ethereum v1.15.2 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 374522e438..9854aa1119 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 2 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 2 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From aec1964410648e1eda9e70685f27e846f2328c0f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 17 Feb 2025 12:01:20 +0100 Subject: [PATCH 04/10] version: begin v1.15.3 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 9854aa1119..f0fb3195b3 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 2 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 3 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From 3adfa1fbeb8b8711ca67221fff63d4d670fdf95b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 18 Feb 2025 03:48:42 +0100 Subject: [PATCH 05/10] trie: do not expect ordering in stacktrie during fuzzing (#31170) This PR removes the assumption of the stacktrie and trie to have the same ordering. This was hit by the fuzzers on oss-fuzz --------- Co-authored-by: Gary Rong --- trie/stacktrie_fuzzer_test.go | 25 ++++++++++------------ trie/trie_test.go | 39 ++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go index df487d16bf..7ff6ef0235 100644 --- a/trie/stacktrie_fuzzer_test.go +++ b/trie/stacktrie_fuzzer_test.go @@ -37,16 +37,13 @@ func FuzzStackTrie(f *testing.F) { } func fuzz(data []byte, debugging bool) { - // This spongeDb is used to check the sequence of disk-db-writes var ( - input = bytes.NewReader(data) - spongeA = &spongeDb{sponge: crypto.NewKeccakState()} - dbA = newTestDatabase(rawdb.NewDatabase(spongeA), rawdb.HashScheme) - trieA = NewEmpty(dbA) - spongeB = &spongeDb{sponge: crypto.NewKeccakState()} - dbB = newTestDatabase(rawdb.NewDatabase(spongeB), rawdb.HashScheme) - trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { - rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) + input = bytes.NewReader(data) + dbA = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme) + trieA = NewEmpty(dbA) + memDB = rawdb.NewMemoryDatabase() + trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { + rawdb.WriteTrieNode(memDB, common.Hash{}, path, hash, blob, rawdb.HashScheme) }) vals []*kv maxElements = 10000 @@ -55,13 +52,17 @@ func fuzz(data []byte, debugging bool) { ) // Fill the trie with elements for i := 0; input.Len() > 0 && i < maxElements; i++ { + // Build the key k := make([]byte, 32) input.Read(k) + + // Build the val var a uint16 binary.Read(input, binary.LittleEndian, &a) a = 1 + a%100 v := make([]byte, a) input.Read(v) + if input.Len() == 0 { // If it was exhausted while reading, the value may be all zeroes, // thus 'deletion' which is not supported on stacktrie @@ -73,6 +74,7 @@ func fuzz(data []byte, debugging bool) { } keys[string(k)] = struct{}{} vals = append(vals, &kv{k: k, v: v}) + trieA.MustUpdate(k, v) } if len(vals) == 0 { @@ -99,11 +101,6 @@ func fuzz(data []byte, debugging bool) { if rootA != rootB { panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB)) } - sumA := spongeA.sponge.Sum(nil) - sumB := spongeB.sponge.Sum(nil) - if !bytes.Equal(sumA, sumB) { - panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB)) - } // Ensure all the nodes are persisted correctly var ( diff --git a/trie/trie_test.go b/trie/trie_test.go index 423ed30fe8..77234d9d9b 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -779,6 +779,7 @@ func TestCommitAfterHash(t *testing.T) { func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { // Make the random benchmark deterministic random := rand.New(rand.NewSource(0)) + // Create a realistic account trie to hash addresses = make([][20]byte, size) for i := 0; i < len(addresses); i++ { @@ -795,13 +796,18 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) { ) // The big.Rand function is not deterministic with regards to 64 vs 32 bit systems, // and will consume different amount of data from the rand source. - //balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) + // balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) // Therefore, we instead just read via byte buffer numBytes := random.Uint32() % 33 // [0, 32] bytes balanceBytes := make([]byte, numBytes) random.Read(balanceBytes) balance := new(uint256.Int).SetBytes(balanceBytes) - data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code}) + data, _ := rlp.EncodeToBytes(&types.StateAccount{ + Nonce: nonce, + Balance: balance, + Root: root, + CodeHash: code, + }) accounts[i] = data } return addresses, accounts @@ -856,6 +862,7 @@ func (s *spongeDb) Flush() { s.sponge.Write([]byte(key)) s.sponge.Write([]byte(s.values[key])) } + fmt.Println(len(s.keys)) } // spongeBatch is a dummy batch which immediately writes to the underlying spongedb @@ -873,10 +880,12 @@ func (b *spongeBatch) Write() error { return nil } func (b *spongeBatch) Reset() {} func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil } -// TestCommitSequence tests that the trie.Commit operation writes the elements of the trie -// in the expected order. -// The test data was based on the 'master' code, and is basically random. It can be used -// to check whether changes to the trie modifies the write order or data in any way. +// TestCommitSequence tests that the trie.Commit operation writes the elements +// of the trie in the expected order. +// +// The test data was based on the 'master' code, and is basically random. +// It can be used to check whether changes to the trie modifies the write order +// or data in any way. func TestCommitSequence(t *testing.T) { for i, tc := range []struct { count int @@ -887,19 +896,23 @@ func TestCommitSequence(t *testing.T) { {2000, common.FromHex("4574cd8e6b17f3fe8ad89140d1d0bf4f1bd7a87a8ac3fb623b33550544c77635")}, } { addresses, accounts := makeAccounts(tc.count) + // This spongeDb is used to check the sequence of disk-db-writes s := &spongeDb{sponge: crypto.NewKeccakState()} db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) - trie := NewEmpty(db) + // Fill the trie with elements + trie := NewEmpty(db) for i := 0; i < tc.count; i++ { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Flush trie -> database root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + // Flush memdb -> disk (sponge) db.Commit(root) + if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { t.Errorf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp) } @@ -917,12 +930,13 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { {200, common.FromHex("dde92ca9812e068e6982d04b40846dc65a61a9fd4996fc0f55f2fde172a8e13c")}, {2000, common.FromHex("ab553a7f9aff82e3929c382908e30ef7dd17a332933e92ba3fe873fc661ef382")}, } { - prng := rand.New(rand.NewSource(int64(i))) // This spongeDb is used to check the sequence of disk-db-writes + prng := rand.New(rand.NewSource(int64(i))) s := &spongeDb{sponge: crypto.NewKeccakState()} db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) - trie := NewEmpty(db) + // Fill the trie with elements + trie := NewEmpty(db) for i := 0; i < tc.count; i++ { key := make([]byte, 32) var val []byte @@ -939,6 +953,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { // Flush trie -> database root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) + // Flush memdb -> disk (sponge) db.Commit(root) if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { @@ -974,19 +989,22 @@ func TestCommitSequenceStackTrie(t *testing.T) { // For the stack trie, we need to do inserts in proper order key := make([]byte, 32) binary.BigEndian.PutUint64(key, uint64(i)) - var val []byte + // 50% short elements, 50% large elements + var val []byte if prng.Intn(2) == 0 { val = make([]byte, 1+prng.Intn(32)) } else { val = make([]byte, 1+prng.Intn(1024)) } prng.Read(val) + trie.Update(key, val) stTrie.Update(key, val) } // Flush trie -> database root, nodes := trie.Commit(false) + // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Commit(root) @@ -1045,6 +1063,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) { // Flush trie -> database root, nodes := trie.Commit(false) + // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Commit(root) From 68d477670c562747ba670523b14379506eecb112 Mon Sep 17 00:00:00 2001 From: piersy Date: Tue, 18 Feb 2025 05:50:19 +0000 Subject: [PATCH 06/10] utils: clarify description for history.state flag (#31164) --- cmd/utils/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f079df83b9..243b5d22f0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -262,7 +262,7 @@ var ( } StateHistoryFlag = &cli.Uint64Flag{ Name: "history.state", - Usage: "Number of recent blocks to retain state history for (default = 90,000 blocks, 0 = entire chain)", + Usage: "Number of recent blocks to retain state history for, only relevant in state.scheme=path (default = 90,000 blocks, 0 = entire chain)", Value: ethconfig.Defaults.StateHistory, Category: flags.StateCategory, } From 7332a1bc0ae14d25333d697a6a7a97b159928efb Mon Sep 17 00:00:00 2001 From: EdisonSR <61781882@qq.com> Date: Tue, 18 Feb 2025 18:15:36 +0800 Subject: [PATCH 07/10] ethclient: add comment describing block number tags (#30984) Adds a comment on how to use rpc.*BlockNumber and the explanation of the block number tags --------- Co-authored-by: Gary Rong --- ethclient/ethclient.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index f10626c01f..c761d4f8d3 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -85,11 +85,23 @@ func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) } -// BlockByNumber returns a block from the current canonical chain. If number is nil, the -// latest known block is returned. +// BlockByNumber returns a block from the current canonical chain. +// If `number` is nil, the latest known block is returned. // -// Note that loading full blocks requires two requests. Use HeaderByNumber -// if you don't need all transactions or uncle headers. +// Use `HeaderByNumber` if you don't need full transaction data or uncle headers. +// +// Supported special block number tags: +// - `earliest` : The genesis (earliest) block +// - `latest` : The most recently included block +// - `safe` : The latest safe head block +// - `finalized` : The latest finalized block +// - `pending` : The pending block +// +// Example usage: +// +// ```go +// BlockByNumber(context.Background(), big.NewInt(int64(rpc.LatestBlockNumber))) +// ``` func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) } @@ -210,8 +222,21 @@ func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He return head, err } -// HeaderByNumber returns a block header from the current canonical chain. If number is -// nil, the latest known header is returned. +// HeaderByNumber returns a block header from the current canonical chain. +// If `number` is nil, the latest known block header is returned. +// +// Supported special block number tags: +// - `earliest` : The genesis (earliest) block +// - `latest` : The most recently included block +// - `safe` : The latest safe head block +// - `finalized` : The latest finalized block +// - `pending` : The pending block +// +// Example usage: +// +// ```go +// HeaderByNumber(context.Background(), big.NewInt(int64(rpc.LatestBlockNumber))) +// ``` func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { var head *types.Header err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) From 32c6aa8a1a2595cbb89b05f93440d230841f8431 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 18 Feb 2025 21:53:33 +0800 Subject: [PATCH 08/10] core/vm: clean up EVM environmental structure (#31061) This PR does a few things including: - Remove `ContractRef` interface - Remove `vm.AccountRef` which implements `ContractRef` interface - Maintain the `jumpDests` struct in EVM for sharing between call frames - Simplify the delegateCall context initialization --- core/state_processor.go | 6 +- core/state_transition.go | 5 +- core/vm/contract.go | 86 +++++--------------- core/vm/eips.go | 8 +- core/vm/evm.go | 130 ++++++++++++++---------------- core/vm/gas_table_test.go | 4 +- core/vm/instructions.go | 12 +-- core/vm/instructions_test.go | 11 +-- core/vm/interface.go | 15 ---- core/vm/interpreter_test.go | 2 +- core/vm/runtime/runtime.go | 13 ++- eth/tracers/js/tracer_test.go | 19 +---- eth/tracers/logger/logger_test.go | 18 +---- tests/state_test.go | 5 +- 14 files changed, 109 insertions(+), 225 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 2c74f6f407..4e365c6550 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -229,7 +229,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) { } evm.SetTxContext(NewEVMTxContext(msg)) evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress) - _, _, _ = evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + _, _, _ = evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) evm.StateDB.Finalise(true) } @@ -253,7 +253,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { } evm.SetTxContext(NewEVMTxContext(msg)) evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress) - _, _, err := evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + _, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) if err != nil { panic(err) } @@ -292,7 +292,7 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte } evm.SetTxContext(NewEVMTxContext(msg)) evm.StateDB.AddAddressToAccessList(addr) - ret, _, _ := evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + ret, _, _ := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) evm.StateDB.Finalise(true) if len(ret) == 0 { return // skip empty output diff --git a/core/state_transition.go b/core/state_transition.go index e7edd76ced..0f9ee9eea5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -423,7 +423,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { var ( msg = st.msg - sender = vm.AccountRef(msg.From) rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time) contractCreation = msg.To == nil floorDataGas uint64 @@ -484,7 +483,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { vmerr error // vm errors do not effect consensus and are therefore not assigned to err ) if contractCreation { - ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value) + ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value) } else { // Increment the nonce for the next transaction. st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall) @@ -507,7 +506,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { } // Execute the transaction's call. - ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value) + ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value) } // Compute refund counter, capped to a refund quotient. diff --git a/core/vm/contract.go b/core/vm/contract.go index 5670244e17..0eaa91d959 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -22,39 +22,20 @@ import ( "github.com/holiman/uint256" ) -// ContractRef is a reference to the contract's backing object -type ContractRef interface { - Address() common.Address -} - -// AccountRef implements ContractRef. -// -// Account references are used during EVM initialisation and -// its primary use is to fetch addresses. Removing this object -// proves difficult because of the cached jump destinations which -// are fetched from the parent contract (i.e. the caller), which -// is a ContractRef. -type AccountRef common.Address - -// Address casts AccountRef to an Address -func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } - // Contract represents an ethereum contract in the state database. It contains // the contract code, calling arguments. Contract implements ContractRef type Contract struct { - // CallerAddress is the result of the caller which initialised this - // contract. However when the "call method" is delegated this value - // needs to be initialised to that of the caller's caller. - CallerAddress common.Address - caller ContractRef - self ContractRef + // caller is the result of the caller which initialised this + // contract. However, when the "call method" is delegated this + // value needs to be initialised to that of the caller's caller. + caller common.Address + address common.Address jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. analysis bitvec // Locally cached result of JUMPDEST analysis Code []byte CodeHash common.Hash - CodeAddr *common.Address Input []byte // is the execution frame represented by this object a contract deployment @@ -66,23 +47,18 @@ type Contract struct { } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract { - c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object} - - if parent, ok := caller.(*Contract); ok { - // Reuse JUMPDEST analysis from parent context if available. - c.jumpdests = parent.jumpdests - } else { - c.jumpdests = make(map[common.Hash]bitvec) +func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests map[common.Hash]bitvec) *Contract { + // Initialize the jump analysis map if it's nil, mostly for tests + if jumpDests == nil { + jumpDests = make(map[common.Hash]bitvec) + } + return &Contract{ + caller: caller, + address: address, + jumpdests: jumpDests, + Gas: gas, + value: value, } - - // Gas should be a pointer so it can safely be reduced through the run - // This pointer will be off the state transition - c.Gas = gas - // ensures a value is set - c.value = value - - return c } func (c *Contract) validJumpdest(dest *uint256.Int) bool { @@ -132,18 +108,6 @@ func (c *Contract) isCode(udest uint64) bool { return c.analysis.codeSegment(udest) } -// AsDelegate sets the contract to be a delegate call and returns the current -// contract (for chaining calls) -func (c *Contract) AsDelegate() *Contract { - // NOTE: caller must, at all times be a contract. It should never happen - // that caller is something other than a Contract. - parent := c.caller.(*Contract) - c.CallerAddress = parent.CallerAddress - c.value = parent.value - - return c -} - // GetOp returns the n'th element in the contract's byte array func (c *Contract) GetOp(n uint64) OpCode { if n < uint64(len(c.Code)) { @@ -158,7 +122,7 @@ func (c *Contract) GetOp(n uint64) OpCode { // Caller will recursively call caller when the contract is a delegate // call, including that of caller's caller. func (c *Contract) Caller() common.Address { - return c.CallerAddress + return c.caller } // UseGas attempts the use gas and subtracts it and returns true on success @@ -186,7 +150,7 @@ func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.G // Address returns the contracts address func (c *Contract) Address() common.Address { - return c.self.Address() + return c.address } // Value returns the contract's value (sent to it from it's caller) @@ -194,18 +158,8 @@ func (c *Contract) Value() *uint256.Int { return c.value } -// SetCallCode sets the code of the contract and address of the backing data -// object -func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { +// SetCallCode sets the code of the contract, +func (c *Contract) SetCallCode(hash common.Hash, code []byte) { c.Code = code c.CodeHash = hash - c.CodeAddr = addr -} - -// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash. -// In case hash is not provided, the jumpdest analysis will not be saved to the parent context -func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) { - c.Code = codeAndHash.code - c.CodeHash = codeAndHash.hash - c.CodeAddr = addr } diff --git a/core/vm/eips.go b/core/vm/eips.go index 515999bd19..6159eade7e 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -338,13 +338,9 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC } addr := common.Address(a.Bytes20()) code := interpreter.evm.StateDB.GetCode(addr) - contract := &Contract{ - Code: code, - self: AccountRef(addr), - } paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - if !contract.IsSystemCall { - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + if !scope.Contract.IsSystemCall { + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false) if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { scope.Contract.Gas = 0 return nil, ErrOutOfGas diff --git a/core/vm/evm.go b/core/vm/evm.go index 442441e9ae..c28dcb2554 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -91,29 +91,40 @@ type EVM struct { // Context provides auxiliary blockchain related information Context BlockContext TxContext + // StateDB gives access to the underlying state StateDB StateDB - // Depth is the current call stack + + // depth is the current call stack depth int // chainConfig contains information about the current chain chainConfig *params.ChainConfig + // chain rules contains the chain rules for the current epoch chainRules params.Rules - // virtual machine configuration options used to initialise the - // evm. + + // virtual machine configuration options used to initialise the evm Config Config - // global (to this context) ethereum virtual machine - // used throughout the execution of the tx. + + // global (to this context) ethereum virtual machine used throughout + // the execution of the tx interpreter *EVMInterpreter + // abort is used to abort the EVM calling operations abort atomic.Bool + // callGasTemp holds the gas available for the current call. This is needed because the // available gas is calculated in gasCall* according to the 63/64 rule and later // applied in opCall*. callGasTemp uint64 + // precompiles holds the precompiled contracts for the current epoch precompiles map[common.Address]PrecompiledContract + + // jumpDests is the aggregated result of JUMPDEST analysis made through + // the life cycle of EVM. + jumpDests map[common.Hash]bitvec } // NewEVM constructs an EVM instance with the supplied block context, state @@ -127,6 +138,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), + jumpDests: make(map[common.Hash]bitvec), } evm.precompiles = activePrecompiledContracts(evm.chainRules) evm.interpreter = NewEVMInterpreter(evm) @@ -165,18 +177,18 @@ func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } -func isSystemCall(caller ContractRef) bool { - return caller.Address() == params.SystemAddress +func isSystemCall(caller common.Address) bool { + return caller == params.SystemAddress } // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takse // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. -func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { // Capture the tracer start/end events in debug mode if evm.Config.Tracer != nil { - evm.captureBegin(evm.depth, CALL, caller.Address(), addr, input, gas, value.ToBig()) + evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig()) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -186,7 +198,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance - if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) { return nil, gas, ErrInsufficientBalance } snapshot := evm.StateDB.Snapshot() @@ -209,23 +221,20 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) + evm.Context.Transfer(evm.StateDB, caller, addr, value) if isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. code := evm.resolveCode(addr) if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { - addrCopy := addr - // If the account has no code, we can abort here - // The depth-check is already done, and precompiles handled above - contract := NewContract(caller, AccountRef(addrCopy), value, gas) + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, addr, value, gas, evm.jumpDests) contract.IsSystemCall = isSystemCall(caller) - contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code) + contract.SetCallCode(evm.resolveCodeHash(addr), code) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -256,10 +265,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. -func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.captureBegin(evm.depth, CALLCODE, caller.Address(), addr, input, gas, value.ToBig()) + evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig()) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -272,7 +281,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Note although it's noop to transfer X ether to caller itself. But // if caller doesn't have enough balance, it would be an error to allow // over-charging itself. So the check here is necessary. - if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + if !evm.Context.CanTransfer(evm.StateDB, caller, value) { return nil, gas, ErrInsufficientBalance } var snapshot = evm.StateDB.Snapshot() @@ -281,11 +290,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { - addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, AccountRef(caller.Address()), value, gas) - contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) + contract := NewContract(caller, caller, value, gas, evm.jumpDests) + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -295,7 +303,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } - gas = 0 } } @@ -307,14 +314,11 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // // DelegateCall differs from CallCode in the sense that it executes the given address' // code with the caller as context and the caller is set to the caller of the caller. -func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - // NOTE: caller must, at all times be a contract. It should never happen - // that caller is something other than a Contract. - parent := caller.(*Contract) // DELEGATECALL inherits value from parent call - evm.captureBegin(evm.depth, DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig()) + evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas, value.ToBig()) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -329,10 +333,11 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { - addrCopy := addr // Initialise a new contract and make initialise the delegate values - contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() - contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) + // + // Note: The value refers to the original value from the parent call. + contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -352,10 +357,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // as parameters while disallowing any modifications to the state during the call. // Opcodes that attempt to perform such modifications will result in exceptions // instead of performing the modifications. -func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { +func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.captureBegin(evm.depth, STATICCALL, caller.Address(), addr, input, gas, nil) + evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -380,14 +385,11 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { - // At this point, we use a copy of address. If we don't, the go compiler will - // leak the 'contract' to the outer scope, and make allocation for 'contract' - // even if the actual execution ends on RunPrecompiled above. - addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas) - contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) + contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) + // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. @@ -407,22 +409,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte return ret, gas, err } -type codeAndHash struct { - code []byte - hash common.Hash -} - -func (c *codeAndHash) Hash() common.Hash { - if c.hash == (common.Hash{}) { - c.hash = crypto.Keccak256Hash(c.code) - } - return c.hash -} - // create creates a new contract using code as deployment code. -func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { +func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { if evm.Config.Tracer != nil { - evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig()) + evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig()) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -432,14 +422,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.depth > int(params.CallCreateDepth) { return nil, common.Address{}, gas, ErrDepth } - if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + if !evm.Context.CanTransfer(evm.StateDB, caller, value) { return nil, common.Address{}, gas, ErrInsufficientBalance } - nonce := evm.StateDB.GetNonce(caller.Address()) + nonce := evm.StateDB.GetNonce(caller) if nonce+1 < nonce { return nil, common.Address{}, gas, ErrNonceUintOverflow } - evm.StateDB.SetNonce(caller.Address(), nonce+1, tracing.NonceChangeContractCreator) + evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator) // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { @@ -500,15 +490,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } gas = gas - statelessGas } - evm.Context.Transfer(evm.StateDB, caller.Address(), address, value) + evm.Context.Transfer(evm.StateDB, caller, address, value) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, AccountRef(address), value, gas) - contract.SetCodeOptionalHash(&address, codeAndHash) + contract := NewContract(caller, address, value, gas, evm.jumpDests) + + // Explicitly set the code to a null hash to prevent caching of jump analysis + // for the initialization code. + contract.SetCallCode(common.Hash{}, code) contract.IsDeployment = true - ret, err = evm.initNewContract(contract, address, value) + ret, err = evm.initNewContract(contract, address) if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -520,7 +513,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // initNewContract runs a new contract's creation code, performs checks on the // resulting code that is to be deployed, and consumes necessary gas. -func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) { +func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]byte, error) { ret, err := evm.interpreter.Run(contract, nil, false) if err != nil { return ret, err @@ -552,19 +545,18 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address, valu } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { - contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address())) - return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE) +func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller)) + return evm.create(caller, code, gas, value, contractAddr, CREATE) } // Create2 creates a new contract using code as deployment code. // // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. -func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { - codeAndHash := &codeAndHash{code: code} - contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) - return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) +func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code)) + return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) } // resolveCode returns the code associated with the provided account. After diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index be86885261..cb6143c0b5 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -97,7 +97,7 @@ func TestEIP2200(t *testing.T) { } evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) - _, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int)) + _, gas, err := evm.Call(common.Address{}, address, nil, tt.gaspool, new(uint256.Int)) if !errors.Is(err, tt.failure) { t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) } @@ -153,7 +153,7 @@ func TestCreateGas(t *testing.T) { evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, config) var startGas = uint64(testGas) - ret, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int)) + ret, gas, err := evm.Call(common.Address{}, address, nil, startGas, new(uint256.Int)) if err != nil { return false } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index c9eea33507..1785ffc139 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -677,7 +677,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation) - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value) + res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract.Address(), input, gas, &value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -718,7 +718,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2) // reuse size int for stackvalue stackvalue := size - res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, + res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract.Address(), input, gas, &endowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { @@ -755,7 +755,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if !value.IsZero() { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value) + ret, returnGas, err := interpreter.evm.Call(scope.Contract.Address(), toAddr, args, gas, &value) if err != nil { temp.Clear() @@ -789,7 +789,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, &value) + ret, returnGas, err := interpreter.evm.CallCode(scope.Contract.Address(), toAddr, args, gas, &value) if err != nil { temp.Clear() } else { @@ -818,7 +818,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) - ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas) + ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value) if err != nil { temp.Clear() } else { @@ -847,7 +847,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) - ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas) + ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract.Address(), toAddr, args, gas) if err != nil { temp.Clear() } else { diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 08f2b2bfea..0902d17c54 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -49,14 +49,6 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff var commonParams []*twoOperandParams var twoOpMethods map[string]executionFunc -type contractRef struct { - addr common.Address -} - -func (c contractRef) Address() common.Address { - return c.addr -} - func init() { // Params is a list of common edgecases that should be used for some common tests params := []string{ @@ -575,8 +567,7 @@ func TestOpTstore(t *testing.T) { mem = NewMemory() caller = common.Address{} to = common.Address{1} - contractRef = contractRef{caller} - contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) + contract = NewContract(caller, to, new(uint256.Int), 0, nil) scopeContext = ScopeContext{mem, stack, contract} value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) diff --git a/core/vm/interface.go b/core/vm/interface.go index 0d7862a66e..57f35cb249 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -17,8 +17,6 @@ package vm import ( - "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/stateless" @@ -104,16 +102,3 @@ type StateDB interface { // Finalise must be invoked at the end of a transaction Finalise(bool) } - -// CallContext provides a basic interface for the EVM calling conventions. The EVM -// depends on this context being implemented for doing subcalls and initialising new EVM contracts. -type CallContext interface { - // Call calls another contract. - Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) - // CallCode takes another contracts code and execute within our own context - CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) - // DelegateCall is same as CallCode except sender and value is propagated from parent to child scope - DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) - // Create creates a new contract - Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) -} diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index cacad8f813..0b93dd59e7 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -53,7 +53,7 @@ func TestLoopInterrupt(t *testing.T) { timeout := make(chan bool) go func(evm *EVM) { - _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int)) + _, _, err := evm.Call(common.Address{}, address, nil, math.MaxUint64, new(uint256.Int)) errChannel <- err }(evm) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 2243e14b65..9d984291f2 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -128,7 +128,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { var ( address = common.BytesToAddress([]byte("contract")) vmenv = NewEnv(cfg) - sender = vm.AccountRef(cfg.Origin) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil { @@ -143,7 +142,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { cfg.State.SetCode(address, code) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( - sender, + cfg.Origin, common.BytesToAddress([]byte("contract")), input, cfg.GasLimit, @@ -166,9 +165,8 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) } var ( - vmenv = NewEnv(cfg) - sender = vm.AccountRef(cfg.Origin) - rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) + vmenv = NewEnv(cfg) + rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil { cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) @@ -179,7 +177,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil) // Call the code with the given configuration. code, address, leftOverGas, err := vmenv.Create( - sender, + cfg.Origin, input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), @@ -200,7 +198,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er var ( vmenv = NewEnv(cfg) - sender = vm.AccountRef(cfg.Origin) statedb = cfg.State rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) @@ -214,7 +211,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( - sender, + cfg.Origin, address, input, cfg.GasLimit, diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index faad1e7194..dbfc7308f7 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -33,19 +33,6 @@ import ( "github.com/holiman/uint256" ) -type account struct{} - -func (account) SubBalance(amount *big.Int) {} -func (account) AddBalance(amount *big.Int) {} -func (account) SetAddress(common.Address) {} -func (account) Value() *big.Int { return nil } -func (account) SetBalance(*uint256.Int) {} -func (account) SetNonce(uint64) {} -func (account) Balance() *uint256.Int { return nil } -func (account) Address() common.Address { return common.Address{} } -func (account) SetCode(common.Hash, []byte) {} -func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} - type dummyStatedb struct { state.StateDB } @@ -68,7 +55,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo gasLimit uint64 = 31000 startGas uint64 = 10000 value = uint256.NewInt(0) - contract = vm.NewContract(account{}, account{}, value, startGas) + contract = vm.NewContract(common.Address{}, common.Address{}, value, startGas, nil) ) evm.SetTxContext(vmctx.txCtx) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} @@ -190,7 +177,7 @@ func TestHaltBetweenSteps(t *testing.T) { t.Fatal(err) } scope := &vm.ScopeContext{ - Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0), + Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), } evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)}) @@ -288,7 +275,7 @@ func TestEnterExit(t *testing.T) { t.Fatal(err) } scope := &vm.ScopeContext{ - Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0), + Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), } tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.OnExit(1, []byte{}, 400, nil, false) diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index e7799dde35..b1e38bf627 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -29,22 +29,6 @@ import ( "github.com/holiman/uint256" ) -type dummyContractRef struct { - calledForEach bool -} - -func (dummyContractRef) Address() common.Address { return common.Address{} } -func (dummyContractRef) Value() *big.Int { return new(big.Int) } -func (dummyContractRef) SetCode(common.Hash, []byte) {} -func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) { - d.calledForEach = true -} -func (d *dummyContractRef) SubBalance(amount *big.Int) {} -func (d *dummyContractRef) AddBalance(amount *big.Int) {} -func (d *dummyContractRef) SetBalance(*big.Int) {} -func (d *dummyContractRef) SetNonce(uint64) {} -func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } - type dummyStatedb struct { state.StateDB } @@ -59,7 +43,7 @@ func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) - contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 100000) + contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), 100000, nil) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash diff --git a/tests/state_test.go b/tests/state_test.go index 0c9553c075..e7525118e2 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -310,8 +310,7 @@ func runBenchmark(b *testing.B, t *StateTest) { evm.SetTxContext(txContext) // Create "contract" for sender to cache code analysis. - sender := vm.NewContract(vm.AccountRef(msg.From), vm.AccountRef(msg.From), - nil, 0) + sender := vm.NewContract(msg.From, msg.From, nil, 0, nil) var ( gasUsed uint64 @@ -326,7 +325,7 @@ func runBenchmark(b *testing.B, t *StateTest) { start := time.Now() // Execute the message. - _, leftOverGas, err := evm.Call(sender, *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value)) + _, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value)) if err != nil { b.Error(err) return From ef00a6e9a2bcb7b5737808d7e56a5c196009f918 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 18 Feb 2025 15:03:48 +0100 Subject: [PATCH 09/10] params: add osaka blob schedule (#31174) Prevents crashes when running execution spec tests for osaka --- consensus/misc/eip4844/eip4844.go | 8 ++++++++ core/genesis_test.go | 1 + params/config.go | 9 +++++++++ tests/init.go | 10 ++++++++++ 4 files changed, 28 insertions(+) diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go index 4a2754b55c..32b34f4e53 100644 --- a/consensus/misc/eip4844/eip4844.go +++ b/consensus/misc/eip4844/eip4844.go @@ -83,6 +83,8 @@ func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTim func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int { var frac uint64 switch config.LatestFork(header.Time) { + case forks.Osaka: + frac = config.BlobScheduleConfig.Osaka.UpdateFraction case forks.Prague: frac = config.BlobScheduleConfig.Prague.UpdateFraction case forks.Cancun: @@ -103,6 +105,8 @@ func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { s = cfg.BlobScheduleConfig ) switch { + case cfg.IsOsaka(london, time) && s.Osaka != nil: + return s.Osaka.Max case cfg.IsPrague(london, time) && s.Prague != nil: return s.Prague.Max case cfg.IsCancun(london, time) && s.Cancun != nil: @@ -125,6 +129,8 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int { return 0 } switch { + case s.Osaka != nil: + return s.Osaka.Max case s.Prague != nil: return s.Prague.Max case s.Cancun != nil: @@ -144,6 +150,8 @@ func targetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { s = cfg.BlobScheduleConfig ) switch { + case cfg.IsOsaka(london, time) && s.Osaka != nil: + return s.Osaka.Target case cfg.IsPrague(london, time) && s.Prague != nil: return s.Prague.Target case cfg.IsCancun(london, time) && s.Cancun != nil: diff --git a/core/genesis_test.go b/core/genesis_test.go index ad75afcd9e..86e5617ef8 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -281,6 +281,7 @@ func TestVerkleGenesisCommit(t *testing.T) { BlobScheduleConfig: ¶ms.BlobScheduleConfig{ Cancun: params.DefaultCancunBlobConfig, Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, Verkle: params.DefaultPragueBlobConfig, }, } diff --git a/params/config.go b/params/config.go index 5a2e10a943..523c987f01 100644 --- a/params/config.go +++ b/params/config.go @@ -317,10 +317,17 @@ var ( Max: 9, UpdateFraction: 5007716, } + // DefaultOsakaBlobConfig is the default blob configuration for the Osaka fork. + DefaultOsakaBlobConfig = &BlobConfig{ + Target: 6, + Max: 9, + UpdateFraction: 5007716, + } // DefaultBlobSchedule is the latest configured blob schedule for test chains. DefaultBlobSchedule = &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, + Osaka: DefaultOsakaBlobConfig, } ) @@ -501,6 +508,7 @@ type BlobConfig struct { type BlobScheduleConfig struct { Cancun *BlobConfig `json:"cancun,omitempty"` Prague *BlobConfig `json:"prague,omitempty"` + Osaka *BlobConfig `json:"osaka,omitempty"` Verkle *BlobConfig `json:"verkle,omitempty"` } @@ -732,6 +740,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error { }{ {name: "cancun", timestamp: c.CancunTime, config: bsc.Cancun}, {name: "prague", timestamp: c.PragueTime, config: bsc.Prague}, + {name: "osaka", timestamp: c.OsakaTime, config: bsc.Osaka}, } { if cur.config != nil { if err := cur.config.validate(); err != nil { diff --git a/tests/init.go b/tests/init.go index ee8d354224..a8bc424fa2 100644 --- a/tests/init.go +++ b/tests/init.go @@ -431,6 +431,11 @@ var Forks = map[string]*params.ChainConfig{ PragueTime: u64(0), OsakaTime: u64(0), DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + }, }, "PragueToOsakaAtTime15k": { ChainID: big.NewInt(1), @@ -453,6 +458,11 @@ var Forks = map[string]*params.ChainConfig{ PragueTime: u64(0), OsakaTime: u64(15_000), DepositContractAddress: params.MainnetChainConfig.DepositContractAddress, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + Osaka: params.DefaultOsakaBlobConfig, + }, }, } From dab746b3efb24954a9d556e910a53fe527846bf4 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Tue, 18 Feb 2025 15:08:43 -0500 Subject: [PATCH 10/10] eth/catalyst: support earlier forks in SimulatedBeacon (#31084) Co-authored-by: Marius van der Wijden --- eth/catalyst/simulated_beacon.go | 54 +++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index a24ff52101..c71add93bc 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -33,6 +33,8 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/forks" "github.com/ethereum/go-ethereum/rpc" ) @@ -95,6 +97,16 @@ type SimulatedBeacon struct { lastBlockTime uint64 } +func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersion { + switch config.LatestFork(time) { + case forks.Prague, forks.Cancun: + return engine.PayloadV3 + case forks.Paris, forks.Shanghai: + return engine.PayloadV2 + } + panic("invalid fork, simulated beacon needs to be started post-merge") +} + // NewSimulatedBeacon constructs a new simulated beacon chain. func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) { block := eth.BlockChain().CurrentBlock() @@ -107,7 +119,8 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err // if genesis block, send forkchoiceUpdated to trigger transition to PoS if block.Number.Sign() == 0 { - if _, err := engineAPI.ForkchoiceUpdatedV3(current, nil); err != nil { + version := payloadVersion(eth.BlockChain().Config(), block.Time) + if _, err := engineAPI.forkchoiceUpdated(current, nil, version, false); err != nil { return nil, err } } @@ -171,6 +184,8 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u return fmt.Errorf("failed to sync txpool: %w", err) } + version := payloadVersion(c.eth.BlockChain().Config(), timestamp) + var random [32]byte rand.Read(random[:]) fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, &engine.PayloadAttributes{ @@ -179,7 +194,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u Withdrawals: withdrawals, Random: random, BeaconRoot: &common.Hash{}, - }, engine.PayloadV3, false) + }, version, false) if err != nil { return err } @@ -204,28 +219,39 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u } } - // Independently calculate the blob hashes from sidecars. - blobHashes := make([]common.Hash, 0) - if envelope.BlobsBundle != nil { - hasher := sha256.New() - for _, commit := range envelope.BlobsBundle.Commitments { - var c kzg4844.Commitment - if len(commit) != len(c) { - return errors.New("invalid commitment length") + var ( + blobHashes []common.Hash + beaconRoot *common.Hash + requests [][]byte + ) + // Compute post-shanghai fields + if version > engine.PayloadV2 { + // Independently calculate the blob hashes from sidecars. + blobHashes = make([]common.Hash, 0) + if envelope.BlobsBundle != nil { + hasher := sha256.New() + for _, commit := range envelope.BlobsBundle.Commitments { + var c kzg4844.Commitment + if len(commit) != len(c) { + return errors.New("invalid commitment length") + } + copy(c[:], commit) + blobHashes = append(blobHashes, kzg4844.CalcBlobHashV1(hasher, &c)) } - copy(c[:], commit) - blobHashes = append(blobHashes, kzg4844.CalcBlobHashV1(hasher, &c)) } + beaconRoot = &common.Hash{} + requests = envelope.Requests } + // Mark the payload as canon - _, err = c.engineAPI.newPayload(*payload, blobHashes, &common.Hash{}, envelope.Requests, false) + _, err = c.engineAPI.newPayload(*payload, blobHashes, beaconRoot, requests, false) if err != nil { return err } c.setCurrentState(payload.BlockHash, finalizedHash) // Mark the block containing the payload as canonical - if _, err = c.engineAPI.ForkchoiceUpdatedV3(c.curForkchoiceState, nil); err != nil { + if _, err = c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, nil, version, false); err != nil { return err } c.lastBlockTime = payload.Timestamp