Skip to content

Commit

Permalink
getProof rpc method
Browse files Browse the repository at this point in the history
  • Loading branch information
pnowosie committed Oct 11, 2024
1 parent 1786cb3 commit 1e50ea2
Show file tree
Hide file tree
Showing 13 changed files with 816 additions and 119 deletions.
25 changes: 25 additions & 0 deletions blockchain/pending.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package blockchain

import (
"errors"

"github.com/NethermindEth/juno/core"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/core/trie"
)

type Pending struct {
Expand Down Expand Up @@ -65,3 +68,25 @@ func (p *PendingState) Class(classHash *felt.Felt) (*core.DeclaredClass, error)

return p.head.Class(classHash)
}

// Note[pnowosie]: Maybe extending StateReader with the following methods was not a good idea?
func (p *PendingState) ClassTrie() (*trie.Trie, func() error, error) {
return nil, nopCloser, errFeatureNotImplemented
}

func (p *PendingState) StorageTrie() (*trie.Trie, func() error, error) {
return nil, nopCloser, errFeatureNotImplemented
}

func (p *PendingState) StorageTrieForAddr(*felt.Felt) (*trie.Trie, error) {
return nil, errFeatureNotImplemented
}

func (p *PendingState) StateAndClassRoot() (*felt.Felt, *felt.Felt, error) {
return nil, nil, errFeatureNotImplemented
}

var (
errFeatureNotImplemented = errors.New("feature not implemented for a historical state")
nopCloser = func() error { return nil }
)
50 changes: 50 additions & 0 deletions core/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ type StateReader interface {
ContractNonce(addr *felt.Felt) (*felt.Felt, error)
ContractStorage(addr, key *felt.Felt) (*felt.Felt, error)
Class(classHash *felt.Felt) (*DeclaredClass, error)

// NOTE: Not a best way to add them here - it assumes current state and atm cannot be implemented for hitsrical states
ClassTrie() (*trie.Trie, func() error, error)
StorageTrie() (*trie.Trie, func() error, error)
StorageTrieForAddr(addr *felt.Felt) (*trie.Trie, error)
StateAndClassRoot() (*felt.Felt, *felt.Felt, error)
}

type State struct {
Expand Down Expand Up @@ -129,6 +135,18 @@ func (s *State) storage() (*trie.Trie, func() error, error) {
return s.globalTrie(db.StateTrie, trie.NewTriePedersen)
}

func (s *State) StorageTrie() (*trie.Trie, func() error, error) {
return s.storage()
}

func (s *State) ClassTrie() (*trie.Trie, func() error, error) {
return s.classesTrie()
}

func (s *State) StorageTrieForAddr(addr *felt.Felt) (*trie.Trie, error) {
return storage(addr, s.txn)
}

func (s *State) classesTrie() (*trie.Trie, func() error, error) {
return s.globalTrie(db.ClassesTrie, trie.NewTriePoseidon)
}
Expand Down Expand Up @@ -721,3 +739,35 @@ func (s *State) buildReverseDiff(blockNumber uint64, diff *StateDiff) (*StateDif

return &reversed, nil
}

func (s *State) StateAndClassRoot() (*felt.Felt, *felt.Felt, error) {
var storageRoot, classesRoot *felt.Felt

sStorage, closer, err := s.storage()
if err != nil {
return nil, nil, err
}

if storageRoot, err = sStorage.Root(); err != nil {
return nil, nil, err
}

if err = closer(); err != nil {
return nil, nil, err
}

classes, closer, err := s.classesTrie()
if err != nil {
return nil, nil, err
}

if classesRoot, err = classes.Root(); err != nil {
return nil, nil, err
}

if err = closer(); err != nil {
return nil, nil, err
}

return storageRoot, classesRoot, nil
}
23 changes: 23 additions & 0 deletions core/state_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"

"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/core/trie"
"github.com/NethermindEth/juno/db"
)

Expand Down Expand Up @@ -87,3 +88,25 @@ func (s *stateSnapshot) Class(classHash *felt.Felt) (*DeclaredClass, error) {
}
return declaredClass, nil
}

// Note[pnowosie]: Maybe extending StateReader with the following methods was not a good idea?
func (s *stateSnapshot) ClassTrie() (*trie.Trie, func() error, error) {
return nil, nopCloser, errFeatureNotImplemented
}

func (s *stateSnapshot) StorageTrie() (*trie.Trie, func() error, error) {
return nil, nopCloser, errFeatureNotImplemented
}

func (s *stateSnapshot) StorageTrieForAddr(*felt.Felt) (*trie.Trie, error) {
return nil, errFeatureNotImplemented
}

func (s *stateSnapshot) StateAndClassRoot() (*felt.Felt, *felt.Felt, error) {
return nil, nil, errFeatureNotImplemented
}

