Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/ethapi: prevent unnecessary resource usage in eth_getProof implementation #27310

Merged
merged 3 commits into from May 31, 2023
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 42 additions & 23 deletions internal/ethapi/api.go
Expand Up @@ -655,43 +655,62 @@ type StorageResult struct {
Proof []string `json:"proof"`
}

// proofList implements ethdb.KeyValueWriter and collects the proofs as
// hex-strings for delivery to rpc-caller.
type proofList []string

func (n *proofList) Put(key []byte, value []byte) error {
*n = append(*n, hexutil.Encode(value))
return nil
}

func (n *proofList) Delete(key []byte) error {
panic("not supported")
}

// GetProof returns the Merkle-proof for a given account and optionally some storage keys.
func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) {
var (
keys = make([]common.Hash, len(storageKeys))
storageProof = make([]StorageResult, len(storageKeys))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe storageProofs?

storageTrie state.Trie
storageHash = types.EmptyRootHash
codeHash = types.EmptyCodeHash
)
// Greedily deserialize all keys. This prevents state access on invalid input
for i, hexKey := range storageKeys {
if key, err := decodeHash(hexKey); err != nil {
return nil, err
} else {
keys[i] = key
}
}
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return nil, err
}
storageTrie, err := state.StorageTrie(address)
if err != nil {
if storageTrie, err = state.StorageTrie(address); err != nil {
return nil, err
}
storageHash := types.EmptyRootHash
codeHash := state.GetCodeHash(address)
storageProof := make([]StorageResult, len(storageKeys))

// if we have a storageTrie, (which means the account exists), we can update the storagehash
// if we have a storageTrie, the account exists). and we we must update
holiman marked this conversation as resolved.
Show resolved Hide resolved
// the storage root hash and the code hash
holiman marked this conversation as resolved.
Show resolved Hide resolved
if storageTrie != nil {
storageHash = storageTrie.Hash()
} else {
// no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray.
codeHash = crypto.Keccak256Hash(nil)
codeHash = state.GetCodeHash(address)
}

// create the proof for the storageKeys
for i, hexKey := range storageKeys {
key, err := decodeHash(hexKey)
if err != nil {
return nil, err
for i, key := range keys {
if storageTrie == nil {
storageProof[i] = StorageResult{storageKeys[i], &hexutil.Big{}, []string{}}
continue
}
if storageTrie != nil {
proof, storageError := state.GetStorageProof(address, key)
if storageError != nil {
return nil, storageError
}
storageProof[i] = StorageResult{hexKey, (*hexutil.Big)(state.GetState(address, key).Big()), toHexSlice(proof)}
} else {
storageProof[i] = StorageResult{hexKey, &hexutil.Big{}, []string{}}
var proof proofList
if err := storageTrie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof); err != nil {
return nil, err
}
storageProof[i] = StorageResult{storageKeys[i],
(*hexutil.Big)(state.GetState(address, key).Big()),
proof}
}

// create the accountProof
Expand Down