Skip to content

Commit

Permalink
core, trie: expose batch trie getters into the statedb
Browse files Browse the repository at this point in the history
  • Loading branch information
karalabe committed May 8, 2024
1 parent 8fedd9f commit 9a4e5db
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
10 changes: 10 additions & 0 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,21 @@ type Trie interface {
// be returned.
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
// 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)

// 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
// provided account object with associated algorithm and then updates it
// in the trie with provided address.
Expand Down
70 changes: 70 additions & 0 deletions trie/secure_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
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.
// 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.
Expand All @@ -108,6 +132,29 @@ func (t *StateTrie) GetAccount(address common.Address) (*types.StateAccount, 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
// account hash that is the hash of address. This constitutes an abstraction
// leak, since the client code needs to know the key format.
Expand All @@ -121,6 +168,29 @@ func (t *StateTrie) GetAccountByHash(addrHash common.Hash) (*types.StateAccount,
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
// 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.
Expand Down
30 changes: 30 additions & 0 deletions trie/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,21 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
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
// 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.
Expand All @@ -127,6 +142,21 @@ func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error)
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.
// If the tree is corrupted, an error will be returned.
func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error {
Expand Down

0 comments on commit 9a4e5db

Please sign in to comment.