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

rpc(revive): get from latest block num #116

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,12 @@ func (s *stateObject) fetchExpiredFromRemote(prefixKey []byte, key common.Hash,

// if no prefix, query from revive trie, got the newest expired info
if resolvePath {
_, err := tr.GetStorage(s.address, key.Bytes())
val, err := tr.GetStorage(s.address, key.Bytes())
asyukii marked this conversation as resolved.
Show resolved Hide resolved
// TODO(asyukii): temporary fix snap expired, but trie not expire, may investigate more later.
if val != nil {
s.pendingReviveState[string(crypto.Keccak256(key[:]))] = common.BytesToHash(val)
return val, nil
}
enErr, ok := err.(*trie.ExpiredNodeError)
if !ok {
return nil, fmt.Errorf("cannot find expired state from trie, err: %v", err)
Expand Down
9 changes: 9 additions & 0 deletions core/types/revive_state.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package types

import (
"github.com/ethereum/go-ethereum/common/hexutil"
)

type ReviveStorageProof struct {
Key string `json:"key"`
PrefixKey string `json:"prefixKey"`
Proof []string `json:"proof"`
}

type ReviveResult struct {
StorageProof []ReviveStorageProof `json:"storageProof"`
BlockNum hexutil.Uint64 `json:"blockNum"`
}
23 changes: 12 additions & 11 deletions ethclient/gethclient/gethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,21 @@ func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []s

// GetStorageReviveProof returns the proof for the given keys. Prefix keys can be specified to obtain partial proof for a given key.
// Both keys and prefix keys should have the same length. If user wish to obtain full proof for a given key, the corresponding prefix key should be empty string.
func (ec *Client) GetStorageReviveProof(ctx context.Context, account common.Address, keys []string, prefixKeys []string, hash common.Hash) ([]types.ReviveStorageProof, error) {
func (ec *Client) GetStorageReviveProof(ctx context.Context, account common.Address, keys []string, prefixKeys []string, hash common.Hash) (*types.ReviveResult, error) {
type reviveResult struct {
StorageProof []types.ReviveStorageProof `json:"storageProof"`
BlockNum hexutil.Uint64 `json:"blockNum"`
}

var err error
storageResults := make([]types.ReviveStorageProof, 0, len(keys))
var res reviveResult

if len(keys) != len(prefixKeys) {
return nil, fmt.Errorf("keys and prefixKeys must be same length")
}
err = ec.c.CallContext(ctx, &res, "eth_getStorageReviveProof", account, keys, prefixKeys, hash)

if hash == (common.Hash{}) {
err = ec.c.CallContext(ctx, &storageResults, "eth_getStorageReviveProof", account, keys, prefixKeys, "latest")
} else {
err = ec.c.CallContext(ctx, &storageResults, "eth_getStorageReviveProof", account, keys, prefixKeys, hash)
}
return storageResults, err
return &types.ReviveResult{
StorageProof: res.StorageProof,
BlockNum: res.BlockNum,
}, err
}

// CallContract executes a message call transaction, which is directly executed in the VM
Expand Down
14 changes: 8 additions & 6 deletions ethclient/gethclient/gethclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,21 +242,23 @@ func testGetProof(t *testing.T, client *rpc.Client) {
func testGetStorageReviveProof(t *testing.T, client *rpc.Client) {
ec := New(client)
result, err := ec.GetStorageReviveProof(context.Background(), testAddr, []string{testSlot.String()}, []string{""}, common.Hash{})
proofs := result.StorageProof

if err != nil {
t.Fatal(err)
}

// test storage
if len(result) != 1 {
t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result))
if len(proofs) != 1 {
t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(proofs))
}

if result[0].Key != testSlot.String() {
t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), result[0].Key)
if proofs[0].Key != testSlot.String() {
t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), proofs[0].Key)
}

if result[0].PrefixKey != "" {
t.Fatalf("invalid storage proof prefix key, want: %q, got: %q", "", result[0].PrefixKey)
if proofs[0].PrefixKey != "" {
t.Fatalf("invalid storage proof prefix key, want: %q, got: %q", "", proofs[0].PrefixKey)
}
}

Expand Down
20 changes: 12 additions & 8 deletions ethdb/fullstatedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -63,35 +64,38 @@ func (f *FullStateRPCServer) GetStorageReviveProof(stateRoot common.Hash, accoun
getStorageProofTimer.Update(time.Since(start))
}(time.Now())

var result types.ReviveResult

getProofMeter.Mark(int64(len(keys)))
// find from lru cache, now it cache key proof
uncahcedPrefixKeys := make([]string, 0, len(prefixKeys))
uncahcedKeys := make([]string, 0, len(keys))
uncachedPrefixKeys := make([]string, 0, len(prefixKeys))
uncachedKeys := make([]string, 0, len(keys))
ret := make([]types.ReviveStorageProof, 0, len(keys))
for i, key := range keys {
val, ok := f.cache.Get(proofCacheKey(account, root, prefixKeys[i], key))
log.Debug("GetStorageReviveProof hit cache", "account", account, "key", key, "ok", ok)
if !ok {
uncahcedPrefixKeys = append(uncahcedPrefixKeys, prefixKeys[i])
uncahcedKeys = append(uncahcedKeys, keys[i])
uncachedPrefixKeys = append(uncachedPrefixKeys, prefixKeys[i])
uncachedKeys = append(uncachedKeys, keys[i])
continue
}
getProofHitCacheMeter.Mark(1)
ret = append(ret, val.(types.ReviveStorageProof))
}
if len(uncahcedKeys) == 0 {
if len(uncachedKeys) == 0 {
return ret, nil
}

// TODO(0xbundler): add timeout in flags?
ctx, cancelFunc := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelFunc()
proofs := make([]types.ReviveStorageProof, 0, len(uncahcedKeys))
err := f.client.CallContext(ctx, &proofs, "eth_getStorageReviveProof", stateRoot, account, root, uncahcedKeys, uncahcedPrefixKeys)
err := f.client.CallContext(ctx, &result, "eth_getStorageReviveProof", stateRoot, account, root, uncachedKeys, uncachedPrefixKeys)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get storage revive proof, err: %v, remote's block number: %v", err, result.BlockNum)
}

proofs := result.StorageProof

// add to cache
for _, proof := range proofs {
f.cache.Add(proofCacheKey(account, root, proof.PrefixKey, proof.Key), proof)
Expand Down
44 changes: 37 additions & 7 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
"github.com/tyler-smith/go-bip39"
)

Expand Down Expand Up @@ -765,7 +766,7 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st

// GetStorageReviveProof returns the proof for the given keys. Prefix keys can be specified to obtain partial proof for a given key.
// Both keys and prefix keys should have the same length. If user wish to obtain full proof for a given key, the corresponding prefix key should be empty string.
func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot common.Hash, address common.Address, root common.Hash, storageKeys []string, storagePrefixKeys []string) ([]types.ReviveStorageProof, error) {
func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot common.Hash, address common.Address, root common.Hash, storageKeys []string, storagePrefixKeys []string) (*types.ReviveResult, error) {
defer func(start time.Time) {
getStorageProofTimer.Update(time.Since(start))
}(time.Now())
Expand All @@ -775,11 +776,42 @@ func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot com
}

