diff --git a/core/chain_makers.go b/core/chain_makers.go index 26714845eb..799300229d 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -122,6 +122,11 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti if err != nil { panic(err) } + // Merge the tx-local access event into the "block-local" one, in order to collect + // all values, so that the witness can be built. + if b.statedb.GetTrie().IsVerkle() { + b.statedb.AccessEvents().Merge(evm.AccessEvents) + } b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) if b.header.BlobGasUsed != nil { @@ -379,7 +384,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse misc.ApplyDAOHardFork(statedb) } - if config.IsPrague(b.header.Number, b.header.Time) { + if config.IsPrague(b.header.Number, b.header.Time) || config.IsVerkle(b.header.Number, b.header.Time) { // EIP-2935 blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) blockContext.Random = &common.Hash{} // enable post-merge instruction set @@ -487,13 +492,11 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine // Save pre state for proof generation // preState := statedb.Copy() - // Pre-execution system calls. - if config.IsPrague(b.header.Number, b.header.Time) { - // EIP-2935 - blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) - evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) - ProcessParentBlockHash(b.header.ParentHash, evm) - } + // EIP-2935 / 7709 + blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) + blockContext.Random = &common.Hash{} // enable post-merge instruction set + evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) + ProcessParentBlockHash(b.header.ParentHash, evm) // Execute any user modifications to the block. if gen != nil { @@ -561,7 +564,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine return cm.chain, cm.receipts, proofs, keyvals } -func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) { +func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (common.Hash, ethdb.Database, []*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) { db := rawdb.NewMemoryDatabase() cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) cacheConfig.SnapshotLimit = 0 @@ -572,7 +575,7 @@ func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n panic(err) } blocks, receipts, proofs, keyvals := GenerateVerkleChain(genesis.Config, genesisBlock, engine, db, triedb, n, gen) - return db, blocks, receipts, proofs, keyvals + return genesisBlock.Hash(), db, blocks, receipts, proofs, keyvals } func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 31bdd06b46..25d823cc87 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -157,6 +157,10 @@ func (s *hookedStateDB) Witness() *stateless.Witness { return s.inner.Witness() } +func (s *hookedStateDB) AccessEvents() *AccessEvents { + return s.inner.AccessEvents() +} + func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { prev := s.inner.SubBalance(addr, amount, reason) if s.hooks.OnBalanceChange != nil && !amount.IsZero() { diff --git a/core/state_processor.go b/core/state_processor.go index 3eb83a673a..cc147569f8 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -85,7 +85,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, evm) } - if p.config.IsPrague(block.Number(), block.Time()) { + if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) { ProcessParentBlockHash(block.ParentHash(), evm) } @@ -155,6 +155,12 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, } *usedGas += result.UsedGas + // Merge the tx-local access event into the "block-local" one, in order to collect + // all values, so that the witness can be built. + if statedb.GetTrie().IsVerkle() { + statedb.AccessEvents().Merge(evm.AccessEvents) + } + return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil } @@ -181,12 +187,6 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } - // Merge the tx-local access event into the "block-local" one, in order to collect - // all values, so that the witness can be built. - if statedb.GetTrie().IsVerkle() { - statedb.AccessEvents().Merge(evm.AccessEvents) - } - // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) @@ -229,12 +229,12 @@ 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(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560, true) evm.StateDB.Finalise(true) } // ProcessParentBlockHash stores the parent block hash in the history storage contract -// as per EIP-2935. +// as per EIP-2935/7709. func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { if tracer := evm.Config.Tracer; tracer != nil { onSystemCallStart(tracer, evm.GetVMContext()) @@ -253,7 +253,13 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { } evm.SetTxContext(NewEVMTxContext(msg)) evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress) - _, _, _ = evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + _, _, err := evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560, true) + if err != nil { + panic(err) + } + if evm.StateDB.AccessEvents() != nil { + evm.StateDB.AccessEvents().Merge(evm.AccessEvents) + } evm.StateDB.Finalise(true) } @@ -286,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(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560, true) 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 b6203e6aae..feb29ad2f2 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -484,7 +484,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(sender, st.to(), msg.Data, st.gasRemaining, value, false) } var gasRefund uint64 diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go index 02e94963c4..8e6e5e8335 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -152,7 +152,7 @@ func TestProcessVerkle(t *testing.T) { txCost1*2 + txCost2, txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, } - _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + _, _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { gen.SetPoS() // TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) @@ -219,7 +219,7 @@ func TestProcessParentBlockHash(t *testing.T) { // block 1 parent hash is 0x0100.... // block 2 parent hash is 0x0200.... // etc - checkBlockHashes := func(statedb *state.StateDB) { + checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) { statedb.SetNonce(params.HistoryStorageAddress, 1) statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) // Process n blocks, from 1 .. num @@ -227,20 +227,24 @@ func TestProcessParentBlockHash(t *testing.T) { for i := 1; i <= num; i++ { header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)} vmContext := NewEVMBlockContext(header, nil, new(common.Address)) - evm := vm.NewEVM(vmContext, statedb, params.MergedTestChainConfig, vm.Config{}) + chainConfig := params.MergedTestChainConfig + if isVerkle { + chainConfig = testVerkleChainConfig + } + evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{}) ProcessParentBlockHash(header.ParentHash, evm) } // Read block hashes for block 0 .. num-1 for i := 0; i < num; i++ { - have, want := getContractStoredBlockHash(statedb, uint64(i)), common.Hash{byte(i + 1)} + have, want := getContractStoredBlockHash(statedb, uint64(i), isVerkle), common.Hash{byte(i + 1)} if have != want { - t.Errorf("block %d, have parent hash %v, want %v", i, have, want) + t.Errorf("block %d, verkle=%v, have parent hash %v, want %v", i, isVerkle, have, want) } } } t.Run("MPT", func(t *testing.T) { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - checkBlockHashes(statedb) + checkBlockHashes(statedb, false) }) t.Run("Verkle", func(t *testing.T) { db := rawdb.NewMemoryDatabase() @@ -248,15 +252,18 @@ func TestProcessParentBlockHash(t *testing.T) { cacheConfig.SnapshotLimit = 0 triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) - checkBlockHashes(statedb) + checkBlockHashes(statedb, true) }) } // getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number' -func getContractStoredBlockHash(statedb *state.StateDB, number uint64) common.Hash { +func getContractStoredBlockHash(statedb *state.StateDB, number uint64, isVerkle bool) common.Hash { ringIndex := number % params.HistoryServeWindow var key common.Hash binary.BigEndian.PutUint64(key[24:], ringIndex) + if isVerkle { + return statedb.GetState(params.HistoryStorageAddress, key) + } return statedb.GetState(params.HistoryStorageAddress, key) } @@ -279,7 +286,7 @@ func TestProcessVerkleInvalidContractCreation(t *testing.T) { // // - The second block contains a single failing contract creation transaction, // that fails right off the bat. - _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + genesisH, _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { gen.SetPoS() if i == 0 { @@ -364,8 +371,9 @@ func TestProcessVerkleInvalidContractCreation(t *testing.T) { if stemStateDiff.SuffixDiffs[0].NewValue == nil { t.Fatalf("nil new value in BLOCKHASH contract insert") } - if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { - t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) + if *stemStateDiff.SuffixDiffs[0].NewValue != genesisH { + // je sais pas pourquoi, le hash storé est faux. On dirait le empty code hash mais c'est un hasard + t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, genesisH) } } else { // For all other entries present in the witness, check that nothing beyond @@ -393,8 +401,8 @@ func TestProcessVerkleInvalidContractCreation(t *testing.T) { if stemStateDiff.SuffixDiffs[0].NewValue == nil { t.Fatalf("missing post state value for BLOCKHASH contract at block #2") } - if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54") { - t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: 0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54 != %x", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) + if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { + t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: %x != %x", chain[0].Hash(), (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) } } else if suffixDiff.Suffix > 4 { t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) @@ -440,7 +448,7 @@ func TestProcessVerkleContractWithEmptyCode(t *testing.T) { config.ChainID.SetUint64(69421) gspec := verkleTestGenesis(&config) - _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + genesisH, _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { gen.SetPoS() var tx types.Transaction // a transaction that does some PUSH1n but returns a 0-sized contract @@ -472,8 +480,8 @@ func TestProcessVerkleContractWithEmptyCode(t *testing.T) { if stemStateDiff.SuffixDiffs[0].NewValue == nil { t.Fatalf("nil new value in BLOCKHASH contract insert") } - if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { - t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) + if *stemStateDiff.SuffixDiffs[0].NewValue != genesisH { + t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, genesisH) } } else { for _, suffixDiff := range stemStateDiff.SuffixDiffs { @@ -532,7 +540,7 @@ func TestProcessVerkleExtCodeHashOpcode(t *testing.T) { } extCodeHashContractAddr := crypto.CreateAddress(deployer, 1) - _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + _, _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { gen.SetPoS() if i == 0 { @@ -605,7 +613,7 @@ func TestProcessVerkleBalanceOpcode(t *testing.T) { account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") gspec = verkleTestGenesis(&config) ) - _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + _, _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { gen.SetPoS() txData := slices.Concat( []byte{byte(vm.PUSH20)}, @@ -686,7 +694,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { deployer := crypto.PubkeyToAddress(testKey.PublicKey) contract := crypto.CreateAddress(deployer, 0) - _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + _, _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { gen.SetPoS() if i == 0 { @@ -794,7 +802,7 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { deployer := crypto.PubkeyToAddress(testKey.PublicKey) contract := crypto.CreateAddress(deployer, 0) - _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + _, _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { gen.SetPoS() tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, Value: big.NewInt(42), @@ -897,7 +905,7 @@ func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) deployer := crypto.PubkeyToAddress(testKey.PublicKey) contract := crypto.CreateAddress(deployer, 0) - _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + _, _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { gen.SetPoS() if i == 0 { // Create self-destruct contract, sending 42 wei. @@ -977,7 +985,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} - _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + _, _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { gen.SetPoS() tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, Value: big.NewInt(42), @@ -1043,7 +1051,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} - _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + _, _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { gen.SetPoS() tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, Value: big.NewInt(42), diff --git a/core/vm/contract.go b/core/vm/contract.go index cfda75b27e..5670244e17 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -59,6 +59,7 @@ type Contract struct { // is the execution frame represented by this object a contract deployment IsDeployment bool + IsSystemCall bool Gas uint64 value *uint256.Int diff --git a/core/vm/eips.go b/core/vm/eips.go index a51a18dc60..ab58702428 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -345,10 +345,12 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC self: AccountRef(addr), } paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 - return nil, ErrOutOfGas + if !contract.IsSystemCall { + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } } scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) @@ -367,7 +369,7 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if *pc < codeLen { scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) - if !scope.Contract.IsDeployment && *pc%31 == 0 { + if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall && *pc%31 == 0 { // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. contractAddr := scope.Contract.Address() @@ -397,7 +399,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc { )), ) - if !scope.Contract.IsDeployment { + if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall { contractAddr := scope.Contract.Address() statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { diff --git a/core/vm/evm.go b/core/vm/evm.go index 1a0215459c..63aa7e34ce 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -174,7 +174,7 @@ func (evm *EVM) Interpreter() *EVMInterpreter { // parameters. It also handles any necessary value transfer required and takes // 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 ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, isSyscall bool) (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()) @@ -194,7 +194,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas p, isPrecompile := evm.precompile(addr) if !evm.StateDB.Exist(addr) { - if !isPrecompile && evm.chainRules.IsEIP4762 { + if !isPrecompile && evm.chainRules.IsEIP4762 && !isSyscall { // add proof of absence to witness wgas := evm.AccessEvents.AddAccount(addr, false) if gas < wgas { @@ -225,8 +225,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // 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) + contract.IsSystemCall = isSyscall contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code) - ret, err = evm.interpreter.Run(contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false, isSyscall) gas = contract.Gas } } @@ -286,7 +287,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // 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)) - ret, err = evm.interpreter.Run(contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false, false) gas = contract.Gas } if err != nil { @@ -333,7 +334,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // 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)) - ret, err = evm.interpreter.Run(contract, input, false) + ret, err = evm.interpreter.Run(contract, input, false, false) gas = contract.Gas } if err != nil { @@ -391,7 +392,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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. - ret, err = evm.interpreter.Run(contract, input, true) + ret, err = evm.interpreter.Run(contract, input, true, false) gas = contract.Gas } if err != nil { @@ -521,7 +522,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) { - ret, err := evm.interpreter.Run(contract, nil, false) + ret, err := evm.interpreter.Run(contract, nil, false, false) if err != nil { return ret, err } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index f3a9808251..55855727b5 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -394,7 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsEIP4762 { + if evm.chainRules.IsEIP4762 && !contract.IsSystemCall { if transfersValue { gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) if overflow { @@ -428,7 +428,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsEIP4762 { + if evm.chainRules.IsEIP4762 && !contract.IsSystemCall { address := common.Address(stack.Back(1).Bytes20()) transfersValue := !stack.Back(2).IsZero() if transfersValue { diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index be86885261..ab24dfac68 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(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int), false) 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(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int), false) if err != nil { return false } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 9b9a31a855..de30def386 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -752,7 +752,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, toAddr, args, gas, &value, false) if err != nil { temp.Clear() diff --git a/core/vm/interface.go b/core/vm/interface.go index 011541dde3..3488526fc4 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -98,6 +99,8 @@ type StateDB interface { Witness() *stateless.Witness + AccessEvents() *state.AccessEvents + // Finalise must be invoked at the end of a transaction Finalise(bool) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 996ed6e56a..6eb20649c1 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -159,7 +159,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // It's important to note that any errors returned by the interpreter should be // considered a revert-and-consume-all-gas operation except for // ErrExecutionReverted which means revert-and-keep-gas-left. -func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { +func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly, isSyscall bool) (ret []byte, err error) { // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() @@ -233,7 +233,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged, pcCopy, gasCopy = false, pc, contract.Gas } - if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment { + if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment && !isSyscall { // if the PC ends up in a new "chunk" of verkleized code, charge the // associated costs. contractAddr := contract.Address() diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index cacad8f813..9c928a4c72 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(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int), false) errChannel <- err }(evm) diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 3492994778..751761a911 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -41,6 +41,9 @@ func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor } func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + if contract.IsSystemCall { + return 0, nil + } address := stack.peek().Bytes20() gas := evm.AccessEvents.BasicDataGas(address, false) if gas == 0 { @@ -54,6 +57,9 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } + if contract.IsSystemCall { + return 0, nil + } gas := evm.AccessEvents.BasicDataGas(address, false) if gas == 0 { gas = params.WarmStorageReadCostEIP2929 @@ -62,6 +68,9 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, } func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + if contract.IsSystemCall { + return 0, nil + } address := stack.peek().Bytes20() if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil @@ -79,6 +88,9 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc { if err != nil { return 0, err } + if contract.IsSystemCall { + return gas, nil + } if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile { return gas, nil } @@ -102,6 +114,9 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { return 0, nil } + if contract.IsSystemCall { + return 0, nil + } contractAddr := contract.Address() statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false) if contractAddr != beneficiaryAddr { @@ -131,7 +146,7 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, uint64CodeOffset = gomath.MaxUint64 } _, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) - if !contract.IsDeployment { + if !contract.IsDeployment && !contract.IsSystemCall { gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) } return gas, nil @@ -143,6 +158,9 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo if err != nil { return 0, err } + if contract.IsSystemCall { + return gas, nil + } addr := common.Address(stack.peek().Bytes20()) wgas := evm.AccessEvents.BasicDataGas(addr, false) if wgas == 0 { diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 2243e14b65..f11c4a6d85 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -148,6 +148,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), + false, ) if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) @@ -219,6 +220,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), + false, ) if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index faad1e7194..e833572d1f 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -78,7 +78,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) - ret, err := evm.Interpreter().Run(contract, []byte{}, false) + ret, err := evm.Interpreter().Run(contract, []byte{}, false, false) tracer.OnExit(0, ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index e7799dde35..a84c9dc89c 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -64,7 +64,7 @@ func TestStoreCapture(t *testing.T) { contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash logger.OnTxStart(evm.GetVMContext(), nil, common.Address{}) - _, err := evm.Interpreter().Run(contract, []byte{}, false) + _, err := evm.Interpreter().Run(contract, []byte{}, false, false) if err != nil { t.Fatal(err) } diff --git a/tests/state_test.go b/tests/state_test.go index 7b82b05e58..b4403e1b2b 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -322,7 +322,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, *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value), false) if err != nil { b.Error(err) return