699243f8ae
Verkle trees store the code inside the trie. This PR changes the interface to pass the code, as well as the dirty flag to tell the trie package if the code is dirty and needs to be updated. This is a no-op for the MPT and the odr trie.
248 lines
9.2 KiB
Go
248 lines
9.2 KiB
Go
// Copyright 2017 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// 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 state
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/lru"
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
"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/trienode"
|
|
)
|
|
|
|
const (
|
|
// Number of codehash->size associations to keep.
|
|
codeSizeCacheSize = 100000
|
|
|
|
// Cache size granted for caching clean code.
|
|
codeCacheSize = 64 * 1024 * 1024
|
|
)
|
|
|
|
// Database wraps access to tries and contract code.
|
|
type Database interface {
|
|
// OpenTrie opens the main account trie.
|
|
OpenTrie(root common.Hash) (Trie, error)
|
|
|
|
// OpenStorageTrie opens the storage trie of an account.
|
|
OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error)
|
|
|
|
// CopyTrie returns an independent copy of the given trie.
|
|
CopyTrie(Trie) Trie
|
|
|
|
// ContractCode retrieves a particular contract's code.
|
|
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
|
|
|
|
// ContractCodeSize retrieves a particular contracts code's size.
|
|
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
|
|
|
|
// DiskDB returns the underlying key-value disk database.
|
|
DiskDB() ethdb.KeyValueStore
|
|
|
|
// TrieDB retrieves the low level trie database used for data storage.
|
|
TrieDB() *trie.Database
|
|
}
|
|
|
|
// Trie is a Ethereum Merkle Patricia trie.
|
|
type Trie interface {
|
|
// GetKey returns the sha3 preimage of a hashed key that was previously used
|
|
// to store a value.
|
|
//
|
|
// TODO(fjl): remove this when StateTrie is removed
|
|
GetKey([]byte) []byte
|
|
|
|
// GetStorage returns the value for key stored in the trie. The value bytes
|
|
// must not be modified by the caller. If a node was not found in the database,
|
|
// a trie.MissingNodeError is returned.
|
|
GetStorage(addr common.Address, key []byte) ([]byte, error)
|
|
|
|
// GetAccount abstracts an account read from the trie. It retrieves the
|
|
// account blob from the trie with provided account address and decodes it
|
|
// with associated decoding algorithm. If the specified account is not in
|
|
// the trie, nil will be returned. If the trie is corrupted(e.g. some nodes
|
|
// are missing or the account blob is incorrect for decoding), an error will
|
|
// be returned.
|
|
GetAccount(address common.Address) (*types.StateAccount, error)
|
|
|
|
// UpdateStorage associates key with value in the trie. If value has length zero,
|
|
// any existing value is deleted from the trie. The value bytes must not be modified
|
|
// by the caller while they are stored in the trie. If a node was not found in the
|
|
// database, a trie.MissingNodeError is returned.
|
|
UpdateStorage(addr common.Address, key, value []byte) error
|
|
|
|
// UpdateAccount abstracts an account write to the trie. It encodes the
|
|
// provided account object with associated algorithm and then updates it
|
|
// in the trie with provided address.
|
|
UpdateAccount(address common.Address, account *types.StateAccount) error
|
|
|
|
// UpdateContractCode abstracts code write to the trie. It is expected
|
|
// to be moved to the stateWriter interface when the latter is ready.
|
|
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
|
|
|
|
// DeleteStorage removes any existing value for key from the trie. If a node
|
|
// was not found in the database, a trie.MissingNodeError is returned.
|
|
DeleteStorage(addr common.Address, key []byte) error
|
|
|
|
// DeleteAccount abstracts an account deletion from the trie.
|
|
DeleteAccount(address common.Address) error
|
|
|
|
// Hash returns the root hash of the trie. It does not write to the database and
|
|
// can be used even if the trie doesn't have one.
|
|
Hash() common.Hash
|
|
|
|
// Commit collects all dirty nodes in the trie and replace them with the
|
|
// corresponding node hash. All collected nodes(including dirty leaves if
|
|
// collectLeaf is true) will be encapsulated into a nodeset for return.
|
|
// The returned nodeset can be nil if the trie is clean(nothing to commit).
|
|
// Once the trie is committed, it's not usable anymore. A new trie must
|
|
// be created with new root and updated trie database for following usage
|
|
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet)
|
|
|
|
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
|
|
// starts at the key after the given start key. And error will be returned
|
|
// if fails to create node iterator.
|
|
NodeIterator(startKey []byte) (trie.NodeIterator, error)
|
|
|
|
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
|
|
// on the path to the value at key. The value itself is also included in the last
|
|
// node and can be retrieved by verifying the proof.
|
|
//
|
|
// If the trie does not contain a value for key, the returned proof contains all
|
|
// nodes of the longest existing prefix of the key (at least the root), ending
|
|
// with the node that proves the absence of the key.
|
|
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
|
|
}
|
|
|
|
// NewDatabase creates a backing store for state. The returned database is safe for
|
|
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
|
// historical state in memory, use the NewDatabaseWithConfig constructor.
|
|
func NewDatabase(db ethdb.Database) Database {
|
|
return NewDatabaseWithConfig(db, nil)
|
|
}
|
|
|
|
// NewDatabaseWithConfig creates a backing store for state. The returned database
|
|
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
|
// large memory cache.
|
|
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
|
|
return &cachingDB{
|
|
disk: db,
|
|
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
triedb: trie.NewDatabaseWithConfig(db, config),
|
|
}
|
|
}
|
|
|
|
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
|
|
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
|
|
return &cachingDB{
|
|
disk: db,
|
|
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
triedb: triedb,
|
|
}
|
|
}
|
|
|
|
type cachingDB struct {
|
|
disk ethdb.KeyValueStore
|
|
codeSizeCache *lru.Cache[common.Hash, int]
|
|
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
|
triedb *trie.Database
|
|
}
|
|
|
|
// OpenTrie opens the main account trie at a specific root hash.
|
|
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
|
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tr, nil
|
|
}
|
|
|
|
// OpenStorageTrie opens the storage trie of an account.
|
|
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) {
|
|
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, addrHash, root), db.triedb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tr, nil
|
|
}
|
|
|
|
// CopyTrie returns an independent copy of the given trie.
|
|
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
|
switch t := t.(type) {
|
|
case *trie.StateTrie:
|
|
return t.Copy()
|
|
default:
|
|
panic(fmt.Errorf("unknown trie type %T", t))
|
|
}
|
|
}
|
|
|
|
// ContractCode retrieves a particular contract's code.
|
|
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
|
|
code, _ := db.codeCache.Get(codeHash)
|
|
if len(code) > 0 {
|
|
return code, nil
|
|
}
|
|
code = rawdb.ReadCode(db.disk, codeHash)
|
|
if len(code) > 0 {
|
|
db.codeCache.Add(codeHash, code)
|
|
db.codeSizeCache.Add(codeHash, len(code))
|
|
return code, nil
|
|
}
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// ContractCodeWithPrefix retrieves a particular contract's code. If the
|
|
// code can't be found in the cache, then check the existence with **new**
|
|
// db scheme.
|
|
func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) {
|
|
code, _ := db.codeCache.Get(codeHash)
|
|
if len(code) > 0 {
|
|
return code, nil
|
|
}
|
|
code = rawdb.ReadCodeWithPrefix(db.disk, codeHash)
|
|
if len(code) > 0 {
|
|
db.codeCache.Add(codeHash, code)
|
|
db.codeSizeCache.Add(codeHash, len(code))
|
|
return code, nil
|
|
}
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
// ContractCodeSize retrieves a particular contracts code's size.
|
|
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
|
|
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
|
return cached, nil
|
|
}
|
|
code, err := db.ContractCode(addrHash, codeHash)
|
|
return len(code), err
|
|
}
|
|
|
|
// DiskDB returns the underlying key-value disk database.
|
|
func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
|
|
return db.disk
|
|
}
|
|
|
|
// TrieDB retrieves any intermediate trie-node caching layer.
|
|
func (db *cachingDB) TrieDB() *trie.Database {
|
|
return db.triedb
|
|
}
|