Skip to content

Commit

Permalink
handle predicates outside of core/state.StateDB
Browse files Browse the repository at this point in the history
  • Loading branch information
darioush committed Sep 24, 2024
1 parent e0c04bd commit a165f1f
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 501 deletions.
448 changes: 448 additions & 0 deletions core/contracts_stateful_test.go

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/consensus/misc/eip4844"
"github.com/ava-labs/coreth/core/extstate"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
Expand All @@ -44,9 +45,9 @@ import (
func init() {
vm.StateDbHook = func(rules params.Rules, db vm.StateDB) vm.StateDB {
if rules.IsApricotPhase1 {
return &StateDbAP1{db}
db = &StateDbAP1{db}
}
return db
return &extstate.StateDB{StateDB: db}
}
}

Expand Down
54 changes: 54 additions & 0 deletions core/extstate/statedb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package extstate

import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/predicate"
"github.com/ethereum/go-ethereum/common"
)

type StateDB struct {
vm.StateDB

// Ordered storage slots to be used in predicate verification as set in the tx access list.
// Only set in PrepareAccessList, and un-modified through execution.
predicateStorageSlots map[common.Address][][]byte
}

func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
s.predicateStorageSlots = predicate.PreparePredicateStorageSlots(rules, list)
s.StateDB.Prepare(rules, sender, coinbase, dst, precompiles, list)
}

// GetPredicateStorageSlots returns the storage slots associated with the address, index pair.
// A list of access tuples can be included within transaction types post EIP-2930. The address
// is declared directly on the access tuple and the index is the i'th occurrence of an access
// tuple with the specified address.
//
// Ex. AccessList[[AddrA, Predicate1], [AddrB, Predicate2], [AddrA, Predicate3]]
// In this case, the caller could retrieve predicates 1-3 with the following calls:
// GetPredicateStorageSlots(AddrA, 0) -> Predicate1
// GetPredicateStorageSlots(AddrB, 0) -> Predicate2
// GetPredicateStorageSlots(AddrA, 1) -> Predicate3
func (s *StateDB) GetPredicateStorageSlots(address common.Address, index int) ([]byte, bool) {
predicates, exists := s.predicateStorageSlots[address]
if !exists {
return nil, false
}
if index >= len(predicates) {
return nil, false
}
return predicates[index], true
}

// SetPredicateStorageSlots sets the predicate storage slots for the given address
func (s *StateDB) SetPredicateStorageSlots(address common.Address, predicates [][]byte) {
if s.predicateStorageSlots == nil {
s.predicateStorageSlots = make(map[common.Address][][]byte)
}
s.predicateStorageSlots[address] = predicates
}
7 changes: 4 additions & 3 deletions core/state/test_statedb.go → core/extstate/test_statedb.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package state
package extstate

import (
"testing"

"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/precompile/contract"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)

