core/{.,state,vm},miner,eth/tracers,tests: implement 7709 with a syscall flag (#31036)

Same as #31015 but requires the contract to exist. Not compatible with
any verkle testnet up to now.

This adds a `isSytemCall` flag so that it is possible to detect when a
system call is executed, so that the code execution and other locations
are not added to the witness.

---------

Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com>
Co-authored-by: Felix Lange <fjl@twurst.com>
pull/17439/merge
Guillaume Ballet 2 days ago committed by GitHub
parent 3fcbb6735e
commit 52766bedb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 23
      core/chain_makers.go
  2. 4
      core/state/statedb_hooked.go
  3. 24
      core/state_processor.go
  4. 53
      core/verkle_witness_test.go
  5. 1
      core/vm/contract.go
  6. 14
      core/vm/eips.go
  7. 9
      core/vm/evm.go
  8. 4
      core/vm/gas_table.go
  9. 3
      core/vm/interface.go
  10. 2
      core/vm/interpreter.go
  11. 20
      core/vm/operations_verkle.go

@ -122,6 +122,11 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
if err != nil { if err != nil {
panic(err) 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.txs = append(b.txs, tx)
b.receipts = append(b.receipts, receipt) b.receipts = append(b.receipts, receipt)
if b.header.BlobGasUsed != nil { if b.header.BlobGasUsed != nil {
@ -379,7 +384,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
misc.ApplyDAOHardFork(statedb) 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 // EIP-2935
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
blockContext.Random = &common.Hash{} // enable post-merge instruction set 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 // Save pre state for proof generation
// preState := statedb.Copy() // preState := statedb.Copy()
// Pre-execution system calls. // EIP-2935 / 7709
if config.IsPrague(b.header.Number, b.header.Time) { blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
// EIP-2935 blockContext.Random = &common.Hash{} // enable post-merge instruction set
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) ProcessParentBlockHash(b.header.ParentHash, evm)
ProcessParentBlockHash(b.header.ParentHash, evm)
}
// Execute any user modifications to the block. // Execute any user modifications to the block.
if gen != nil { if gen != nil {
@ -561,7 +564,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
return cm.chain, cm.receipts, proofs, keyvals 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() db := rawdb.NewMemoryDatabase()
cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme)
cacheConfig.SnapshotLimit = 0 cacheConfig.SnapshotLimit = 0
@ -572,7 +575,7 @@ func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n
panic(err) panic(err)
} }
blocks, receipts, proofs, keyvals := GenerateVerkleChain(genesis.Config, genesisBlock, engine, db, triedb, n, gen) 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 { func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {

@ -157,6 +157,10 @@ func (s *hookedStateDB) Witness() *stateless.Witness {
return s.inner.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 { func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
prev := s.inner.SubBalance(addr, amount, reason) prev := s.inner.SubBalance(addr, amount, reason)
if s.hooks.OnBalanceChange != nil && !amount.IsZero() { if s.hooks.OnBalanceChange != nil && !amount.IsZero() {

@ -85,7 +85,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if beaconRoot := block.BeaconRoot(); beaconRoot != nil { if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, evm) 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) ProcessParentBlockHash(block.ParentHash(), evm)
} }
@ -155,6 +155,12 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
} }
*usedGas += result.UsedGas *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 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()) 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. // Set the receipt logs and create the bloom filter.
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
@ -234,7 +234,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
} }
// ProcessParentBlockHash stores the parent block hash in the history storage contract // 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) { func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
if tracer := evm.Config.Tracer; tracer != nil { if tracer := evm.Config.Tracer; tracer != nil {
onSystemCallStart(tracer, evm.GetVMContext()) onSystemCallStart(tracer, evm.GetVMContext())
@ -253,7 +253,13 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
} }
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress) 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)
if err != nil {
panic(err)
}
if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
}
evm.StateDB.Finalise(true) evm.StateDB.Finalise(true)
} }