var (
blockNum hexutil.Uint64
err error
stateDb *state.StateDB
header *types.Header
storageTrie state.Trie
keys = make([]common.Hash, len(storageKeys))
keyLengths = make([]int, len(storageKeys))
prefixKeys = make([][]byte, len(storagePrefixKeys))
storageProof = make([]types.ReviveStorageProof, len(storageKeys))
)

openStorageTrie := func(stateDb *state.StateDB, header *types.Header, address common.Address) (state.Trie, error) {
id := trie.StorageTrieID(header.Root, crypto.Keccak256Hash(address.Bytes()), root)
tr, err := trie.NewStateTrie(id, stateDb.Database().TrieDB())
if err != nil {
return nil, err
}
return tr, nil
}

stateDb, header, _ = s.b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber)
blockNum = hexutil.Uint64(header.Number.Uint64())

storageTrie, err = s.b.StorageTrie(stateRoot, address, root)
if (err != nil || storageTrie == nil) && stateDb != nil {
storageTrie, err = openStorageTrie(stateDb, header, address)
log.Info("GetStorageReviveProof from latest block number", "blockNum", blockNum, "blockHash", header.Hash().Hex())
}

if err != nil || storageTrie == nil {
return &types.ReviveResult{
StorageProof: nil,
BlockNum: blockNum,
}, err
}

// Deserialize all keys. This prevents state access on invalid input.
for i, hexKey := range storageKeys {
var err error
Expand All @@ -798,11 +830,6 @@ func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot com
}
}

storageTrie, err := s.b.StorageTrie(stateRoot, address, root)
if err != nil || storageTrie == nil {
return nil, fmt.Errorf("open StorageTrie err: %v", err)
}

// Create the proofs for the storageKeys.
for i, key := range keys {
// Output key encoding is a bit special: if the input was a 32-byte hash, it is
Expand All @@ -828,7 +855,10 @@ func (s *BlockChainAPI) GetStorageReviveProof(ctx context.Context, stateRoot com
}
}

return storageProof, nil
return &types.ReviveResult{
StorageProof: storageProof,
BlockNum: blockNum,
}, nil
}

// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
Expand Down
1 change: 0 additions & 1 deletion trie/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ func (t *StateTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
// it.
func (t *Trie) traverseNodes(tn node, prefixKey, suffixKey []byte, nodes *[]node, epoch types.StateEpoch, updateEpoch bool) (node, error) {
for len(suffixKey) > 0 && tn != nil {
log.Info("traverseNodes loop", "prefix", common.Bytes2Hex(prefixKey), "suffix", common.Bytes2Hex(suffixKey), "n", tn.fstring(""))
switch n := tn.(type) {
case *shortNode:
if len(suffixKey) >= len(n.Key) && bytes.Equal(n.Key, suffixKey[:len(n.Key)]) {
Expand Down
4 changes: 2 additions & 2 deletions trie/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -1238,8 +1238,8 @@ func (t *Trie) tryRevive(n node, key []byte, targetPrefixKey []byte, nub MPTProo
n1 = n1.copy()
n1.Val = newnode
n1.flags = t.newFlag()
tryUpdateNodeEpoch(nub.n1, t.currentEpoch)
renew, _, err := t.updateChildNodeEpoch(nub.n1, key, pos, t.currentEpoch)
tryUpdateNodeEpoch(n1, t.currentEpoch)
renew, _, err := t.updateChildNodeEpoch(n1, key, pos, t.currentEpoch)
if err != nil {
return nil, false, fmt.Errorf("update child node epoch while reviving failed, err: %v", err)
}
Expand Down