diff --git a/core/state/database.go b/core/state/database.go index 188ecf0c86..8b6c4695c8 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -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. diff --git a/trie/secure_trie.go b/trie/secure_trie.go index efd4dfb5d3..04794a4395 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -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. @@ -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. @@ -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. diff --git a/trie/verkle.go b/trie/verkle.go index 01d813d9ec..9168c3b29f 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -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. @@ -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 {