Skip to content

Commit

Permalink
Unified point cache
Browse files Browse the repository at this point in the history
  • Loading branch information
gballet committed Mar 15, 2023
1 parent 2d5ce8a commit 08513ec
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 56 deletions.
26 changes: 6 additions & 20 deletions core/types/access_witness.go → core/state/access_witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package types
package state

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/gballet/go-verkle"
)

type VerkleStem [31]byte
Expand Down Expand Up @@ -51,23 +50,21 @@ type AccessWitness struct {
// block.
InitialValue map[string][]byte

// Caches all the points that correspond to an address,
// so they are not recalculated.
addrToPoint map[string]*verkle.Point

// Caches which code chunks have been accessed, in order
// to reduce the number of times that GetTreeKeyCodeChunk
// is called.
CodeLocations map[string]map[uint64]struct{}

statedb *StateDB
}

func NewAccessWitness() *AccessWitness {
func NewAccessWitness(statedb *StateDB) *AccessWitness {
return &AccessWitness{
Branches: make(map[VerkleStem]Mode),
Chunks: make(map[common.Hash]Mode),
InitialValue: make(map[string][]byte),
addrToPoint: make(map[string]*verkle.Point),
CodeLocations: make(map[string]map[uint64]struct{}),
statedb: statedb,
}
}

Expand Down Expand Up @@ -246,26 +243,15 @@ func (aw *AccessWitness) Copy() *AccessWitness {
Branches: make(map[VerkleStem]Mode),
Chunks: make(map[common.Hash]Mode),
InitialValue: make(map[string][]byte),
addrToPoint: make(map[string]*verkle.Point),
}

naw.Merge(aw)

return naw
}

func (aw *AccessWitness) getTreeKeyHeader(addr []byte) *verkle.Point {
if point, ok := aw.addrToPoint[string(addr)]; ok {
return point
}

point := utils.EvaluateAddressPoint(addr)
aw.addrToPoint[string(addr)] = point
return point
}

