core, trie: expose batch trie getters into the statedb

This commit is contained in:
Péter Szilágyi 2024-05-08 14:38:30 +03:00
parent 8fedd9f9e0
commit 9a4e5db197
3 changed files with 110 additions and 0 deletions

View file

@ -87,11 +87,21 @@ type Trie interface {
// be returned. // be returned.
GetAccount(address common.Address) (*types.StateAccount, error) GetAccount(address common.Address) (*types.StateAccount, error)
// GetAccountBatch is a batched version of GetAccount that simultaneously looks
// up multiple slots. The advantage vs. the singleton version is the potential
// for concurrent disk lookups.
GetAccountBatch(addrs []common.Address) ([]*types.StateAccount, error)
// GetStorage returns the value for key stored in the trie. The value bytes // 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, // must not be modified by the caller. If a node was not found in the database,
// a trie.MissingNodeError is returned. // a trie.MissingNodeError is returned.
GetStorage(addr common.Address, key []byte) ([]byte, error) GetStorage(addr common.Address, key []byte) ([]byte, error)
// GetStorageBatch is a batched version of GetStorage that simultaneously looks
// up multiple slots. The advantage vs. teh singleton version is the potential
// for concurrent disk lookups.
GetStorageBatch(addrs []common.Address, keys [][]byte) ([][]byte, error)
// UpdateAccount abstracts an account write to the trie. It encodes the // UpdateAccount abstracts an account write to the trie. It encodes the
// provided account object with associated algorithm and then updates it // provided account object with associated algorithm and then updates it
// in the trie with provided address. // in the trie with provided address.

View file

@ -95,6 +95,30 @@ func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
return content, err return content, err
} }
// GetStorageBatch is a batched version of GetStorage that simultaneously looks
// up multiple slots. The advantage vs. the singleton version is the potential
// for concurrent disk lookups.
func (t *StateTrie) GetStorageBatch(_ []common.Address, keys [][]byte) ([][]byte, error) {
hashes := make([][]byte, len(keys))
for i, key := range keys {
hashes[i] = t.hashKey(key)
}
encs, err := t.trie.GetBatch(hashes)
if err != nil {
return nil, err
}
content := make([][]byte, len(keys))
for i, enc := range encs {
if len(enc) > 0 {
_, content[i], _, err = rlp.Split(enc)
if err != nil {
return content, err
}
}
}
return content, nil
}
// GetAccount attempts to retrieve an account with provided account address. // GetAccount attempts to retrieve an account with provided account address.
// If the specified account is not in the trie, nil will be returned. // If the specified account is not in the trie, nil will be returned.
// If a trie node is not found in the database, a MissingNodeError is returned. // If a trie node is not found in the database, a MissingNodeError is returned.
@ -108,6 +132,29 @@ func (t *StateTrie) GetAccount(address common.Address) (*types.StateAccount, err
return ret, err return ret, err
} }
// GetAccountBatch is a batched version of GetAccount that simultaneously looks
// up multiple slots. The advantage vs. the singleton version is the potential
// for concurrent disk lookups.
func (t *StateTrie) GetAccountBatch(addrs []common.Address) ([]*types.StateAccount, error) {
hashes := make([][]byte, len(addrs))
for i, addr := range addrs {
hashes[i] = t.hashKey(addr.Bytes())
}
encs, err := t.trie.GetBatch(hashes)
if err != nil {
return nil, err
}
content := make([]*types.StateAccount, len(addrs))
for i, enc := range encs {
if len(enc) > 0 {
if err = rlp.DecodeBytes(enc, content[i]); err != nil {
return content, err
}
}
}
return content, nil
}
// GetAccountByHash does the same thing as GetAccount, however it expects an // GetAccountByHash does the same thing as GetAccount, however it expects an
// account hash that is the hash of address. This constitutes an abstraction // account hash that is the hash of address. This constitutes an abstraction
// leak, since the client code needs to know the key format. // leak, since the client code needs to know the key format.
@ -121,6 +168,29 @@ func (t *StateTrie) GetAccountByHash(addrHash common.Hash) (*types.StateAccount,
return ret, err return ret, err
} }
// GetAccountBatchByHash does the same thing as GetAccountBatch, however it
// expects account hashes that are the hashes of addresses. This constitutes
// an abstraction leak, since the client code needs to know the key format.
func (t *StateTrie) GetAccountBatchByHash(addrHashes []common.Hash) ([]*types.StateAccount, error) {
hashes := make([][]byte, len(addrHashes))
for i, hash := range addrHashes {
hashes[i] = hash.Bytes()
}
encs, err := t.trie.GetBatch(hashes)
if err != nil {
return nil, err
}
content := make([]*types.StateAccount, len(addrHashes))
for i, enc := range encs {
if len(enc) > 0 {
if err = rlp.DecodeBytes(enc, content[i]); err != nil {
return content, err
}
}
}
return content, nil
}
// GetNode attempts to retrieve a trie node by compact-encoded path. It is not // GetNode attempts to retrieve a trie node by compact-encoded path. It is not
// possible to use keybyte-encoding as the path might contain odd nibbles. // possible to use keybyte-encoding as the path might contain odd nibbles.
// If the specified trie node is not in the trie, nil will be returned. // If the specified trie node is not in the trie, nil will be returned.

View file

@ -115,6 +115,21 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
return acc, nil return acc, nil
} }
// GetAccountBatch is a batched version of GetAccount that simultaneously looks
// up multiple slots. The advantage vs. the singleton version is the potential
// for concurrent disk lookups.
func (t *VerkleTrie) GetAccountBatch(addrs []common.Address) ([]*types.StateAccount, error) {
results := make([]*types.StateAccount, len(addrs))
for i, addr := range addrs {
acc, err := t.GetAccount(addr)
if err != nil {
return results, err
}
results[i] = acc
}
return results, nil
}
// GetStorage implements state.Trie, retrieving the storage slot with the specified // GetStorage implements state.Trie, retrieving the storage slot with the specified
// account address and storage key. If the specified slot is not in the verkle tree, // account address and storage key. If the specified slot is not in the verkle tree,
// nil will be returned. If the tree is corrupted, an error will be returned. // nil will be returned. If the tree is corrupted, an error will be returned.
@ -127,6 +142,21 @@ func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error)
return common.TrimLeftZeroes(val), nil return common.TrimLeftZeroes(val), nil
} }
// GetStorageBatch is a batched version of GetStorage that simultaneously looks
// up multiple slots. The advantage vs. the singleton version is the potential
// for concurrent disk lookups.
func (t *VerkleTrie) GetStorageBatch(addrs []common.Address, keys [][]byte) ([][]byte, error) {
results := make([][]byte, len(keys))
for i := 0; i < len(keys); i++ {
val, err := t.GetStorage(addrs[i], keys[i])
if err != nil {
return results, err
}
results[i] = val
}
return results, nil
}
// UpdateAccount implements state.Trie, writing the provided account into the tree. // UpdateAccount implements state.Trie, writing the provided account into the tree.
// If the tree is corrupted, an error will be returned. // If the tree is corrupted, an error will be returned.
func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error { func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error {