Skip to content

Commit

Permalink
turn access witness into an access list
Browse files Browse the repository at this point in the history
  • Loading branch information
gballet committed Feb 23, 2024
1 parent 31f522b commit 3e1aa9a
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 32 deletions.
82 changes: 72 additions & 10 deletions core/state/access_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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 }
5 changes: 3 additions & 2 deletions core/state/access_witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand All @@ -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),
Expand Down
34 changes: 29 additions & 5 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
14 changes: 7 additions & 7 deletions core/state/statedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down Expand Up @@ -947,21 +947,21 @@ 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
// Make a copy
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)
}
}
Expand Down
8 changes: 4 additions & 4 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3e1aa9a

Please sign in to comment.