func (aw *AccessWitness) GetTreeKeyVersionCached(addr []byte) []byte {
p := aw.getTreeKeyHeader(addr)
p := aw.statedb.db.(*VerkleDB).GetTreeKeyHeader(addr)
v := utils.PointToHash(p, utils.VersionLeafKey)
return v[:]
}
Expand Down
14 changes: 12 additions & 2 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/gballet/go-verkle"
lru "github.com/hashicorp/golang-lru"
)
Expand Down Expand Up @@ -141,6 +142,7 @@ func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
diskdb: db,
codeSizeCache: csc,
codeCache: fastcache.New(codeCacheSize),
addrToPoint: make(utils.PointCache),
}
}
return &cachingDB{
Expand Down Expand Up @@ -241,12 +243,20 @@ type VerkleDB struct {
diskdb ethdb.KeyValueStore
codeSizeCache *lru.Cache
codeCache *fastcache.Cache

// Caches all the points that correspond to an address,
// so they are not recalculated.
addrToPoint utils.PointCache
}

func (db *VerkleDB) GetTreeKeyHeader(addr []byte) *verkle.Point {
return db.addrToPoint.GetTreeKeyHeader(addr)
}

// OpenTrie opens the main account trie.
func (db *VerkleDB) OpenTrie(root common.Hash) (Trie, error) {
if root == (common.Hash{}) || root == emptyRoot {
return trie.NewVerkleTrie(verkle.New(), db.db), nil
return trie.NewVerkleTrie(verkle.New(), db.db, &db.addrToPoint), nil
}
payload, err := db.DiskDB().Get(root[:])
if err != nil {
Expand All @@ -257,7 +267,7 @@ func (db *VerkleDB) OpenTrie(root common.Hash) (Trie, error) {
if err != nil {
panic(err)
}
return trie.NewVerkleTrie(r, db.db), err
return trie.NewVerkleTrie(r, db.db, &db.addrToPoint), err
}

// OpenStorageTrie opens the storage trie of an account.
Expand Down
13 changes: 2 additions & 11 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
"github.com/gballet/go-verkle"
"github.com/holiman/uint256"
)

Expand Down Expand Up @@ -73,8 +72,6 @@ type stateObject struct {
data types.StateAccount
db *StateDB

pointEval *verkle.Point

// DB error.
// State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs
Expand Down Expand Up @@ -115,16 +112,11 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st
if data.Root == (common.Hash{}) {
data.Root = emptyRoot
}
var pointEval *verkle.Point
if db.GetTrie().IsVerkle() {
pointEval = trieUtils.EvaluateAddressPoint(address.Bytes())
}

return &stateObject{
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
pointEval: pointEval,
data: data,
originStorage: make(Storage),
pendingStorage: make(Storage),
Expand Down Expand Up @@ -352,7 +344,7 @@ func (s *stateObject) updateTrie(db Database) Trie {
var v []byte
if (value == common.Hash{}) {
if tr.IsVerkle() {
k := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(s.pointEval, new(uint256.Int).SetBytes(key[:]))
k := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(s.db.db.(*VerkleDB).GetTreeKeyHeader(s.address[:]), new(uint256.Int).SetBytes(key[:]))
s.setError(tr.TryDelete(k))
//s.db.db.TrieDB().DiskDB().Delete(append(s.address[:], key[:]...))
} else {
Expand All @@ -365,9 +357,8 @@ func (s *stateObject) updateTrie(db Database) Trie {
if !tr.IsVerkle() {
s.setError(tr.TryUpdate(key[:], v))
} else {
k := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(s.pointEval, new(uint256.Int).SetBytes(key[:]))
// Update the trie, with v as a value
s.setError(tr.TryUpdate(k, value[:]))
s.setError(tr.TryUpdate(key[:], value[:]))
}
s.db.StorageUpdated += 1
}
Expand Down
12 changes: 6 additions & 6 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ type StateDB struct {
// Per-transaction access list
accessList *accessList

witness *types.AccessWitness
witness *AccessWitness

// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
Expand Down Expand Up @@ -155,7 +155,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
hasher: crypto.NewKeccakState(),
}
if tr.IsVerkle() {
sdb.witness = types.NewAccessWitness()
sdb.witness = NewAccessWitness(sdb)
if sdb.snaps == nil {
snapconfig := snapshot.Config{
CacheSize: 256,
Expand Down Expand Up @@ -184,14 +184,14 @@ func (s *StateDB) Snaps() *snapshot.Tree {
return s.snaps
}

func (s *StateDB) Witness() *types.AccessWitness {
func (s *StateDB) Witness() *AccessWitness {
if s.witness == nil {
s.witness = types.NewAccessWitness()
s.witness = NewAccessWitness(s)
}
return s.witness
}

func (s *StateDB) SetWitness(aw *types.AccessWitness) {
func (s *StateDB) SetWitness(aw *AccessWitness) {
s.witness = aw
}

Expand Down Expand Up @@ -532,7 +532,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
groupOffset := (chunknr + 128) % 256
if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
values = make([][]byte, verkle.NodeWidth)
key = utils.GetTreeKeyCodeChunkWithEvaluatedAddress(obj.pointEval, uint256.NewInt(chunknr))
key = utils.GetTreeKeyCodeChunkWithEvaluatedAddress(obj.db.db.(*VerkleDB).GetTreeKeyHeader(obj.address[:]), uint256.NewInt(chunknr))
}
values[groupOffset] = chunks[i : i+32]

Expand Down
2 changes: 1 addition & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, author *com
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
if config.IsCancun(blockNumber) {
txContext.Accesses = types.NewAccessWitness()
txContext.Accesses = state.NewAccessWitness(statedb)
}
evm.Reset(txContext, statedb)

Expand Down
12 changes: 6 additions & 6 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
Expand Down Expand Up @@ -86,7 +86,7 @@ type TxContext struct {
Origin common.Address // Provides information for ORIGIN
GasPrice *big.Int // Provides information for GASPRICE

Accesses *types.AccessWitness
Accesses *state.AccessWitness
}

// EVM is the Ethereum Virtual Machine base object and provides
Expand Down Expand Up @@ -129,9 +129,6 @@ type EVM struct {
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
if txCtx.Accesses == nil && chainConfig.IsCancun(blockCtx.BlockNumber) {
txCtx.Accesses = types.NewAccessWitness()
}
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
Expand All @@ -140,6 +137,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
}
if txCtx.Accesses == nil && chainConfig.IsCancun(blockCtx.BlockNumber) {
txCtx.Accesses = state.NewAccessWitness(evm.StateDB.(*state.StateDB))
}
evm.interpreter = NewEVMInterpreter(evm, config)
return evm
}
Expand All @@ -148,7 +148,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
// This is not threadsafe and should only be done very cautiously.
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
if txCtx.Accesses == nil && evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
txCtx.Accesses = types.NewAccessWitness()
txCtx.Accesses = state.NewAccessWitness(evm.StateDB.(*state.StateDB))
}
evm.TxContext = txCtx
evm.StateDB = statedb
Expand Down
5 changes: 3 additions & 2 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
Expand Down Expand Up @@ -383,7 +384,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}

// touchChunkOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
func touchChunkOnReadAndChargeGas(chunks trie.ChunkedCode, offset uint64, evals [][]byte, code []byte, accesses *types.AccessWitness, deployment bool) uint64 {
func touchChunkOnReadAndChargeGas(chunks trie.ChunkedCode, offset uint64, evals [][]byte, code []byte, accesses *state.AccessWitness, deployment bool) uint64 {
// note that in the case where the executed 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 @@ -413,7 +414,7 @@ func touchChunkOnReadAndChargeGas(chunks trie.ChunkedCode, offset uint64, evals
}

// touchEachChunksOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
func touchEachChunksOnReadAndChargeGas(offset, size uint64, contract *Contract, code []byte, accesses *types.AccessWitness, deployment bool) uint64 {
func touchEachChunksOnReadAndChargeGas(offset, size uint64, contract *Contract, code []byte, accesses *state.AccessWitness, deployment bool) uint64 {
// 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
5 changes: 3 additions & 2 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/types"
)

Expand Down Expand Up @@ -78,8 +79,8 @@ type StateDB interface {

ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error

Witness() *types.AccessWitness
SetWitness(*types.AccessWitness)
Witness() *state.AccessWitness
SetWitness(*state.AccessWitness)
}

// CallContext provides a basic interface for the EVM calling conventions. The EVM
Expand Down
12 changes: 12 additions & 0 deletions trie/utils/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ var (
getTreePolyIndex0Point *verkle.Point
)

type PointCache map[string]*verkle.Point

func (pc PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point {
if point, ok := pc[string(addr)]; ok {
return point
}

point := EvaluateAddressPoint(addr)
pc[string(addr)] = point
return point
}

func init() {
// The byte array is the Marshalled output of the point computed as such:
//cfg, _ := verkle.GetConfig()
Expand Down
16 changes: 10 additions & 6 deletions trie/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,26 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/gballet/go-verkle"
"github.com/holiman/uint256"
)

// VerkleTrie is a wrapper around VerkleNode that implements the trie.Trie
// interface so that Verkle trees can be reused verbatim.
type VerkleTrie struct {
root verkle.VerkleNode
db *Database
root verkle.VerkleNode
db *Database
pointCache *utils.PointCache
}

func (vt *VerkleTrie) ToDot() string {
return verkle.ToDot(vt.root)
}

func NewVerkleTrie(root verkle.VerkleNode, db *Database) *VerkleTrie {
func NewVerkleTrie(root verkle.VerkleNode, db *Database, pointCache *utils.PointCache) *VerkleTrie {
return &VerkleTrie{
root: root,
db: db,
root: root,
db: db,
pointCache: pointCache,
}
}

Expand Down Expand Up @@ -175,9 +178,10 @@ func (trie *VerkleTrie) TryUpdateStem(key []byte, values [][]byte) error {
// by the caller while they are stored in the trie. If a node was not found in the
// database, a trie.MissingNodeError is returned.
func (trie *VerkleTrie) TryUpdate(key, value []byte) error {
k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(trie.pointCache.GetTreeKeyHeader(key), new(uint256.Int).SetBytes(key[:]))
var v [32]byte
copy(v[:], value[:])
return trie.root.Insert(key, v[:], func(h []byte) ([]byte, error) {
return trie.root.Insert(k, v[:], func(h []byte) ([]byte, error) {
return trie.db.diskdb.Get(h)
})
}
Expand Down

0 comments on commit 08513ec

Please sign in to comment.