var (
errFeatureNotImplemented = errors.New("feature not implemented for a historical state")
nopCloser = func() error { return nil }
)
3 changes: 3 additions & 0 deletions core/trie/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func (k *Key) SubKey(n uint8) (*Key, error) {
if n > k.len {
return nil, errors.New(fmt.Sprint("cannot subtract key of length %i from key of length %i", n, k.len))
}
if n == k.len {
return &Key{}, nil
}

newKey := &Key{len: n}
copy(newKey.bitset[:], k.bitset[len(k.bitset)-int((k.len+7)/8):]) //nolint:mnd
Expand Down
4 changes: 2 additions & 2 deletions core/trie/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Node struct {
}

// Hash calculates the hash of a [Node]
func (n *Node) Hash(path *Key, hashFunc hashFunc) *felt.Felt {
func (n *Node) Hash(path *Key, hashFunc HashFunc) *felt.Felt {
if path.Len() == 0 {
// we have to deference the Value, since the Node can released back
// to the NodePool and be reused anytime
Expand All @@ -33,7 +33,7 @@ func (n *Node) Hash(path *Key, hashFunc hashFunc) *felt.Felt {
}

// Hash calculates the hash of a [Node]
func (n *Node) HashFromParent(parnetKey, nodeKey *Key, hashFunc hashFunc) *felt.Felt {
func (n *Node) HashFromParent(parnetKey, nodeKey *Key, hashFunc HashFunc) *felt.Felt {
path := path(nodeKey, parnetKey)
return n.Hash(&path, hashFunc)
}
Expand Down
31 changes: 18 additions & 13 deletions core/trie/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var (
)

type ProofNode interface {
Hash(hash hashFunc) *felt.Felt
Hash(hash HashFunc) *felt.Felt
Len() uint8
PrettyPrint()
}
Expand All @@ -23,7 +23,7 @@ type Binary struct {
RightHash *felt.Felt
}

func (b *Binary) Hash(hash hashFunc) *felt.Felt {
func (b *Binary) Hash(hash HashFunc) *felt.Felt {
return hash(b.LeftHash, b.RightHash)
}

Expand All @@ -42,7 +42,7 @@ type Edge struct {
Path *Key // path from parent to child
}

func (e *Edge) Hash(hash hashFunc) *felt.Felt {
func (e *Edge) Hash(hash HashFunc) *felt.Felt {
length := make([]byte, len(e.Path.bitset))
length[len(e.Path.bitset)-1] = e.Path.len
pathFelt := e.Path.Felt()
Expand All @@ -54,6 +54,11 @@ func (e *Edge) Len() uint8 {
return e.Path.Len()
}

func (e *Edge) PathInt() uint64 {
f := e.Path.Felt()
return f.Uint64()
}

func (e *Edge) PrettyPrint() {
fmt.Printf(" Edge:\n")
fmt.Printf(" Child: %v\n", e.Child)
Expand Down Expand Up @@ -199,7 +204,7 @@ func traverseNodes(currNode ProofNode, path *[]ProofNode, nodeHashes map[felt.Fe
// merges paths in the specified order [commonNodes..., leftNodes..., rightNodes...]
// ordering of the merged path is not important
// since SplitProofPath can discover the left and right paths using the merged path and the rootHash
func MergeProofPaths(leftPath, rightPath []ProofNode, hash hashFunc) ([]ProofNode, *felt.Felt, error) {
func MergeProofPaths(leftPath, rightPath []ProofNode, hash HashFunc) ([]ProofNode, *felt.Felt, error) {
merged := []ProofNode{}
minLen := min(len(leftPath), len(rightPath))

Expand Down Expand Up @@ -236,7 +241,7 @@ func MergeProofPaths(leftPath, rightPath []ProofNode, hash hashFunc) ([]ProofNod
// SplitProofPath splits the merged proof path into two paths (left and right), which were merged before
// it first validates that the merged path is not circular, the split happens at most once and rootHash exists
// then calls traverseNodes to split the path to left and right paths
func SplitProofPath(mergedPath []ProofNode, rootHash *felt.Felt, hash hashFunc) ([]ProofNode, []ProofNode, error) {
func SplitProofPath(mergedPath []ProofNode, rootHash *felt.Felt, hash HashFunc) ([]ProofNode, []ProofNode, error) {
commonPath := []ProofNode{}
leftPath := []ProofNode{}
rightPath := []ProofNode{}
Expand Down Expand Up @@ -316,7 +321,7 @@ func GetProof(key *Key, tri *Trie) ([]ProofNode, error) {

// verifyProof checks if `leafPath` leads from `root` to `leafHash` along the `proofNodes`
// https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L2006
func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode, hash hashFunc) bool {
func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode, hash HashFunc) bool {
expectedHash := root
remainingPath := NewKey(key.len, key.bitset[:])
for i, proofNode := range proofs {
Expand Down Expand Up @@ -345,7 +350,7 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode
return true
}

if !proofNode.Path.Equal(subKey) {
if !proofNode.Path.Equal(subKey) && !subKey.Equal(&Key{}) {
return false
}
expectedHash = proofNode.Child
Expand All @@ -363,7 +368,7 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode
// and therefore it's hash won't match the expected root.
// ref: https://github.com/ethereum/go-ethereum/blob/v1.14.3/trie/proof.go#L484
func VerifyRangeProof(root *felt.Felt, keys, values []*felt.Felt, proofKeys [2]*Key, proofValues [2]*felt.Felt,
proofs [2][]ProofNode, hash hashFunc,
proofs [2][]ProofNode, hash HashFunc,
) (bool, error) {
// Step 0: checks
if len(keys) != len(values) {
Expand Down Expand Up @@ -440,7 +445,7 @@ func ensureMonotonicIncreasing(proofKeys [2]*Key, keys []*felt.Felt) error {
}

// compressNode determines if the node needs compressed, and if so, the len needed to arrive at the next key
func compressNode(idx int, proofNodes []ProofNode, hashF hashFunc) (int, uint8, error) {
func compressNode(idx int, proofNodes []ProofNode, hashF HashFunc) (int, uint8, error) {
parent := proofNodes[idx]

if idx == len(proofNodes)-1 {
Expand Down Expand Up @@ -474,7 +479,7 @@ func compressNode(idx int, proofNodes []ProofNode, hashF hashFunc) (int, uint8,
}

func assignChild(i, compressedParent int, parentNode *Node,
nilKey, leafKey, parentKey *Key, proofNodes []ProofNode, hashF hashFunc,
nilKey, leafKey, parentKey *Key, proofNodes []ProofNode, hashF HashFunc,
) (*Key, error) {
childInd := i + compressedParent + 1
childKey, err := getChildKey(childInd, parentKey, leafKey, nilKey, proofNodes, hashF)
Expand All @@ -494,7 +499,7 @@ func assignChild(i, compressedParent int, parentNode *Node,
// ProofToPath returns a set of storage nodes from the root to the end of the proof path.
// The storage nodes will have the hashes of the children, but only the key of the child
// along the path outlined by the proof.
func ProofToPath(proofNodes []ProofNode, leafKey *Key, hashF hashFunc) ([]StorageNode, error) {
func ProofToPath(proofNodes []ProofNode, leafKey *Key, hashF HashFunc) ([]StorageNode, error) {
pathNodes := []StorageNode{}

// Child keys that can't be derived are set to nilKey, so that we can store the node
Expand Down Expand Up @@ -552,7 +557,7 @@ func ProofToPath(proofNodes []ProofNode, leafKey *Key, hashF hashFunc) ([]Storag
return pathNodes, nil
}

func skipNode(pNode ProofNode, pathNodes []StorageNode, hashF hashFunc) bool {
func skipNode(pNode ProofNode, pathNodes []StorageNode, hashF HashFunc) bool {
lastNode := pathNodes[len(pathNodes)-1].node
noLeftMatch, noRightMatch := false, false
if lastNode.LeftHash != nil && !pNode.Hash(hashF).Equal(lastNode.LeftHash) {
Expand Down Expand Up @@ -607,7 +612,7 @@ func getParentKey(idx int, compressedParentOffset uint8, leafKey *Key,
return crntKey, err
}

func getChildKey(childIdx int, crntKey, leafKey, nilKey *Key, proofNodes []ProofNode, hashF hashFunc) (*Key, error) {
func getChildKey(childIdx int, crntKey, leafKey, nilKey *Key, proofNodes []ProofNode, hashF HashFunc) (*Key, error) {
if childIdx > len(proofNodes)-1 {
return nilKey, nil
}
Expand Down
10 changes: 7 additions & 3 deletions core/trie/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/NethermindEth/juno/db"
)

type hashFunc func(*felt.Felt, *felt.Felt) *felt.Felt
type HashFunc func(*felt.Felt, *felt.Felt) *felt.Felt

// Trie is a dense Merkle Patricia Trie (i.e., all internal nodes have two children).
//
Expand All @@ -37,7 +37,7 @@ type Trie struct {
rootKey *Key
maxKey *felt.Felt
storage *Storage
hash hashFunc
hash HashFunc

dirtyNodes []*Key
rootKeyIsDirty bool
Expand All @@ -53,7 +53,7 @@ func NewTriePoseidon(storage *Storage, height uint8) (*Trie, error) {
return newTrie(storage, height, crypto.Poseidon)
}

func newTrie(storage *Storage, height uint8, hash hashFunc) (*Trie, error) {
func newTrie(storage *Storage, height uint8, hash HashFunc) (*Trie, error) {
if height > felt.Bits {
return nil, fmt.Errorf("max trie height is %d, got: %d", felt.Bits, height)
}
Expand Down Expand Up @@ -668,6 +668,10 @@ func (t *Trie) RootKey() *Key {
return t.rootKey
}

func (t *Trie) HashFunc() HashFunc {
return t.hash
}

func (t *Trie) Dump() {
t.dump(0, nil)
}
Expand Down
Loading

0 comments on commit 1e50ea2

Please sign in to comment.