From 3e1aa9a3a990b84a4695a37ec245b73134b85ace Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:34:54 +0100 Subject: [PATCH] turn access witness into an access list --- core/state/access_list.go | 82 +++++++++++++++++++++++++++++++----- core/state/access_witness.go | 5 ++- core/state/statedb.go | 34 ++++++++++++--- core/state/statedb_test.go | 14 +++--- core/vm/evm.go | 8 ++-- core/vm/instructions.go | 5 ++- core/vm/interface.go | 4 +- 7 files changed, 120 insertions(+), 32 deletions(-) diff --git a/core/state/access_list.go b/core/state/access_list.go index 419469134595..1e0cf5cf2ed2 100644 --- a/core/state/access_list.go +++ b/core/state/access_list.go @@ -18,22 +18,45 @@ package state import ( "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) -type accessList struct { +type AccessList interface { + ContainsAddress(address common.Address) bool + Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) + Copy() AccessList + AddAddress(address common.Address) bool + AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) + DeleteSlot(address common.Address, slot common.Hash) + DeleteAddress(address common.Address) + + TouchAndChargeProofOfAbsence(addr []byte) uint64 + TouchAndChargeMessageCall(addr []byte) uint64 + TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 + TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 + TouchAndChargeContractCreateCompleted(addr []byte) uint64 + TouchTxOriginAndComputeGas(originAddr []byte) uint64 + TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 + TouchAddressOnWriteAndComputeGas(addr []byte, index uint256.Int, suffix byte) uint64 + TouchAddressOnReadAndComputeGas(addr []byte, index uint256.Int, suffix byte) uint64 + Merge(AccessList) + Keys() [][]byte +} + +type accessList2929 struct { addresses map[common.Address]int slots []map[common.Hash]struct{} } // ContainsAddress returns true if the address is in the access list. -func (al *accessList) ContainsAddress(address common.Address) bool { +func (al *accessList2929) ContainsAddress(address common.Address) bool { _, ok := al.addresses[address] return ok } // Contains checks if a slot within an account is present in the access list, returning // separate flags for the presence of the account and the slot respectively. -func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { +func (al *accessList2929) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { idx, ok := al.addresses[address] if !ok { // no such address (and hence zero slots) @@ -48,14 +71,14 @@ func (al *accessList) Contains(address common.Address, slot common.Hash) (addres } // newAccessList creates a new accessList. -func newAccessList() *accessList { - return &accessList{ +func newAccessList() *accessList2929 { + return &accessList2929{ addresses: make(map[common.Address]int), } } // Copy creates an independent copy of an accessList. -func (a *accessList) Copy() *accessList { +func (a *accessList2929) Copy() AccessList { cp := newAccessList() for k, v := range a.addresses { cp.addresses[k] = v @@ -73,7 +96,7 @@ func (a *accessList) Copy() *accessList { // AddAddress adds an address to the access list, and returns 'true' if the operation // caused a change (addr was not previously in the list). -func (al *accessList) AddAddress(address common.Address) bool { +func (al *accessList2929) AddAddress(address common.Address) bool { if _, present := al.addresses[address]; present { return false } @@ -86,7 +109,7 @@ func (al *accessList) AddAddress(address common.Address) bool { // - address added // - slot added // For any 'true' value returned, a corresponding journal entry must be made. -func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { +func (al *accessList2929) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { idx, addrPresent := al.addresses[address] if !addrPresent || idx == -1 { // Address not present, or addr present but no slots there @@ -110,7 +133,7 @@ func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrCha // This operation needs to be performed in the same order as the addition happened. // This method is meant to be used by the journal, which maintains ordering of // operations. -func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { +func (al *accessList2929) DeleteSlot(address common.Address, slot common.Hash) { idx, addrOk := al.addresses[address] // There are two ways this can fail if !addrOk { @@ -131,6 +154,45 @@ func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { // needs to be performed in the same order as the addition happened. // This method is meant to be used by the journal, which maintains ordering of // operations. -func (al *accessList) DeleteAddress(address common.Address) { +func (al *accessList2929) DeleteAddress(address common.Address) { delete(al.addresses, address) } + +func (al *accessList2929) TouchAndChargeProofOfAbsence(addr []byte) uint64 { + return 0 +} + +func (al *accessList2929) TouchAndChargeMessageCall(addr []byte) uint64 { + return 0 +} + +func (al *accessList2929) TouchAndChargeValueTransfer(callerAddr []byte, targetAddr []byte) uint64 { + return 0 +} + +func (al *accessList2929) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 { + return 0 +} + +func (al *accessList2929) TouchAndChargeContractCreateCompleted(addr []byte) uint64 { + return 0 +} + +func (al *accessList2929) TouchTxOriginAndComputeGas(originAddr []byte) uint64 { + return 0 +} + +func (al *accessList2929) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 { + return 0 +} + +func (al *accessList2929) TouchAddressOnWriteAndComputeGas(addr []byte, index uint256.Int, subIndex byte) uint64 { + return 0 +} + +func (al *accessList2929) TouchAddressOnReadAndComputeGas(addr []byte, index uint256.Int, subIndex byte) uint64 { + return 0 +} + +func (al *accessList2929) Merge(other AccessList) {} +func (al *accessList2929) Keys() [][]byte { return nil } diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 7eb0990b645c..14fbeec59e3e 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -56,7 +56,8 @@ func NewAccessWitness(pointCache *utils.PointCache) *AccessWitness { // Merge is used to merge the witness that got generated during the execution // of a tx, with the accumulation of witnesses that were generated during the // execution of all the txs preceding this one in a given block. -func (aw *AccessWitness) Merge(other *AccessWitness) { +func (aw *AccessWitness) Merge(o AccessList) { + other := o.(*AccessWitness) for k := range other.branches { aw.branches[k] |= other.branches[k] } @@ -78,7 +79,7 @@ func (aw *AccessWitness) Keys() [][]byte { return keys } -func (aw *AccessWitness) Copy() *AccessWitness { +func (aw *AccessWitness) Copy() AccessList { naw := &AccessWitness{ branches: make(map[branchAccessKey]mode), chunks: make(map[chunkAccessKey]mode), diff --git a/core/state/statedb.go b/core/state/statedb.go index c1a874c1b295..697cd8ca5842 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -113,13 +113,13 @@ type StateDB struct { preimages map[common.Hash][]byte // Per-transaction access list - accessList *accessList + accessList AccessList // Transient storage transientStorage transientStorage // Verkle witness - witness *AccessWitness + witness AccessList // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. @@ -188,18 +188,18 @@ func (s *StateDB) Snaps() *snapshot.Tree { return s.snaps } -func (s *StateDB) NewAccessWitness() *AccessWitness { +func (s *StateDB) NewAccessWitness() AccessList { return NewAccessWitness(s.db.(*cachingDB).addrToPoint) } -func (s *StateDB) Witness() *AccessWitness { +func (s *StateDB) Witness() AccessList { if s.witness == nil { s.witness = s.NewAccessWitness() } return s.witness } -func (s *StateDB) SetWitness(aw *AccessWitness) { +func (s *StateDB) SetWitness(aw AccessList) { s.witness = aw } @@ -1461,3 +1461,27 @@ func copy2DSet[k comparable](set map[k]map[common.Hash][]byte) map[k]map[common. } return copied } + +func (aw *AccessWitness) ContainsAddress(address common.Address) bool { + panic("not implemented") // TODO: Implement +} + +func (aw *AccessWitness) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + panic("not implemented") // TODO: Implement +} + +func (aw *AccessWitness) AddAddress(address common.Address) bool { + panic("not implemented") // TODO: Implement +} + +func (aw *AccessWitness) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { + panic("not implemented") // TODO: Implement +} + +func (aw *AccessWitness) DeleteSlot(address common.Address, slot common.Hash) { + panic("not implemented") // TODO: Implement +} + +func (aw *AccessWitness) DeleteAddress(address common.Address) { + panic("not implemented") // TODO: Implement +} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index e6479076fa98..fdf2a0e19047 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -823,7 +823,7 @@ func TestStateDBAccessList(t *testing.T) { } } // Check that only the expected addresses are present in the access list - for address := range state.accessList.addresses { + for address := range state.accessList.(*accessList2929).addresses { if _, exist := addressMap[address]; !exist { t.Fatalf("extra address %x in access list", address) } @@ -849,9 +849,9 @@ func TestStateDBAccessList(t *testing.T) { } } // Check that no extra elements are in the access list - index := state.accessList.addresses[address] + index := state.accessList.(*accessList2929).addresses[address] if index >= 0 { - stateSlots := state.accessList.slots[index] + stateSlots := state.accessList.(*accessList2929).slots[index] for s := range stateSlots { if _, slotPresent := slotMap[s]; !slotPresent { t.Fatalf("scope has extra slot %v (address %v)", s, addrString) @@ -947,10 +947,10 @@ func TestStateDBAccessList(t *testing.T) { if state.AddressInAccessList(addr("aa")) { t.Fatalf("addr present, expected missing") } - if got, exp := len(state.accessList.addresses), 0; got != exp { + if got, exp := len(state.accessList.(*accessList2929).addresses), 0; got != exp { t.Fatalf("expected empty, got %d", got) } - if got, exp := len(state.accessList.slots), 0; got != exp { + if got, exp := len(state.accessList.(*accessList2929).slots), 0; got != exp { t.Fatalf("expected empty, got %d", got) } // Check the copy @@ -958,10 +958,10 @@ func TestStateDBAccessList(t *testing.T) { state = stateCopy1 verifyAddrs("aa", "bb") verifySlots("bb", "01", "02") - if got, exp := len(state.accessList.addresses), 2; got != exp { + if got, exp := len(state.accessList.(*accessList2929).addresses), 2; got != exp { t.Fatalf("expected empty, got %d", got) } - if got, exp := len(state.accessList.slots), 1; got != exp { + if got, exp := len(state.accessList.(*accessList2929).slots), 1; got != exp { t.Fatalf("expected empty, got %d", got) } } diff --git a/core/vm/evm.go b/core/vm/evm.go index bcd5248bb72c..28a3e6d9146c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -84,10 +84,10 @@ type BlockContext struct { // All fields can change between transactions. type TxContext struct { // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE - BlobHashes []common.Hash // Provides information for BLOBHASH - Accesses *state.AccessWitness // Capture all state accesses for this tx + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE + BlobHashes []common.Hash // Provides information for BLOBHASH + Accesses state.AccessList // Capture all state accesses for this tx } // EVM is the Ethereum Virtual Machine base object and provides diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 7fb340da7b24..d5d0e70a1225 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -390,7 +390,8 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } // touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs -func touchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, accesses *state.AccessWitness) uint64 { +func touchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, al state.AccessList) uint64 { + accesses := al.(*state.AccessWitness) // note that in the case where the copied code is outside the range of the // contract code but touches the last leaf with contract code in it, // we don't include the last leaf of code in the AccessWitness. The @@ -499,7 +500,7 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ return nil, nil } -func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.AccessWitness) common.Hash { +func getBlockHashFromContract(number uint64, statedb StateDB, witness state.AccessList) common.Hash { var pnum common.Hash binary.BigEndian.PutUint64(pnum[24:], number) witness.TouchAddressOnReadAndComputeGas(params.HistoryStorageAddress[:], *uint256.NewInt(number / 256), byte(number&0xFF)) diff --git a/core/vm/interface.go b/core/vm/interface.go index 0a02a0181c05..eae40c083094 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -80,8 +80,8 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) - Witness() *state.AccessWitness - SetWitness(*state.AccessWitness) + Witness() state.AccessList + SetWitness(state.AccessList) } // CallContext provides a basic interface for the EVM calling conventions. The EVM