@ -152,7 +152,7 @@ func TestProcessVerkle(t *testing.T) {
txCost1*2 + txCost2, txCost1*2 + txCost2,
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, 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() gen.SetPoS()
// TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) // 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 1 parent hash is 0x0100....
// block 2 parent hash is 0x0200.... // block 2 parent hash is 0x0200....
// etc // etc
checkBlockHashes := func(statedb *state.StateDB) { checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
statedb.SetNonce(params.HistoryStorageAddress, 1) statedb.SetNonce(params.HistoryStorageAddress, 1)
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
// Process n blocks, from 1 .. num // Process n blocks, from 1 .. num
@ -227,20 +227,24 @@ func TestProcessParentBlockHash(t *testing.T) {
for i := 1; i <= num; i++ { for i := 1; i <= num; i++ {
header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)} header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)}
vmContext := NewEVMBlockContext(header, nil, new(common.Address)) 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) ProcessParentBlockHash(header.ParentHash, evm)
} }
// Read block hashes for block 0 .. num-1 // Read block hashes for block 0 .. num-1
for i := 0; i < num; i++ { 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 { 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) { t.Run("MPT", func(t *testing.T) {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
checkBlockHashes(statedb) checkBlockHashes(statedb, false)
}) })
t.Run("Verkle", func(t *testing.T) { t.Run("Verkle", func(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
@ -248,15 +252,18 @@ func TestProcessParentBlockHash(t *testing.T) {
cacheConfig.SnapshotLimit = 0 cacheConfig.SnapshotLimit = 0
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) 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' // 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 ringIndex := number % params.HistoryServeWindow
var key common.Hash var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex) binary.BigEndian.PutUint64(key[24:], ringIndex)
if isVerkle {
return statedb.GetState(params.HistoryStorageAddress, key)
}
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, // - The second block contains a single failing contract creation transaction,
// that fails right off the bat. // 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() gen.SetPoS()
if i == 0 { if i == 0 {
@ -364,8 +371,8 @@ func TestProcessVerkleInvalidContractCreation(t *testing.T) {
if stemStateDiff.SuffixDiffs[0].NewValue == nil { if stemStateDiff.SuffixDiffs[0].NewValue == nil {
t.Fatalf("nil new value in BLOCKHASH contract insert") t.Fatalf("nil new value in BLOCKHASH contract insert")
} }
if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { if *stemStateDiff.SuffixDiffs[0].NewValue != genesisH {
t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, genesisH)
} }
} else { } else {
// For all other entries present in the witness, check that nothing beyond // For all other entries present in the witness, check that nothing beyond
@ -393,8 +400,8 @@ func TestProcessVerkleInvalidContractCreation(t *testing.T) {
if stemStateDiff.SuffixDiffs[0].NewValue == nil { if stemStateDiff.SuffixDiffs[0].NewValue == nil {
t.Fatalf("missing post state value for BLOCKHASH contract at block #2") t.Fatalf("missing post state value for BLOCKHASH contract at block #2")
} }
if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54") { if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() {
t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: 0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54 != %x", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) 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 { } else if suffixDiff.Suffix > 4 {
t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix)
@ -440,7 +447,7 @@ func TestProcessVerkleContractWithEmptyCode(t *testing.T) {
config.ChainID.SetUint64(69421) config.ChainID.SetUint64(69421)
gspec := verkleTestGenesis(&config) 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() gen.SetPoS()
var tx types.Transaction var tx types.Transaction
// a transaction that does some PUSH1n but returns a 0-sized contract // a transaction that does some PUSH1n but returns a 0-sized contract
@ -472,8 +479,8 @@ func TestProcessVerkleContractWithEmptyCode(t *testing.T) {
if stemStateDiff.SuffixDiffs[0].NewValue == nil { if stemStateDiff.SuffixDiffs[0].NewValue == nil {
t.Fatalf("nil new value in BLOCKHASH contract insert") t.Fatalf("nil new value in BLOCKHASH contract insert")
} }
if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { if *stemStateDiff.SuffixDiffs[0].NewValue != genesisH {
t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, genesisH)
} }
} else { } else {
for _, suffixDiff := range stemStateDiff.SuffixDiffs { for _, suffixDiff := range stemStateDiff.SuffixDiffs {
@ -532,7 +539,7 @@ func TestProcessVerkleExtCodeHashOpcode(t *testing.T) {
} }
extCodeHashContractAddr := crypto.CreateAddress(deployer, 1) 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() gen.SetPoS()
if i == 0 { if i == 0 {
@ -605,7 +612,7 @@ func TestProcessVerkleBalanceOpcode(t *testing.T) {
account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d")
gspec = verkleTestGenesis(&config) 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() gen.SetPoS()
txData := slices.Concat( txData := slices.Concat(
[]byte{byte(vm.PUSH20)}, []byte{byte(vm.PUSH20)},
@ -686,7 +693,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) {
deployer := crypto.PubkeyToAddress(testKey.PublicKey) deployer := crypto.PubkeyToAddress(testKey.PublicKey)
contract := crypto.CreateAddress(deployer, 0) 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() gen.SetPoS()
if i == 0 { if i == 0 {
@ -794,7 +801,7 @@ func TestProcessVerkleSelfDestructInSameTx(t *testing.T) {
deployer := crypto.PubkeyToAddress(testKey.PublicKey) deployer := crypto.PubkeyToAddress(testKey.PublicKey)
contract := crypto.CreateAddress(deployer, 0) 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() gen.SetPoS()
tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0,
Value: big.NewInt(42), Value: big.NewInt(42),
@ -897,7 +904,7 @@ func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T)
deployer := crypto.PubkeyToAddress(testKey.PublicKey) deployer := crypto.PubkeyToAddress(testKey.PublicKey)
contract := crypto.CreateAddress(deployer, 0) 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() gen.SetPoS()
if i == 0 { if i == 0 {
// Create self-destruct contract, sending 42 wei. // Create self-destruct contract, sending 42 wei.
@ -977,7 +984,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) {
selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} 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() gen.SetPoS()
tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0,
Value: big.NewInt(42), Value: big.NewInt(42),
@ -1043,7 +1050,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount
selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} 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() gen.SetPoS()
tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0,
Value: big.NewInt(42), Value: big.NewInt(42),

@ -59,6 +59,7 @@ type Contract struct {
// is the execution frame represented by this object a contract deployment // is the execution frame represented by this object a contract deployment
IsDeployment bool IsDeployment bool
IsSystemCall bool
Gas uint64 Gas uint64
value *uint256.Int value *uint256.Int

@ -345,10 +345,12 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
self: AccountRef(addr), self: AccountRef(addr),
} }
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) if !contract.IsSystemCall {
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
scope.Contract.Gas = 0 if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
return nil, ErrOutOfGas scope.Contract.Gas = 0
return nil, ErrOutOfGas
}
} }
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
@ -367,7 +369,7 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
if *pc < codeLen { if *pc < codeLen {
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) 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 // touch next chunk if PUSH1 is at the boundary. if so, *pc has
// advanced past this boundary. // advanced past this boundary.
contractAddr := scope.Contract.Address() 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() contractAddr := scope.Contract.Address()
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) 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) { if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {

@ -165,8 +165,12 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
return evm.interpreter return evm.interpreter
} }
func isSystemCall(caller ContractRef) bool {
return caller.Address() == params.SystemAddress
}
// Call executes the contract associated with the addr with the given input as // Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes // 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 // the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer. // 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) (ret []byte, leftOverGas uint64, err error) {
@ -189,7 +193,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
p, isPrecompile := evm.precompile(addr) p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) { if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP4762 { if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
// add proof of absence to witness // add proof of absence to witness
wgas := evm.AccessEvents.AddAccount(addr, false) wgas := evm.AccessEvents.AddAccount(addr, false)
if gas < wgas { if gas < wgas {
@ -220,6 +224,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// If the account has no code, we can abort here // If the account has no code, we can abort here
// The depth-check is already done, and precompiles handled above // The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.IsSystemCall = isSystemCall(caller)
contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code) contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code)
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas

@ -394,7 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
if transfersValue { if transfersValue {
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
if overflow { 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 { if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if evm.chainRules.IsEIP4762 { if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
address := common.Address(stack.Back(1).Bytes20()) address := common.Address(stack.Back(1).Bytes20())
transfersValue := !stack.Back(2).IsZero() transfersValue := !stack.Back(2).IsZero()
if transfersValue { if transfersValue {

@ -20,6 +20,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "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/stateless"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -98,6 +99,8 @@ type StateDB interface {
Witness() *stateless.Witness Witness() *stateless.Witness
AccessEvents() *state.AccessEvents
// Finalise must be invoked at the end of a transaction // Finalise must be invoked at the end of a transaction
Finalise(bool) Finalise(bool)
} }

@ -233,7 +233,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged, pcCopy, gasCopy = false, pc, contract.Gas logged, pcCopy, gasCopy = false, pc, contract.Gas
} }
if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment { if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
// if the PC ends up in a new "chunk" of verkleized code, charge the // if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs. // associated costs.
contractAddr := contract.Address() contractAddr := contract.Address()

@ -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) { func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if contract.IsSystemCall {
return 0, nil
}
address := stack.peek().Bytes20() address := stack.peek().Bytes20()
gas := evm.AccessEvents.BasicDataGas(address, false) gas := evm.AccessEvents.BasicDataGas(address, false)
if gas == 0 { if gas == 0 {
@ -54,6 +57,9 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if _, isPrecompile := evm.precompile(address); isPrecompile { if _, isPrecompile := evm.precompile(address); isPrecompile {
return 0, nil return 0, nil
} }
if contract.IsSystemCall {
return 0, nil
}
gas := evm.AccessEvents.BasicDataGas(address, false) gas := evm.AccessEvents.BasicDataGas(address, false)
if gas == 0 { if gas == 0 {
gas = params.WarmStorageReadCostEIP2929 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) { func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
if contract.IsSystemCall {
return 0, nil
}
address := stack.peek().Bytes20() address := stack.peek().Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile { if _, isPrecompile := evm.precompile(address); isPrecompile {
return 0, nil return 0, nil
@ -79,6 +88,9 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
if err != nil { if err != nil {
return 0, err return 0, err
} }
if contract.IsSystemCall {
return gas, nil
}
if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile { if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
return gas, nil return gas, nil
} }
@ -102,6 +114,9 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
return 0, nil return 0, nil
} }
if contract.IsSystemCall {
return 0, nil
}
contractAddr := contract.Address() contractAddr := contract.Address()
statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false) statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false)
if contractAddr != beneficiaryAddr { if contractAddr != beneficiaryAddr {
@ -131,7 +146,7 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
uint64CodeOffset = gomath.MaxUint64 uint64CodeOffset = gomath.MaxUint64
} }
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) _, 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) gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
} }
return gas, nil return gas, nil
@ -143,6 +158,9 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if err != nil { if err != nil {
return 0, err return 0, err
} }
if contract.IsSystemCall {
return gas, nil
}
addr := common.Address(stack.peek().Bytes20()) addr := common.Address(stack.peek().Bytes20())
wgas := evm.AccessEvents.BasicDataGas(addr, false) wgas := evm.AccessEvents.BasicDataGas(addr, false)
if wgas == 0 { if wgas == 0 {

Loading…
Cancel
Save