func NewTestStateDB(t testing.TB) contract.StateDB {
db := rawdb.NewMemoryDatabase()
stateDB, err := New(common.Hash{}, NewDatabase(db), nil)
stateDB, err := state.New(common.Hash{}, state.NewDatabase(db), nil)
require.NoError(t, err)
return stateDB
return &StateDB{StateDB: stateDB}
}
81 changes: 17 additions & 64 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/metrics"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/predicate"
"github.com/ava-labs/coreth/trie"
"github.com/ava-labs/coreth/trie/trienode"
"github.com/ava-labs/coreth/trie/triestate"
Expand Down Expand Up @@ -118,9 +117,6 @@ type StateDB struct {

// Per-transaction access list
accessList *accessList
// Ordered storage slots to be used in predicate verification as set in the tx access list.
// Only set in PrepareAccessList, and un-modified through execution.
predicateStorageSlots map[common.Address][][]byte

// Transient storage
transientStorage transientStorage
Expand Down Expand Up @@ -173,24 +169,23 @@ func NewWithSnapshot(root common.Hash, db Database, snap snapshot.Snapshot) (*St
return nil, err
}
sdb := &StateDB{
db: db,
trie: tr,
originalRoot: root,
accounts: make(map[common.Hash][]byte),
storages: make(map[common.Hash]map[common.Hash][]byte),
accountsOrigin: make(map[common.Address][]byte),
storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
stateObjects: make(map[common.Address]*stateObject),
stateObjectsPending: make(map[common.Address]struct{}),
stateObjectsDirty: make(map[common.Address]struct{}),
stateObjectsDestruct: make(map[common.Address]*types.StateAccount),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
predicateStorageSlots: make(map[common.Address][][]byte),
accessList: newAccessList(),
transientStorage: newTransientStorage(),
hasher: crypto.NewKeccakState(),
db: db,
trie: tr,
originalRoot: root,
accounts: make(map[common.Hash][]byte),
storages: make(map[common.Hash]map[common.Hash][]byte),
accountsOrigin: make(map[common.Address][]byte),
storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
stateObjects: make(map[common.Address]*stateObject),
stateObjectsPending: make(map[common.Address]struct{}),
stateObjectsDirty: make(map[common.Address]struct{}),
stateObjectsDestruct: make(map[common.Address]*types.StateAccount),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
accessList: newAccessList(),
transientStorage: newTransientStorage(),
hasher: crypto.NewKeccakState(),
}
if snap != nil {
if snap.Root() != root {
Expand Down Expand Up @@ -771,19 +766,6 @@ func (s *StateDB) CreateAccount(addr common.Address) {
}
}

// copyPredicateStorageSlots creates a deep copy of the provided predicateStorageSlots map.
func copyPredicateStorageSlots(predicateStorageSlots map[common.Address][][]byte) map[common.Address][][]byte {
res := make(map[common.Address][][]byte, len(predicateStorageSlots))
for address, predicates := range predicateStorageSlots {
copiedPredicates := make([][]byte, len(predicates))
for i, predicateBytes := range predicates {
copiedPredicates[i] = common.CopyBytes(predicateBytes)
}
res[address] = copiedPredicates
}
return res
}

// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
func (s *StateDB) Copy() *StateDB {
Expand Down Expand Up @@ -877,7 +859,6 @@ func (s *StateDB) Copy() *StateDB {
// in the middle of a transaction.
state.accessList = s.accessList.Copy()
state.transientStorage = s.transientStorage.Copy()
state.predicateStorageSlots = copyPredicateStorageSlots(s.predicateStorageSlots)

// If there's a prefetcher running, make an inactive copy of it that can
// only access data but does not actively preload (since the user will not
Expand Down Expand Up @@ -1442,8 +1423,6 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
if rules.IsDurango { // EIP-3651: warm coinbase
al.AddAddress(coinbase)
}

s.predicateStorageSlots = predicate.PreparePredicateStorageSlots(rules, list)
}
// Reset transient storage at the beginning of transaction execution
s.transientStorage = newTransientStorage()
Expand Down Expand Up @@ -1489,27 +1468,6 @@ func (s *StateDB) GetTxHash() common.Hash {
return s.thash
}

// GetPredicateStorageSlots returns the storage slots associated with the address, index pair.
// A list of access tuples can be included within transaction types post EIP-2930. The address
// is declared directly on the access tuple and the index is the i'th occurrence of an access
// tuple with the specified address.
//
// Ex. AccessList[[AddrA, Predicate1], [AddrB, Predicate2], [AddrA, Predicate3]]
// In this case, the caller could retrieve predicates 1-3 with the following calls:
// GetPredicateStorageSlots(AddrA, 0) -> Predicate1
// GetPredicateStorageSlots(AddrB, 0) -> Predicate2
// GetPredicateStorageSlots(AddrA, 1) -> Predicate3
func (s *StateDB) GetPredicateStorageSlots(address common.Address, index int) ([]byte, bool) {
predicates, exists := s.predicateStorageSlots[address]
if !exists {
return nil, false
}
if index >= len(predicates) {
return nil, false
}
return predicates[index], true
}

// convertAccountSet converts a provided account set from address keyed to hash keyed.
func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount) map[common.Hash]struct{} {
ret := make(map[common.Hash]struct{}, len(set))
Expand All @@ -1524,11 +1482,6 @@ func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount)
return ret
}

// SetPredicateStorageSlots sets the predicate storage slots for the given address
func (s *StateDB) SetPredicateStorageSlots(address common.Address, predicates [][]byte) {
s.predicateStorageSlots[address] = predicates
}

// copySet returns a deep-copied set.
func copySet[k comparable](set map[k][]byte) map[k][]byte {
copied := make(map[k][]byte, len(set))
Expand Down
4 changes: 3 additions & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"math/big"

"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core/extstate"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
Expand Down Expand Up @@ -242,7 +243,8 @@ func ApplyPrecompileActivations(c *params.ChainConfig, parentTimestamp *uint64,
// can be called from within Solidity contracts. Solidity adds a check before invoking a contract to ensure
// that it does not attempt to invoke a non-existent contract.
statedb.SetCode(module.Address, []byte{0x1})
if err := module.Configure(c, activatingConfig, statedb, blockContext); err != nil {
extstatedb := &extstate.StateDB{StateDB: statedb}
if err := module.Configure(c, activatingConfig, extstatedb, blockContext); err != nil {
return fmt.Errorf("could not configure precompile, name: %s, reason: %w", module.ConfigKey, err)
}
}
Expand Down
Loading

0 comments on commit a165f1f

Please sign in to comment.