Skip to content

Commit

Permalink
add account and storage proof api
Browse files Browse the repository at this point in the history
  • Loading branch information
ralph-pichler committed Dec 8, 2022
1 parent 453edc5 commit 2300537
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 0 deletions.
1 change: 1 addition & 0 deletions api/accounts/accounts.go
Expand Up @@ -355,5 +355,6 @@ func (a *Accounts) Mount(root *mux.Router, pathPrefix string) {
sub.Path("/{address}/storage/{key}").Methods("GET").HandlerFunc(utils.WrapHandlerFunc(a.handleGetStorage))
sub.Path("").Methods("POST").HandlerFunc(utils.WrapHandlerFunc(a.handleCallContract))
sub.Path("/{address}").Methods("POST").HandlerFunc(utils.WrapHandlerFunc(a.handleCallContract))
sub.Path("/proof/{address}/{key}").Methods("POST").HandlerFunc(utils.WrapHandlerFunc(a.handleProofStorage))

}
72 changes: 72 additions & 0 deletions api/accounts/proof.go
@@ -0,0 +1,72 @@
package accounts

import (
"net/http"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/vechain/thor/api/utils"
"github.com/vechain/thor/chain"
"github.com/vechain/thor/thor"
)

type merkleProof struct {
StateRoot string `json:"stateRoot"`
Account string `json:"account"`
Slot string `json:"slot"`
Value string `json:"value"`
AccountProof []string `json:"accountProof"`
StorageProof []string `json:"storageProof"`
}

func (a *Accounts) proofStorage(addr thor.Address, slot thor.Bytes32, summary *chain.BlockSummary) (p *merkleProof, err error) {
state := a.stater.
NewState(summary.Header.StateRoot(), summary.Header.Number(), summary.Conflicts, summary.SteadyNum)

proof, storageProofBytes, value, err := state.ProveStorage(addr, slot)
if err != nil {
return nil, err
}

var accountProof []string
for _, p := range proof {
accountProof = append(accountProof, hexutil.Encode(p))
}

var storageProof []string
for _, p := range storageProofBytes {
storageProof = append(storageProof, hexutil.Encode(p))
}

return &merkleProof{
StateRoot: summary.Header.StateRoot().String(),
Account: hexutil.Encode(addr[:]),
Slot: hexutil.Encode(slot[:]),
Value: hexutil.Encode(value[:]),
AccountProof: accountProof,
StorageProof: storageProof,
}, nil
}

func (a *Accounts) handleProofStorage(w http.ResponseWriter, req *http.Request) error {
hexAddr := mux.Vars(req)["address"]
addr, err := thor.ParseAddress(hexAddr)
if err != nil {
return utils.BadRequest(errors.WithMessage(err, "address"))
}
slot, err := thor.ParseBytes32(mux.Vars(req)["key"])
if err != nil {
return utils.BadRequest(errors.WithMessage(err, "storage"))
}
summary, err := a.handleRevision(req.URL.Query().Get("revision"))
if err != nil {
return err
}
proof, err := a.proofStorage(addr, slot, summary)
if err != nil {
return err
}

return utils.WriteJSON(w, proof)
}
15 changes: 15 additions & 0 deletions muxdb/internal/trie/trie.go
Expand Up @@ -478,3 +478,18 @@ type leafAvailable struct {
func (*leafAvailable) Error() string {
return "leaf available"
}

type proofList [][]byte

func (n *proofList) Put(key []byte, value []byte) error {
valueCopy := make([]byte, len(value))
copy(valueCopy, value)
*n = append(*n, valueCopy)
return nil
}

func (t *Trie) Prove(key []byte, fromLevel uint) ([][]byte, error) {
var proofList proofList
err := t.ext.Prove(key, fromLevel, &proofList)
return proofList, err
}
41 changes: 41 additions & 0 deletions state/state.go
Expand Up @@ -538,6 +538,47 @@ func (s *State) Stage(newBlockNum, newBlockConflicts uint32) (*Stage, error) {
}, nil
}

func (s *State) ProveStorage(addr thor.Address, slot thor.Bytes32) ([][]byte, [][]byte, thor.Bytes32, error) {
secured := thor.Blake2b(addr[:])
securedSlot := thor.Blake2b(slot[:])

// ensure all nodes are loaded
// otherwise the Prove call below may fail sometimes
_, _, err := s.trie.Get(secured[:])
if err != nil {
return nil, nil, thor.Bytes32{}, err
}

proof, err := s.trie.Prove(secured[:], 0)
if err != nil {
return nil, nil, thor.Bytes32{}, err
}

storageTrie, err := s.BuildStorageTrie(addr)
if err != nil {
return nil, nil, thor.Bytes32{}, err
}

v, err := s.GetStorage(addr, slot)
if err != nil {
return nil, nil, thor.Bytes32{}, err
}

// ensure all nodes are loaded
// otherwise the Prove call below may fail sometimes
_, _, err = storageTrie.Get(securedSlot[:])
if err != nil {
return nil, nil, thor.Bytes32{}, err
}

storageProofBytes, err := storageTrie.Prove(securedSlot[:], 0)
if err != nil {
return nil, nil, thor.Bytes32{}, err
}

return proof, storageProofBytes, v, nil
}

type (
storageKey struct {
addr thor.Address
Expand Down
8 changes: 8 additions & 0 deletions trie/proof.go
Expand Up @@ -18,6 +18,7 @@ package trie

import (
"bytes"
"errors"
"fmt"

"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -91,6 +92,13 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb DatabaseWriter) error {
return nil
}

func (t *ExtendedTrie) Prove(key []byte, fromLevel uint, proofDb DatabaseWriter) error {
if t.IsNonCrypto() {
return errors.New("non-crypto")
}
return t.trie.Prove(key, fromLevel, proofDb)
}

// VerifyProof checks merkle proofs. The given proof must contain the
// value for key in a trie with the given root hash. VerifyProof
// returns an error if the proof contains invalid trie nodes or the
Expand Down

0 comments on commit 2300537

Please sign in to comment.