mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-25 04:57:17 +00:00
239 lines
6.9 KiB
Go
239 lines
6.9 KiB
Go
package ethapi
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"math/big"
|
|
"sort"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
"github.com/ledgerwatch/turbo-geth/common/changeset"
|
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
|
"github.com/ledgerwatch/turbo-geth/common/hexutil"
|
|
"github.com/ledgerwatch/turbo-geth/core/rawdb"
|
|
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
|
|
"github.com/ledgerwatch/turbo-geth/rpc"
|
|
"github.com/ledgerwatch/turbo-geth/turbo/trie"
|
|
)
|
|
|
|
// Result structs for GetProof
|
|
type AccountResult struct {
|
|
Address common.Address `json:"address"`
|
|
AccountProof []string `json:"accountProof"`
|
|
Balance *hexutil.Big `json:"balance"`
|
|
CodeHash common.Hash `json:"codeHash"`
|
|
Nonce hexutil.Uint64 `json:"nonce"`
|
|
StorageHash common.Hash `json:"storageHash"`
|
|
StorageProof []StorageResult `json:"storageProof"`
|
|
}
|
|
type StorageResult struct {
|
|
Key string `json:"key"`
|
|
Value *hexutil.Big `json:"value"`
|
|
Proof []string `json:"proof"`
|
|
}
|
|
|
|
func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNr rpc.BlockNumber) (*AccountResult, error) {
|
|
block := uint64(blockNr.Int64()) + 1
|
|
db := s.b.ChainDb()
|
|
ts := dbutils.EncodeBlockNumber(block)
|
|
accountMap := make(map[string]*accounts.Account)
|
|
if err := changeset.Walk(db, dbutils.PlainAccountChangeSetBucket, ts, 0, func(blockN uint64, a, v []byte) (bool, error) {
|
|
a, v = common.CopyBytes(a), common.CopyBytes(v)
|
|
var kHash, err = common.HashData(a)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
k := kHash[:]
|
|
if _, ok := accountMap[string(k)]; !ok {
|
|
if len(v) > 0 {
|
|
var a accounts.Account
|
|
if innerErr := a.DecodeForStorage(v); innerErr != nil {
|
|
return false, innerErr
|
|
}
|
|
accountMap[string(k)] = &a
|
|
} else {
|
|
accountMap[string(k)] = nil
|
|
}
|
|
}
|
|
return true, nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
storageMap := make(map[string][]byte)
|
|
if err := changeset.Walk(db, dbutils.PlainAccountChangeSetBucket, ts, 0, func(blockN uint64, a, v []byte) (bool, error) {
|
|
a, v = common.CopyBytes(a), common.CopyBytes(v)
|
|
var kHash, err = common.HashData(a)
|
|
if err != nil {
|
|
return true, err
|
|
}
|
|
k := kHash[:]
|
|
if _, ok := storageMap[string(k)]; !ok {
|
|
storageMap[string(k)] = v
|
|
}
|
|
return true, nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
var unfurlList = make([]string, len(accountMap)+len(storageMap))
|
|
unfurl := trie.NewRetainList(0)
|
|
i := 0
|
|
for ks, acc := range accountMap {
|
|
unfurlList[i] = ks
|
|
i++
|
|
unfurl.AddKey([]byte(ks))
|
|
if acc != nil {
|
|
// Fill the code hashes
|
|
if acc.Incarnation > 0 && acc.IsEmptyCodeHash() {
|
|
if codeHash, err1 := db.Get(dbutils.ContractCodeBucket, dbutils.GenerateStoragePrefix([]byte(ks), acc.Incarnation)); err1 == nil {
|
|
copy(acc.CodeHash[:], codeHash)
|
|
} else {
|
|
return nil, err1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for ks := range storageMap {
|
|
unfurlList[i] = ks
|
|
i++
|
|
var sk [64]byte
|
|
copy(sk[:], []byte(ks)[:common.HashLength])
|
|
copy(sk[common.HashLength:], []byte(ks)[common.HashLength+common.IncarnationLength:])
|
|
unfurl.AddKey(sk[:])
|
|
}
|
|
rl := trie.NewRetainList(0)
|
|
addrHash, err := common.HashData(address[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rl.AddKey(addrHash[:])
|
|
unfurl.AddKey(addrHash[:])
|
|
for _, key := range storageKeys {
|
|
keyAsHash := common.HexToHash(key)
|
|
if keyHash, err1 := common.HashData(keyAsHash[:]); err1 == nil {
|
|
trieKey := append(addrHash[:], keyHash[:]...)
|
|
rl.AddKey(trieKey)
|
|
unfurl.AddKey(trieKey)
|
|
} else {
|
|
return nil, err1
|
|
}
|
|
}
|
|
sort.Strings(unfurlList)
|
|
loader := trie.NewFlatDbSubTrieLoader()
|
|
if err = loader.Reset(db, unfurl, unfurl, nil /* hashCollector */, [][]byte{nil}, []int{0}, false); err != nil {
|
|
return nil, err
|
|
}
|
|
r := &Receiver{defaultReceiver: trie.NewDefaultReceiver(), unfurlList: unfurlList, accountMap: accountMap, storageMap: storageMap}
|
|
r.defaultReceiver.Reset(rl, nil /* hashCollector */, false)
|
|
loader.SetStreamReceiver(r)
|
|
subTries, err1 := loader.LoadSubTries()
|
|
if err1 != nil {
|
|
return nil, err1
|
|
}
|
|
hash, err := rawdb.ReadCanonicalHash(db, block-1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
header := rawdb.ReadHeader(db, hash, block-1)
|
|
tr := trie.New(header.Root)
|
|
if err = tr.HookSubTries(subTries, [][]byte{nil}); err != nil {
|
|
return nil, err
|
|
}
|
|
accountProof, err2 := tr.Prove(addrHash[:], 0, false /* storage */)
|
|
if err2 != nil {
|
|
return nil, err2
|
|
}
|
|
storageProof := make([]StorageResult, len(storageKeys))
|
|
for i, key := range storageKeys {
|
|
keyAsHash := common.HexToHash(key)
|
|
if keyHash, err1 := common.HashData(keyAsHash[:]); err1 == nil {
|
|
trieKey := append(addrHash[:], keyHash[:]...)
|
|
if proof, err3 := tr.Prove(trieKey, 64 /* nibbles to get to the storage sub-trie */, true /* storage */); err3 == nil {
|
|
v, _ := tr.Get(trieKey)
|
|
bv := new(big.Int)
|
|
bv.SetBytes(v)
|
|
storageProof[i] = StorageResult{key, (*hexutil.Big)(bv), toHexSlice(proof)}
|
|
} else {
|
|
return nil, err3
|
|
}
|
|
} else {
|
|
return nil, err1
|
|
}
|
|
}
|
|
acc, found := tr.GetAccount(addrHash[:])
|
|
if !found {
|
|
return nil, nil
|
|
}
|
|
return &AccountResult{
|
|
Address: address,
|
|
AccountProof: toHexSlice(accountProof),
|
|
Balance: (*hexutil.Big)(acc.Balance.ToBig()),
|
|
CodeHash: acc.CodeHash,
|
|
Nonce: hexutil.Uint64(acc.Nonce),
|
|
StorageHash: acc.Root,
|
|
StorageProof: storageProof,
|
|
}, nil
|
|
}
|
|
|
|
type Receiver struct {
|
|
defaultReceiver *trie.DefaultReceiver
|
|
accountMap map[string]*accounts.Account
|
|
storageMap map[string][]byte
|
|
unfurlList []string
|
|
currentIdx int
|
|
}
|
|
|
|
func (r *Receiver) Root() common.Hash { panic("don't call me") }
|
|
func (r *Receiver) Receive(
|
|
itemType trie.StreamItem,
|
|
accountKey []byte,
|
|
storageKey []byte,
|
|
accountValue *accounts.Account,
|
|
storageValue []byte,
|
|
hash []byte,
|
|
hasTree bool,
|
|
cutoff int,
|
|
) error {
|
|
for r.currentIdx < len(r.unfurlList) {
|
|
ks := r.unfurlList[r.currentIdx]
|
|
k := []byte(ks)
|
|
var c int
|
|
switch itemType {
|
|
case trie.StorageStreamItem, trie.SHashStreamItem:
|
|
c = bytes.Compare(k, storageKey)
|
|
case trie.AccountStreamItem, trie.AHashStreamItem:
|
|
c = bytes.Compare(k, accountKey)
|
|
case trie.CutoffStreamItem:
|
|
c = -1
|
|
}
|
|
if c > 0 {
|
|
return r.defaultReceiver.Receive(itemType, accountKey, storageKey, accountValue, storageValue, hash, hasTree, cutoff)
|
|
}
|
|
if len(k) > common.HashLength {
|
|
v := r.storageMap[ks]
|
|
if c <= 0 && len(v) > 0 {
|
|
if err := r.defaultReceiver.Receive(trie.StorageStreamItem, nil, k, nil, v, nil, hasTree, 0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
v := r.accountMap[ks]
|
|
if c <= 0 && v != nil {
|
|
if err := r.defaultReceiver.Receive(trie.AccountStreamItem, k, nil, v, nil, nil, hasTree, 0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
r.currentIdx++
|
|
if c == 0 {
|
|
return nil
|
|
}
|
|
}
|
|
// We ran out of modifications, simply pass through
|
|
return r.defaultReceiver.Receive(itemType, accountKey, storageKey, accountValue, storageValue, hash, hasTree, cutoff)
|
|
}
|
|
|
|
func (r *Receiver) Result() trie.SubTries {
|
|
return r.defaultReceiver.Result()
|
|
}
|