From 1c1f8ca2e847f7793a62bff9b4fa9e1771953d13 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 29 Apr 2024 16:50:44 +0300 Subject: [PATCH 01/31] implement getProof and verifyProof on trie --- core/trie/proof.go | 80 +++++++++++++++++++++++++++++++++++ core/trie/proof_test.go | 93 +++++++++++++++++++++++++++++++++++++++++ core/trie/trie.go | 10 +++++ 3 files changed, 183 insertions(+) create mode 100644 core/trie/proof.go create mode 100644 core/trie/proof_test.go diff --git a/core/trie/proof.go b/core/trie/proof.go new file mode 100644 index 0000000000..83ec9ce557 --- /dev/null +++ b/core/trie/proof.go @@ -0,0 +1,80 @@ +package trie + +import ( + "errors" + + "github.com/NethermindEth/juno/core/crypto" + "github.com/NethermindEth/juno/core/felt" +) + +type Membership int + +const ( + Member Membership = iota + NonMember +) + +type ProofNode struct { + LeftHash *felt.Felt + RightHash *felt.Felt +} + +// https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L514 +func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { + leafKey := tri.feltToKey(leaf) + nodesToLeaf, err := tri.nodesFromRoot(&leafKey) + if err != nil { + return nil, err + } + proofNodes := make([]ProofNode, len(nodesToLeaf)-1) + + getHash := func(tri *Trie, key *Key) (*felt.Felt, error) { + keyFelt := key.Felt() + node, err := tri.GetNode(&keyFelt) + if err != nil { + return nil, err + } + return node.Hash(key, crypto.Pedersen), nil + } + + for i, sNode := range nodesToLeaf[:len(nodesToLeaf)-1] { + leftHash, err := getHash(tri, sNode.node.Left) + if err != nil { + return nil, err + } + rightHash, err := getHash(tri, sNode.node.Right) + if err != nil { + return nil, err + } + proofNodes[i] = ProofNode{ + LeftHash: leftHash, + RightHash: rightHash, + } + } + return proofNodes, nil +} + +// 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, leafPath *Key, leafHash felt.Felt, proofNodes []ProofNode, hashFunc hashFunc) (Membership, error) { + expectedHash := root + + for i, pNode := range proofNodes { + pNodeHash := hashFunc(pNode.LeftHash, pNode.RightHash) + if !expectedHash.Equal(pNodeHash) { + return NonMember, errors.New("proof node does not have expected hash") + } + + if leafPath.Test(leafPath.Len() - uint8(i) - 1) { // Todo: are we selecting the correct child here?? + expectedHash = pNode.RightHash + } else { + expectedHash = pNode.LeftHash + } + } + + if !expectedHash.Equal(&leafHash) { + return NonMember, errors.New("value does not match") + } + + return Member, nil +} diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go new file mode 100644 index 0000000000..d93bae320e --- /dev/null +++ b/core/trie/proof_test.go @@ -0,0 +1,93 @@ +package trie_test + +import ( + "testing" + + "github.com/NethermindEth/juno/core/crypto" + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/juno/core/trie" + "github.com/NethermindEth/juno/db/pebble" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func buildSimpleTrie(t *testing.T) *trie.Trie { + // Build trie + memdb := pebble.NewMemTest(t) + txn, err := memdb.NewTransaction(true) + require.NoError(t, err) + + tempTrie, err := trie.NewTriePedersen(trie.NewStorage(txn, []byte{1}), 251) + require.NoError(t, err) + + // Update trie + key1 := new(felt.Felt).SetUint64(1) + key2 := new(felt.Felt).SetUint64(2) + value1 := new(felt.Felt).SetUint64(123) + value2 := new(felt.Felt).SetUint64(456) + + _, err = tempTrie.Put(key1, value1) + require.NoError(t, err) + + _, err = tempTrie.Put(key2, value2) + require.NoError(t, err) + + require.NoError(t, tempTrie.Commit()) + return tempTrie +} + +func getProofNode(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { + getHash := func(tri *trie.Trie, key *trie.Key) (*felt.Felt, error) { + keyFelt := key.Felt() + node2, err := tri.GetNode(&keyFelt) + if err != nil { + return nil, err + } + return node2.Hash(key, crypto.Pedersen), nil + } + + left, err := getHash(tri, node.Left) + require.NoError(t, err) + right, err := getHash(tri, node.Right) + require.NoError(t, err) + + return trie.ProofNode{LeftHash: left, RightHash: right} +} + +func TestGetProofs(t *testing.T) { + t.Run("Simple Trie", func(t *testing.T) { + tempTrie := buildSimpleTrie(t) + + rootNode, err := tempTrie.GetRootNode() + require.NoError(t, err) + rootProofNode := getProofNode(t, tempTrie, rootNode) + expectedProofNodes := []trie.ProofNode{rootProofNode} + + proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(1), tempTrie) + require.NoError(t, err) + + require.Equal(t, expectedProofNodes, proofNodes) + }) +} + +func TestVerifyProofs(t *testing.T) { + t.Run("Simple Trie", func(t *testing.T) { + tempTrie := buildSimpleTrie(t) + + rootNode, err := tempTrie.GetRootNode() + require.NoError(t, err) + rootNodeProof := getProofNode(t, tempTrie, rootNode) + + key1 := new(felt.Felt).SetUint64(1) + key1Bytes := key1.Bytes() + key1Key := trie.NewKey(251, key1Bytes[:]) + + proofNodes := []trie.ProofNode{rootNodeProof} + root := crypto.Pedersen(rootNodeProof.LeftHash, rootNodeProof.RightHash) + + verifiedKey1, err := trie.VerifyProof(root, &key1Key, *rootNodeProof.LeftHash, proofNodes, crypto.Pedersen) + assert.NoError(t, err) + + assert.Equal(t, trie.Member, verifiedKey1) + }) +} diff --git a/core/trie/trie.go b/core/trie/trie.go index 1a5ce7f00a..9c347122fd 100644 --- a/core/trie/trie.go +++ b/core/trie/trie.go @@ -182,6 +182,16 @@ func (t *Trie) Get(key *felt.Felt) (*felt.Felt, error) { return &leafValue, nil } +func (t *Trie) GetRootNode() (*Node, error) { + return t.storage.Get(t.rootKey) +} + +// GetNode returns the node for a given key. Note: it doesn't work for root nodes. +func (t *Trie) GetNode(key *felt.Felt) (*Node, error) { + storageKey := t.feltToKey(key) + return t.storage.Get(&storageKey) +} + // check if we are updating an existing leaf, if yes avoid traversing the trie func (t *Trie) updateLeaf(nodeKey Key, node *Node, value *felt.Felt) (*felt.Felt, error) { // Check if we are updating an existing leaf From 05d87da090fb58a9e9dc386ecd37e21cdc309340 Mon Sep 17 00:00:00 2001 From: rian Date: Thu, 2 May 2024 10:24:04 +0300 Subject: [PATCH 02/31] address two comments --- core/trie/proof.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 83ec9ce557..e8c9a6bca2 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -26,9 +26,10 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { if err != nil { return nil, err } - proofNodes := make([]ProofNode, len(nodesToLeaf)-1) + nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] + proofNodes := make([]ProofNode, len(nodesExcludingLeaf)) - getHash := func(tri *Trie, key *Key) (*felt.Felt, error) { + getHash := func(key *Key) (*felt.Felt, error) { keyFelt := key.Felt() node, err := tri.GetNode(&keyFelt) if err != nil { @@ -37,12 +38,12 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { return node.Hash(key, crypto.Pedersen), nil } - for i, sNode := range nodesToLeaf[:len(nodesToLeaf)-1] { - leftHash, err := getHash(tri, sNode.node.Left) + for i, sNode := range nodesExcludingLeaf { + leftHash, err := getHash(sNode.node.Left) if err != nil { return nil, err } - rightHash, err := getHash(tri, sNode.node.Right) + rightHash, err := getHash(sNode.node.Right) if err != nil { return nil, err } From 7a4dbfe11f3b12e9463bd4b7bfa56e993e3faa8b Mon Sep 17 00:00:00 2001 From: rian Date: Thu, 2 May 2024 10:29:25 +0300 Subject: [PATCH 03/31] change VerifyProof signature --- core/trie/proof.go | 15 ++++----------- core/trie/proof_test.go | 5 +---- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index e8c9a6bca2..5ea969a459 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -7,13 +7,6 @@ import ( "github.com/NethermindEth/juno/core/felt" ) -type Membership int - -const ( - Member Membership = iota - NonMember -) - type ProofNode struct { LeftHash *felt.Felt RightHash *felt.Felt @@ -57,13 +50,13 @@ func GetProof(leaf *felt.Felt, 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, leafPath *Key, leafHash felt.Felt, proofNodes []ProofNode, hashFunc hashFunc) (Membership, error) { +func VerifyProof(root *felt.Felt, leafPath *Key, leafHash felt.Felt, proofNodes []ProofNode, hashFunc hashFunc) error { expectedHash := root for i, pNode := range proofNodes { pNodeHash := hashFunc(pNode.LeftHash, pNode.RightHash) if !expectedHash.Equal(pNodeHash) { - return NonMember, errors.New("proof node does not have expected hash") + return errors.New("proof node does not have the expected hash") } if leafPath.Test(leafPath.Len() - uint8(i) - 1) { // Todo: are we selecting the correct child here?? @@ -74,8 +67,8 @@ func VerifyProof(root *felt.Felt, leafPath *Key, leafHash felt.Felt, proofNodes } if !expectedHash.Equal(&leafHash) { - return NonMember, errors.New("value does not match") + return errors.New("leafHash does not have the expected hash") } - return Member, nil + return nil } diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index d93bae320e..b884795e36 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -85,9 +85,6 @@ func TestVerifyProofs(t *testing.T) { proofNodes := []trie.ProofNode{rootNodeProof} root := crypto.Pedersen(rootNodeProof.LeftHash, rootNodeProof.RightHash) - verifiedKey1, err := trie.VerifyProof(root, &key1Key, *rootNodeProof.LeftHash, proofNodes, crypto.Pedersen) - assert.NoError(t, err) - - assert.Equal(t, trie.Member, verifiedKey1) + assert.NoError(t, trie.VerifyProof(root, &key1Key, *rootNodeProof.LeftHash, proofNodes, crypto.Pedersen)) }) } From f4a5dd9d9d84bbe1bdd9ca0d83a98f093d986bae Mon Sep 17 00:00:00 2001 From: rian Date: Thu, 2 May 2024 11:06:33 +0300 Subject: [PATCH 04/31] remove todo --- core/trie/proof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 5ea969a459..6ac33d6392 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -59,7 +59,7 @@ func VerifyProof(root *felt.Felt, leafPath *Key, leafHash felt.Felt, proofNodes return errors.New("proof node does not have the expected hash") } - if leafPath.Test(leafPath.Len() - uint8(i) - 1) { // Todo: are we selecting the correct child here?? + if leafPath.Test(leafPath.Len() - uint8(i) - 1) { expectedHash = pNode.RightHash } else { expectedHash = pNode.LeftHash From 65887a9daded17537ce29ebb57644ae5bcfba9e8 Mon Sep 17 00:00:00 2001 From: rian Date: Fri, 3 May 2024 12:57:44 +0300 Subject: [PATCH 05/31] edge nodes - wip --- core/trie/proof.go | 114 ++++++++++++++++++++++++++++++---------- core/trie/proof_test.go | 41 ++++++++------- 2 files changed, 107 insertions(+), 48 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 6ac33d6392..c4a8b54700 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -2,16 +2,29 @@ package trie import ( "errors" + "fmt" "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" ) +// https://github.com/starknet-io/starknet-p2p-specs/blob/main/p2p/proto/snapshot.proto#L6 type ProofNode struct { + Binary *Binary + Edge *Edge +} + +type Binary struct { LeftHash *felt.Felt RightHash *felt.Felt } +type Edge struct { + Child *felt.Felt + Path *felt.Felt + Value *felt.Felt +} + // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L514 func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { leafKey := tri.feltToKey(leaf) @@ -32,43 +45,86 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { } for i, sNode := range nodesExcludingLeaf { - leftHash, err := getHash(sNode.node.Left) - if err != nil { - return nil, err - } - rightHash, err := getHash(sNode.node.Right) - if err != nil { - return nil, err - } - proofNodes[i] = ProofNode{ - LeftHash: leftHash, - RightHash: rightHash, + // Determind if binary or edge + sLeft := sNode.node.Left + sRight := sNode.node.Right + if sLeft != nil && sRight != nil { // sNode is (edge) Binary Node + leftHash, err := getHash(sNode.node.Left) + if err != nil { + return nil, err + } + rightHash, err := getHash(sNode.node.Right) + if err != nil { + return nil, err + } + proofNodes[i] = ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + } + + } else { // sNode is Edge Node + nxtNode := nodesToLeaf[i+1].node + nxtKey := nodesToLeaf[i+1].key + fmt.Println("sNode", sNode) + fmt.Println("sNode", sNode.node) + fmt.Println("sNode", sNode.node.Left) + fmt.Println("sNode", sNode.node.Right) + fmt.Println("nxtNode", nxtNode) + + fmt.Println("nxtNode", nxtNode.Left) + fmt.Println("nxtNode", nxtNode.Right) + + // Juno doesn't have a notion of an edge node, so we construct it here + edgePath, ok := findCommonKey(nxtKey, sNode.key) + if !ok { + return nil, errors.New("failed to get edge node path") + } + edgePathFelt := edgePath.Felt() + + var childKey felt.Felt + if sNode.key.Test(sNode.key.len - nxtKey.len - 1) { // Todo: double -check + childKey = sNode.node.Right.Felt() + } else { + childKey = sNode.node.Left.Felt() + } + + proofNodes[i] = ProofNode{ + Edge: &Edge{ + Path: &edgePathFelt, + Child: &childKey, + // Value: value, // Todo: ?? + }, + } } + } return proofNodes, nil } // 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, leafPath *Key, leafHash felt.Felt, proofNodes []ProofNode, hashFunc hashFunc) error { - expectedHash := root +// func VerifyProof(root *felt.Felt, leafPath *Key, leafHash felt.Felt, proofNodes []ProofNode, hashFunc hashFunc) error { +// expectedHash := root - for i, pNode := range proofNodes { - pNodeHash := hashFunc(pNode.LeftHash, pNode.RightHash) - if !expectedHash.Equal(pNodeHash) { - return errors.New("proof node does not have the expected hash") - } +// for i, pNode := range proofNodes { +// pNodeHash := hashFunc(pNode.LeftHash, pNode.RightHash) +// if !expectedHash.Equal(pNodeHash) { +// return errors.New("proof node does not have the expected hash") +// } - if leafPath.Test(leafPath.Len() - uint8(i) - 1) { - expectedHash = pNode.RightHash - } else { - expectedHash = pNode.LeftHash - } - } +// if leafPath.Test(leafPath.Len() - uint8(i) - 1) { +// expectedHash = pNode.RightHash +// } else { +// expectedHash = pNode.LeftHash +// } - if !expectedHash.Equal(&leafHash) { - return errors.New("leafHash does not have the expected hash") - } +// } - return nil -} +// if !expectedHash.Equal(&leafHash) { +// return errors.New("leafHash does not have the expected hash") +// } + +// return nil +// } diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index b884795e36..533dacc428 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -7,7 +7,6 @@ import ( "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/core/trie" "github.com/NethermindEth/juno/db/pebble" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -36,7 +35,7 @@ func buildSimpleTrie(t *testing.T) *trie.Trie { return tempTrie } -func getProofNode(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { +func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { getHash := func(tri *trie.Trie, key *trie.Key) (*felt.Felt, error) { keyFelt := key.Felt() node2, err := tri.GetNode(&keyFelt) @@ -51,16 +50,20 @@ func getProofNode(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode right, err := getHash(tri, node.Right) require.NoError(t, err) - return trie.ProofNode{LeftHash: left, RightHash: right} + return trie.ProofNode{ + Binary: &trie.Binary{ + LeftHash: left, RightHash: right}, + } + } func TestGetProofs(t *testing.T) { - t.Run("Simple Trie", func(t *testing.T) { + t.Run("Simple Trie - edge", func(t *testing.T) { tempTrie := buildSimpleTrie(t) rootNode, err := tempTrie.GetRootNode() require.NoError(t, err) - rootProofNode := getProofNode(t, tempTrie, rootNode) + rootProofNode := getProofNodeBinary(t, tempTrie, rootNode) expectedProofNodes := []trie.ProofNode{rootProofNode} proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(1), tempTrie) @@ -70,21 +73,21 @@ func TestGetProofs(t *testing.T) { }) } -func TestVerifyProofs(t *testing.T) { - t.Run("Simple Trie", func(t *testing.T) { - tempTrie := buildSimpleTrie(t) +// func TestVerifyProofs(t *testing.T) { +// t.Run("Simple Trie", func(t *testing.T) { +// tempTrie := buildSimpleTrie(t) - rootNode, err := tempTrie.GetRootNode() - require.NoError(t, err) - rootNodeProof := getProofNode(t, tempTrie, rootNode) +// rootNode, err := tempTrie.GetRootNode() +// require.NoError(t, err) +// rootNodeProof := getProofNode(t, tempTrie, rootNode) - key1 := new(felt.Felt).SetUint64(1) - key1Bytes := key1.Bytes() - key1Key := trie.NewKey(251, key1Bytes[:]) +// key1 := new(felt.Felt).SetUint64(1) +// key1Bytes := key1.Bytes() +// key1Key := trie.NewKey(251, key1Bytes[:]) - proofNodes := []trie.ProofNode{rootNodeProof} - root := crypto.Pedersen(rootNodeProof.LeftHash, rootNodeProof.RightHash) +// proofNodes := []trie.ProofNode{rootNodeProof} +// root := crypto.Pedersen(rootNodeProof.LeftHash, rootNodeProof.RightHash) - assert.NoError(t, trie.VerifyProof(root, &key1Key, *rootNodeProof.LeftHash, proofNodes, crypto.Pedersen)) - }) -} +// assert.NoError(t, trie.VerifyProof(root, &key1Key, *rootNodeProof.LeftHash, proofNodes, crypto.Pedersen)) +// }) +// } From 53753d1bcc6a565294885b202ea5f410fed7e497 Mon Sep 17 00:00:00 2001 From: rian Date: Fri, 3 May 2024 17:16:03 +0300 Subject: [PATCH 06/31] outline algo fro edge and binary --- core/trie/key.go | 18 +++++++++++ core/trie/proof.go | 74 ++++++++++++++++++---------------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/core/trie/key.go b/core/trie/key.go index f8f4f06734..3915abf202 100644 --- a/core/trie/key.go +++ b/core/trie/key.go @@ -114,3 +114,21 @@ func (k *Key) Truncate(length uint8) { inUseBytes[0] = (inUseBytes[0] << unusedBitsCount) >> unusedBitsCount } } + +func (k *Key) RemoveLastBit() { + if k.len == 0 { + return + } + + k.len-- + + unusedBytes := k.unusedBytes() + clear(unusedBytes) + + // clear upper bits on the last used byte + inUseBytes := k.inUseBytes() + unusedBitsCount := 8 - (k.len % 8) + if unusedBitsCount != 8 && len(inUseBytes) > 0 { + inUseBytes[0] = (inUseBytes[0] << unusedBitsCount) >> unusedBitsCount + } +} diff --git a/core/trie/proof.go b/core/trie/proof.go index c4a8b54700..3d257b6fae 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -1,9 +1,6 @@ package trie import ( - "errors" - "fmt" - "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" ) @@ -44,62 +41,47 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { return node.Hash(key, crypto.Pedersen), nil } + // The spec requires Edge nodes. However Juno doesn't have edge nodes. + // if Len=250 and have left and right -> create a Binary + // if len=250 and only have one child -> create edge + // if len!=250, check if child-parent length = 1, if so ,create Binary + // if len!=250, check if child-parent length = 1, if not ,create Edge for i, sNode := range nodesExcludingLeaf { - // Determind if binary or edge - sLeft := sNode.node.Left - sRight := sNode.node.Right - if sLeft != nil && sRight != nil { // sNode is (edge) Binary Node - leftHash, err := getHash(sNode.node.Left) - if err != nil { - return nil, err - } - rightHash, err := getHash(sNode.node.Right) - if err != nil { - return nil, err - } - proofNodes[i] = ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, - }, - } + // sLeft := sNode.node.Left + // sRight := sNode.node.Right + nxtNode := nodesToLeaf[i+1] - } else { // sNode is Edge Node - nxtNode := nodesToLeaf[i+1].node - nxtKey := nodesToLeaf[i+1].key - fmt.Println("sNode", sNode) - fmt.Println("sNode", sNode.node) - fmt.Println("sNode", sNode.node.Left) - fmt.Println("sNode", sNode.node.Right) - fmt.Println("nxtNode", nxtNode) - - fmt.Println("nxtNode", nxtNode.Left) - fmt.Println("nxtNode", nxtNode.Right) - - // Juno doesn't have a notion of an edge node, so we construct it here - edgePath, ok := findCommonKey(nxtKey, sNode.key) - if !ok { - return nil, errors.New("failed to get edge node path") - } + if nxtNode.key.len-sNode.key.len > 1 { // split node into edge + child + edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) + edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side edgePathFelt := edgePath.Felt() - var childKey felt.Felt - if sNode.key.Test(sNode.key.len - nxtKey.len - 1) { // Todo: double -check - childKey = sNode.node.Right.Felt() - } else { - childKey = sNode.node.Left.Felt() - } + childKeyFelt := sNode.key.Felt() proofNodes[i] = ProofNode{ Edge: &Edge{ Path: &edgePathFelt, - Child: &childKey, + Child: &childKeyFelt, // Value: value, // Todo: ?? }, } } - + leftHash, err := getHash(sNode.node.Left) + if err != nil { + return nil, err + } + rightHash, err := getHash(sNode.node.Right) + if err != nil { + return nil, err + } + proofNodes[i] = ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + } } + return proofNodes, nil } From b374e0cf7b4cce7415143e6cd70a55f152cda0fc Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 11:04:21 +0300 Subject: [PATCH 07/31] update GetProof to acocunt for edge nodes --- core/trie/proof.go | 47 ++++++++++++++++++++++++++++------------------ core/trie/trie.go | 5 +++++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 3d257b6fae..78c4970981 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -1,7 +1,6 @@ package trie import ( - "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" ) @@ -32,45 +31,57 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] proofNodes := make([]ProofNode, len(nodesExcludingLeaf)) - getHash := func(key *Key) (*felt.Felt, error) { - keyFelt := key.Felt() - node, err := tri.GetNode(&keyFelt) + // Edge nodes are defined as having a child with len greater than 1 from the parent + isEdge := func(sNode *storageNode) bool { + sNodeLen := sNode.key.len + lNodeLen := sNode.node.Right.len + rNodeLen := sNode.node.Left.len + return (lNodeLen-sNodeLen > 1) || (rNodeLen-sNodeLen > 1) + } + + // The child key may be an edge node. If so, we only take the "edge" section of the path. + getChildKey := func(childKey *Key) (*felt.Felt, error) { + childNode, err := tri.GetNodeFromKey(childKey) if err != nil { return nil, err } - return node.Hash(key, crypto.Pedersen), nil + childSNode := storageNode{ + key: childKey, + node: childNode, + } + childKeyFelt := childKey.Felt() + if isEdge(&childSNode) { + childEdgePath := NewKey(childSNode.key.len, childSNode.key.bitset[:]) + childEdgePath.RemoveLastBit() + childKeyFelt = childEdgePath.Felt() + } + return &childKeyFelt, nil } - // The spec requires Edge nodes. However Juno doesn't have edge nodes. - // if Len=250 and have left and right -> create a Binary - // if len=250 and only have one child -> create edge - // if len!=250, check if child-parent length = 1, if so ,create Binary - // if len!=250, check if child-parent length = 1, if not ,create Edge + height := uint8(0) for i, sNode := range nodesExcludingLeaf { - // sLeft := sNode.node.Left - // sRight := sNode.node.Right - nxtNode := nodesToLeaf[i+1] + height += uint8(sNode.key.len) - if nxtNode.key.len-sNode.key.len > 1 { // split node into edge + child + if isEdge(&sNode) { // Split into Edge + Binary edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side edgePathFelt := edgePath.Felt() - childKeyFelt := sNode.key.Felt() + childKey := sNode.key.Felt() proofNodes[i] = ProofNode{ Edge: &Edge{ Path: &edgePathFelt, - Child: &childKeyFelt, + Child: &childKey, // Value: value, // Todo: ?? }, } } - leftHash, err := getHash(sNode.node.Left) + leftHash, err := getChildKey(sNode.node.Left) if err != nil { return nil, err } - rightHash, err := getHash(sNode.node.Right) + rightHash, err := getChildKey(sNode.node.Right) if err != nil { return nil, err } diff --git a/core/trie/trie.go b/core/trie/trie.go index 9c347122fd..80f51beede 100644 --- a/core/trie/trie.go +++ b/core/trie/trie.go @@ -186,6 +186,11 @@ func (t *Trie) GetRootNode() (*Node, error) { return t.storage.Get(t.rootKey) } +// GetNodeFromKey returns the node for a given key. +func (t *Trie) GetNodeFromKey(key *Key) (*Node, error) { + return t.storage.Get(key) +} + // GetNode returns the node for a given key. Note: it doesn't work for root nodes. func (t *Trie) GetNode(key *felt.Felt) (*Node, error) { storageKey := t.feltToKey(key) From 2d0f58c91d5c8c253f078ef29fc385f54daf29d6 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 11:45:19 +0300 Subject: [PATCH 08/31] test - wip --- core/trie/proof.go | 28 ++++++++++++++++++++++++++-- core/trie/proof_test.go | 34 ++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 78c4970981..b32fbc4763 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -1,6 +1,8 @@ package trie import ( + "fmt" + "github.com/NethermindEth/juno/core/felt" ) @@ -10,6 +12,21 @@ type ProofNode struct { Edge *Edge } +func (pn *ProofNode) PrettyPrint() { + + if pn.Binary != nil { + fmt.Printf(" Binary:\n") + fmt.Printf(" LeftHash: %v\n", pn.Binary.LeftHash) + fmt.Printf(" RightHash: %v\n", pn.Binary.RightHash) + } + if pn.Edge != nil { + fmt.Printf(" Edge:\n") + fmt.Printf(" Child: %v\n", pn.Edge.Child) + fmt.Printf(" Path: %v\n", pn.Edge.Path) + fmt.Printf(" Value: %v\n", pn.Edge.Value) + } +} + type Binary struct { LeftHash *felt.Felt RightHash *felt.Felt @@ -34,8 +51,14 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // Edge nodes are defined as having a child with len greater than 1 from the parent isEdge := func(sNode *storageNode) bool { sNodeLen := sNode.key.len - lNodeLen := sNode.node.Right.len - rNodeLen := sNode.node.Left.len + rNodeLen := sNodeLen + if sNode.node.Right != nil { + rNodeLen = sNode.node.Right.len + } + lNodeLen := sNodeLen + if sNode.node.Left != nil { + lNodeLen = sNode.node.Left.len + } return (lNodeLen-sNodeLen > 1) || (rNodeLen-sNodeLen > 1) } @@ -62,6 +85,7 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { for i, sNode := range nodesExcludingLeaf { height += uint8(sNode.key.len) + fmt.Println(isEdge(&sNode)) if isEdge(&sNode) { // Split into Edge + Binary edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 533dacc428..0475571dd9 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -7,6 +7,7 @@ import ( "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/core/trie" "github.com/NethermindEth/juno/db/pebble" + "github.com/NethermindEth/juno/utils" "github.com/stretchr/testify/require" ) @@ -20,10 +21,10 @@ func buildSimpleTrie(t *testing.T) *trie.Trie { require.NoError(t, err) // Update trie - key1 := new(felt.Felt).SetUint64(1) - key2 := new(felt.Felt).SetUint64(2) - value1 := new(felt.Felt).SetUint64(123) - value2 := new(felt.Felt).SetUint64(456) + key1 := new(felt.Felt).SetUint64(0) + key2 := new(felt.Felt).SetUint64(1) + value1 := new(felt.Felt).SetUint64(2) + value2 := new(felt.Felt).SetUint64(3) _, err = tempTrie.Put(key1, value1) require.NoError(t, err) @@ -61,14 +62,27 @@ func TestGetProofs(t *testing.T) { t.Run("Simple Trie - edge", func(t *testing.T) { tempTrie := buildSimpleTrie(t) - rootNode, err := tempTrie.GetRootNode() - require.NoError(t, err) - rootProofNode := getProofNodeBinary(t, tempTrie, rootNode) - expectedProofNodes := []trie.ProofNode{rootProofNode} + expectedProofNodes := []trie.ProofNode{ + { + Edge: &trie.Edge{ + Path: &felt.Zero, + Child: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000002"), + RightHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000003"), + }, + }, + } - proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(1), tempTrie) + proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(0), tempTrie) require.NoError(t, err) - + for _, pNode := range proofNodes { + pNode.PrettyPrint() + } + require.Equal(t, len(expectedProofNodes), len(proofNodes)) require.Equal(t, expectedProofNodes, proofNodes) }) } From b40a388e9745cb5c63a0c5b7703a28384c4f2cc8 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 12:44:02 +0300 Subject: [PATCH 09/31] store hashes not keys --- core/trie/proof.go | 85 +++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index b32fbc4763..4156f01d80 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -3,6 +3,7 @@ package trie import ( "fmt" + "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" ) @@ -46,7 +47,15 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { return nil, err } nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] - proofNodes := make([]ProofNode, len(nodesExcludingLeaf)) + proofNodes := []ProofNode{} + + getHash := func(key *Key) (*felt.Felt, error) { + node, err := tri.GetNodeFromKey(key) + if err != nil { + return nil, err + } + return node.Hash(key, crypto.Pedersen), nil + } // Edge nodes are defined as having a child with len greater than 1 from the parent isEdge := func(sNode *storageNode) bool { @@ -63,58 +72,66 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { } // The child key may be an edge node. If so, we only take the "edge" section of the path. - getChildKey := func(childKey *Key) (*felt.Felt, error) { - childNode, err := tri.GetNodeFromKey(childKey) + // getChildKey := func(childKey *Key) (*felt.Felt, error) { + // childNode, err := tri.GetNodeFromKey(childKey) + // if err != nil { + // return nil, err + // } + // childSNode := storageNode{ + // key: childKey, + // node: childNode, + // } + // childKeyFelt := childKey.Felt() + // if isEdge(&childSNode) { + // childEdgePath := NewKey(childSNode.key.len, childSNode.key.bitset[:]) + // childEdgePath.RemoveLastBit() + // childKeyFelt = childEdgePath.Felt() + // } + // return &childKeyFelt, nil + // } + + rootKeyFelt := tri.rootKey.Felt() + height := uint8(0) + for _, sNode := range nodesExcludingLeaf { + height += uint8(sNode.key.len) + + leftHash, err := getHash(sNode.node.Left) if err != nil { return nil, err } - childSNode := storageNode{ - key: childKey, - node: childNode, - } - childKeyFelt := childKey.Felt() - if isEdge(&childSNode) { - childEdgePath := NewKey(childSNode.key.len, childSNode.key.bitset[:]) - childEdgePath.RemoveLastBit() - childKeyFelt = childEdgePath.Felt() - } - return &childKeyFelt, nil - } - height := uint8(0) - for i, sNode := range nodesExcludingLeaf { - height += uint8(sNode.key.len) + rightHash, err := getHash(sNode.node.Right) + if err != nil { + return nil, err + } - fmt.Println(isEdge(&sNode)) - if isEdge(&sNode) { // Split into Edge + Binary + sNodeFelt := sNode.key.Felt() + if isEdge(&sNode) || sNodeFelt.Equal(&rootKeyFelt) { // Split into Edge + Binary // Todo: always split root?? edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side edgePathFelt := edgePath.Felt() - childKey := sNode.key.Felt() + // Todo: get childs hash (should be H(H_l, H_r)) + fmt.Println("edgePathFelt.String()", edgePathFelt.String()) + fmt.Println("childHash.String()", tri.hash(leftHash, rightHash).String()) - proofNodes[i] = ProofNode{ + proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ Path: &edgePathFelt, - Child: &childKey, + Child: tri.hash(leftHash, rightHash), // Value: value, // Todo: ?? }, - } - } - leftHash, err := getChildKey(sNode.node.Left) - if err != nil { - return nil, err - } - rightHash, err := getChildKey(sNode.node.Right) - if err != nil { - return nil, err + }) } - proofNodes[i] = ProofNode{ + + fmt.Println("LeftHash", leftHash.String()) + fmt.Println("rightHash", rightHash.String()) + proofNodes = append(proofNodes, ProofNode{ Binary: &Binary{ LeftHash: leftHash, RightHash: rightHash, }, - } + }) } return proofNodes, nil From 997c9d23c273b92ccd96e1b6516121bfa98901f8 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 12:49:39 +0300 Subject: [PATCH 10/31] GetProof tidy --- core/trie/proof.go | 33 ++++++++------------------------- core/trie/proof_test.go | 4 ++-- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 4156f01d80..216e0e9338 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -60,36 +60,20 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // Edge nodes are defined as having a child with len greater than 1 from the parent isEdge := func(sNode *storageNode) bool { sNodeLen := sNode.key.len - rNodeLen := sNodeLen if sNode.node.Right != nil { - rNodeLen = sNode.node.Right.len + if sNode.node.Right.len-sNodeLen > 1 { + return true + } } - lNodeLen := sNodeLen + if sNode.node.Left != nil { - lNodeLen = sNode.node.Left.len + if sNode.node.Left.len-sNodeLen > 1 { + return true + } } - return (lNodeLen-sNodeLen > 1) || (rNodeLen-sNodeLen > 1) + return false } - // The child key may be an edge node. If so, we only take the "edge" section of the path. - // getChildKey := func(childKey *Key) (*felt.Felt, error) { - // childNode, err := tri.GetNodeFromKey(childKey) - // if err != nil { - // return nil, err - // } - // childSNode := storageNode{ - // key: childKey, - // node: childNode, - // } - // childKeyFelt := childKey.Felt() - // if isEdge(&childSNode) { - // childEdgePath := NewKey(childSNode.key.len, childSNode.key.bitset[:]) - // childEdgePath.RemoveLastBit() - // childKeyFelt = childEdgePath.Felt() - // } - // return &childKeyFelt, nil - // } - rootKeyFelt := tri.rootKey.Felt() height := uint8(0) for _, sNode := range nodesExcludingLeaf { @@ -111,7 +95,6 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side edgePathFelt := edgePath.Felt() - // Todo: get childs hash (should be H(H_l, H_r)) fmt.Println("edgePathFelt.String()", edgePathFelt.String()) fmt.Println("childHash.String()", tri.hash(leftHash, rightHash).String()) diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 0475571dd9..400893b537 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -59,13 +59,13 @@ func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.Proo } func TestGetProofs(t *testing.T) { - t.Run("Simple Trie - edge", func(t *testing.T) { + t.Run("Simple Trie - simple binary", func(t *testing.T) { tempTrie := buildSimpleTrie(t) expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &felt.Zero, + Path: &felt.Zero, // Todo: pathfinder returns 0? But shouldn't be zero?... Child: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), }, }, From 6e6391f78d9bdcf2d410a0ddf2411089d6175a56 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 13:15:44 +0300 Subject: [PATCH 11/31] reimpl VerifyProof --- core/trie/key.go | 20 ++++++++++++ core/trie/proof.go | 72 +++++++++++++++++++++++++++-------------- core/trie/proof_test.go | 4 +-- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/core/trie/key.go b/core/trie/key.go index 3915abf202..618698c34f 100644 --- a/core/trie/key.go +++ b/core/trie/key.go @@ -23,6 +23,26 @@ func NewKey(length uint8, keyBytes []byte) Key { return k } +func (k *Key) SubKey(n uint8) *Key { + if n > k.len { + panic("n is greater than the length of the key") + } + + newKey := &Key{len: n} + copy(newKey.bitset[:], k.bitset[len(k.bitset)-int((k.len+7)/8):]) + + // Shift right by the number of bits that are not needed + shift := k.len - n + for i := len(newKey.bitset) - 1; i >= 0; i-- { + newKey.bitset[i] >>= shift + if i > 0 { + newKey.bitset[i] |= newKey.bitset[i-1] << (8 - shift) + } + } + + return newKey +} + func (k *Key) bytesNeeded() uint { const byteBits = 8 return (uint(k.len) + (byteBits - 1)) / byteBits diff --git a/core/trie/proof.go b/core/trie/proof.go index 216e0e9338..1147228519 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -13,6 +13,21 @@ type ProofNode struct { Edge *Edge } +func (pn *ProofNode) Hash() *felt.Felt { + switch { + case pn.Binary != nil: + return crypto.Pedersen(pn.Binary.LeftHash, pn.Binary.RightHash) + case pn.Edge != nil: + length := make([]byte, 32) + length[31] = pn.Edge.Path.len + pathFelt := pn.Edge.Path.Felt() + lengthFelt := new(felt.Felt).SetBytes(length) + return new(felt.Felt).Add(crypto.Pedersen(pn.Edge.Child, &pathFelt), lengthFelt) + default: + return nil + } +} + func (pn *ProofNode) PrettyPrint() { if pn.Binary != nil { @@ -35,7 +50,7 @@ type Binary struct { type Edge struct { Child *felt.Felt - Path *felt.Felt + Path *Key Value *felt.Felt } @@ -93,14 +108,14 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { if isEdge(&sNode) || sNodeFelt.Equal(&rootKeyFelt) { // Split into Edge + Binary // Todo: always split root?? edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side - edgePathFelt := edgePath.Felt() - fmt.Println("edgePathFelt.String()", edgePathFelt.String()) + epf := edgePath.Felt() + fmt.Println("edgePathFelt.String()", epf.String()) fmt.Println("childHash.String()", tri.hash(leftHash, rightHash).String()) proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ - Path: &edgePathFelt, + Path: &edgePath, // Path from that node to the leaf Child: tri.hash(leftHash, rightHash), // Value: value, // Todo: ?? }, @@ -122,26 +137,35 @@ func GetProof(leaf *felt.Felt, 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, leafPath *Key, leafHash felt.Felt, proofNodes []ProofNode, hashFunc hashFunc) error { -// expectedHash := root - -// for i, pNode := range proofNodes { -// pNodeHash := hashFunc(pNode.LeftHash, pNode.RightHash) -// if !expectedHash.Equal(pNodeHash) { -// return errors.New("proof node does not have the expected hash") -// } +func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode) bool { -// if leafPath.Test(leafPath.Len() - uint8(i) - 1) { -// expectedHash = pNode.RightHash -// } else { -// expectedHash = pNode.LeftHash -// } - -// } + if key.Len() != 251 { + return false + } -// if !expectedHash.Equal(&leafHash) { -// return errors.New("leafHash does not have the expected hash") -// } + expectedHash := root + remainingPath := key -// return nil -// } + for _, proofNode := range proofs { + if !proofNode.Hash().Equal(expectedHash) { + return false + } + switch { + case proofNode.Binary != nil: + if remainingPath.Test(remainingPath.Len() - 1) { + expectedHash = proofNode.Binary.RightHash + } else { + expectedHash = proofNode.Binary.LeftHash + } + remainingPath.RemoveLastBit() + case proofNode.Edge != nil: + // The next "proofNode.Edge.len" bits must match + if !proofNode.Edge.Path.Equal(remainingPath.SubKey(proofNode.Edge.Path.Len())) { + return false + } + expectedHash = proofNode.Edge.Child + remainingPath.Truncate(proofNode.Edge.Path.Len()) + } + } + return expectedHash.Equal(value) +} diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 400893b537..a45d6d471f 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -61,11 +61,11 @@ func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.Proo func TestGetProofs(t *testing.T) { t.Run("Simple Trie - simple binary", func(t *testing.T) { tempTrie := buildSimpleTrie(t) - + zero := trie.NewKey(250, []byte{0}) expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &felt.Zero, // Todo: pathfinder returns 0? But shouldn't be zero?... + Path: &zero, // Todo: pathfinder returns 0? But shouldn't be zero?... Child: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), }, }, From 233afe7ff697e0d03649f28fccfcc679a2b7f4e6 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 13:22:12 +0300 Subject: [PATCH 12/31] impl VerifyProof + tests --- core/trie/proof_test.go | 85 +++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index a45d6d471f..8c02f154a3 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -3,11 +3,11 @@ package trie_test import ( "testing" - "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/core/trie" "github.com/NethermindEth/juno/db/pebble" "github.com/NethermindEth/juno/utils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -36,27 +36,27 @@ func buildSimpleTrie(t *testing.T) *trie.Trie { return tempTrie } -func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { - getHash := func(tri *trie.Trie, key *trie.Key) (*felt.Felt, error) { - keyFelt := key.Felt() - node2, err := tri.GetNode(&keyFelt) - if err != nil { - return nil, err - } - return node2.Hash(key, crypto.Pedersen), nil - } - - left, err := getHash(tri, node.Left) - require.NoError(t, err) - right, err := getHash(tri, node.Right) - require.NoError(t, err) +// func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { +// getHash := func(tri *trie.Trie, key *trie.Key) (*felt.Felt, error) { +// keyFelt := key.Felt() +// node2, err := tri.GetNode(&keyFelt) +// if err != nil { +// return nil, err +// } +// return node2.Hash(key, crypto.Pedersen), nil +// } + +// left, err := getHash(tri, node.Left) +// require.NoError(t, err) +// right, err := getHash(tri, node.Right) +// require.NoError(t, err) + +// return trie.ProofNode{ +// Binary: &trie.Binary{ +// LeftHash: left, RightHash: right}, +// } - return trie.ProofNode{ - Binary: &trie.Binary{ - LeftHash: left, RightHash: right}, - } - -} +// } func TestGetProofs(t *testing.T) { t.Run("Simple Trie - simple binary", func(t *testing.T) { @@ -87,21 +87,30 @@ func TestGetProofs(t *testing.T) { }) } -// func TestVerifyProofs(t *testing.T) { -// t.Run("Simple Trie", func(t *testing.T) { -// tempTrie := buildSimpleTrie(t) - -// rootNode, err := tempTrie.GetRootNode() -// require.NoError(t, err) -// rootNodeProof := getProofNode(t, tempTrie, rootNode) - -// key1 := new(felt.Felt).SetUint64(1) -// key1Bytes := key1.Bytes() -// key1Key := trie.NewKey(251, key1Bytes[:]) - -// proofNodes := []trie.ProofNode{rootNodeProof} -// root := crypto.Pedersen(rootNodeProof.LeftHash, rootNodeProof.RightHash) +func TestVerifyProofs(t *testing.T) { + t.Run("Simple Trie", func(t *testing.T) { + tempTrie := buildSimpleTrie(t) + zero := trie.NewKey(250, []byte{0}) + expectedProofNodes := []trie.ProofNode{ + { + Edge: &trie.Edge{ + Path: &zero, // Todo: pathfinder returns 0? But shouldn't be zero?... + Child: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000002"), + RightHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000003"), + }, + }, + } -// assert.NoError(t, trie.VerifyProof(root, &key1Key, *rootNodeProof.LeftHash, proofNodes, crypto.Pedersen)) -// }) -// } + root, err := tempTrie.Root() + require.NoError(t, err) + key1Bytes := new(felt.Felt).SetUint64(0).Bytes() + key1 := trie.NewKey(251, key1Bytes[:]) + val1 := new(felt.Felt).SetUint64(2) + assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes)) + }) +} From 45eecb1cb3c769307c2140a8a6747011ff0c419c Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 13:27:50 +0300 Subject: [PATCH 13/31] add anoter test for verify proof - both pass --- core/trie/proof_test.go | 66 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 8c02f154a3..2e42c57d1d 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -36,6 +36,36 @@ func buildSimpleTrie(t *testing.T) *trie.Trie { return tempTrie } +func buildSimpleDoubleBinaryTrie(t *testing.T) *trie.Trie { + // Build trie + memdb := pebble.NewMemTest(t) + txn, err := memdb.NewTransaction(true) + require.NoError(t, err) + + tempTrie, err := trie.NewTriePedersen(trie.NewStorage(txn, []byte{1}), 251) + require.NoError(t, err) + + // Update trie + key1 := new(felt.Felt).SetUint64(0) + key2 := new(felt.Felt).SetUint64(1) + key3 := new(felt.Felt).SetUint64(3) + value1 := new(felt.Felt).SetUint64(2) + value2 := new(felt.Felt).SetUint64(3) + value3 := new(felt.Felt).SetUint64(5) + + _, err = tempTrie.Put(key1, value1) + require.NoError(t, err) + + _, err = tempTrie.Put(key2, value2) + require.NoError(t, err) + + _, err = tempTrie.Put(key3, value3) + require.NoError(t, err) + + require.NoError(t, tempTrie.Commit()) + return tempTrie +} + // func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { // getHash := func(tri *trie.Trie, key *trie.Key) (*felt.Felt, error) { // keyFelt := key.Felt() @@ -88,7 +118,8 @@ func TestGetProofs(t *testing.T) { } func TestVerifyProofs(t *testing.T) { - t.Run("Simple Trie", func(t *testing.T) { + // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L2137 + t.Run("Simple binary trie", func(t *testing.T) { tempTrie := buildSimpleTrie(t) zero := trie.NewKey(250, []byte{0}) expectedProofNodes := []trie.ProofNode{ @@ -113,4 +144,37 @@ func TestVerifyProofs(t *testing.T) { val1 := new(felt.Felt).SetUint64(2) assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes)) }) + + // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L2167 + t.Run("Simple double binary trie", func(t *testing.T) { + tempTrie := buildSimpleDoubleBinaryTrie(t) + zero := trie.NewKey(249, []byte{0}) + expectedProofNodes := []trie.ProofNode{ + { + Edge: &trie.Edge{ + Path: &zero, // Todo: 0x7469c4000fe0 ??? + Child: utils.HexToFelt(t, "0x055C81F6A791FD06FC2E2CCAD922397EC76C3E35F2E06C0C0D43D551005A8DEA"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), + RightHash: utils.HexToFelt(t, "0x07C5BC1CC68B7BC8CA2F632DE98297E6DA9594FA23EDE872DD2ABEAFDE353B43"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000002"), + RightHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000003"), + }, + }, + } + + root, err := tempTrie.Root() + require.NoError(t, err) + key1Bytes := new(felt.Felt).SetUint64(0).Bytes() + key1 := trie.NewKey(251, key1Bytes[:]) + val1 := new(felt.Felt).SetUint64(2) + assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes)) + }) } From a8dc36a54a1c5f02d5e3e9f9fee243423296961a Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 15:51:25 +0300 Subject: [PATCH 14/31] TestGetProofs passes --- core/trie/proof.go | 17 +++++++---------- core/trie/proof_test.go | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 1147228519..e9316776a7 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -69,7 +69,8 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { if err != nil { return nil, err } - return node.Hash(key, crypto.Pedersen), nil + return node.Value, nil + // return node.Hash(key, crypto.Pedersen), nil } // Edge nodes are defined as having a child with len greater than 1 from the parent @@ -106,17 +107,13 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { sNodeFelt := sNode.key.Felt() if isEdge(&sNode) || sNodeFelt.Equal(&rootKeyFelt) { // Split into Edge + Binary // Todo: always split root?? - edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) - edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side - - epf := edgePath.Felt() - fmt.Println("edgePathFelt.String()", epf.String()) - fmt.Println("childHash.String()", tri.hash(leftHash, rightHash).String()) + // edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) + // edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ - Path: &edgePath, // Path from that node to the leaf - Child: tri.hash(leftHash, rightHash), + Path: sNode.key, // Todo: Path from that node to the leaf? + Child: sNode.node.Value, // Value: value, // Todo: ?? }, }) @@ -160,7 +157,7 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode remainingPath.RemoveLastBit() case proofNode.Edge != nil: // The next "proofNode.Edge.len" bits must match - if !proofNode.Edge.Path.Equal(remainingPath.SubKey(proofNode.Edge.Path.Len())) { + if !proofNode.Edge.Path.Equal(remainingPath.SubKey(proofNode.Edge.Path.Len())) { // Todo: Isn't edge.path from root? and remaining from edge to leaf?? return false } expectedHash = proofNode.Edge.Child diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 2e42c57d1d..fde1ac70e6 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -1,6 +1,7 @@ package trie_test import ( + "fmt" "testing" "github.com/NethermindEth/juno/core/felt" @@ -91,6 +92,7 @@ func buildSimpleDoubleBinaryTrie(t *testing.T) *trie.Trie { func TestGetProofs(t *testing.T) { t.Run("Simple Trie - simple binary", func(t *testing.T) { tempTrie := buildSimpleTrie(t) + zero := trie.NewKey(250, []byte{0}) expectedProofNodes := []trie.ProofNode{ { @@ -109,10 +111,25 @@ func TestGetProofs(t *testing.T) { proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(0), tempTrie) require.NoError(t, err) + + // Better inspection for _, pNode := range proofNodes { pNode.PrettyPrint() } require.Equal(t, len(expectedProofNodes), len(proofNodes)) + for i, proof := range expectedProofNodes { + if proof.Binary != nil { + fmt.Println(proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) + fmt.Println(proof.Binary.RightHash.String(), expectedProofNodes[i].Binary.RightHash.String()) + require.Equal(t, proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) + require.Equal(t, proof.Binary.RightHash, expectedProofNodes[i].Binary.RightHash) + } else { + fmt.Println(proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) + fmt.Println(proof.Edge.Path.String(), expectedProofNodes[i].Edge.Path.String()) + require.Equal(t, proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) + require.Equal(t, proof.Edge.Path, expectedProofNodes[i].Edge.Path) + } + } require.Equal(t, expectedProofNodes, proofNodes) }) } From ce5549a564619bc8a496e8c193cb2bdf36113328 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 16:29:42 +0300 Subject: [PATCH 15/31] GetProof.. --- core/trie/proof.go | 10 +++-- core/trie/proof_test.go | 98 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index e9316776a7..3503c338d3 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -76,6 +76,9 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // Edge nodes are defined as having a child with len greater than 1 from the parent isEdge := func(sNode *storageNode) bool { sNodeLen := sNode.key.len + if sNodeLen == 250 { // todo: What about edge leaf?? + return false + } if sNode.node.Right != nil { if sNode.node.Right.len-sNodeLen > 1 { return true @@ -90,8 +93,9 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { return false } - rootKeyFelt := tri.rootKey.Felt() height := uint8(0) + + rootHack := true for _, sNode := range nodesExcludingLeaf { height += uint8(sNode.key.len) @@ -105,8 +109,8 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { return nil, err } - sNodeFelt := sNode.key.Felt() - if isEdge(&sNode) || sNodeFelt.Equal(&rootKeyFelt) { // Split into Edge + Binary // Todo: always split root?? + if isEdge(&sNode) || rootHack { // Split into Edge + Binary // Todo: always split root?? + rootHack = false // edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) // edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index fde1ac70e6..e697628fde 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -132,6 +132,104 @@ func TestGetProofs(t *testing.T) { } require.Equal(t, expectedProofNodes, proofNodes) }) + + t.Run("Simple Trie - simple double binary", func(t *testing.T) { + tempTrie := buildSimpleDoubleBinaryTrie(t) + + zero := trie.NewKey(249, []byte{0}) + expectedProofNodes := []trie.ProofNode{ + { + Edge: &trie.Edge{ + Path: &zero, // Todo: 0x7469c4000fe0 ??? + Child: utils.HexToFelt(t, "0x055C81F6A791FD06FC2E2CCAD922397EC76C3E35F2E06C0C0D43D551005A8DEA"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), + RightHash: utils.HexToFelt(t, "0x07C5BC1CC68B7BC8CA2F632DE98297E6DA9594FA23EDE872DD2ABEAFDE353B43"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000002"), + RightHash: utils.HexToFelt(t, "0x0000000000000000000000000000000000000000000000000000000000000003"), + }, + }, + } + + proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(0), tempTrie) + require.NoError(t, err) + + // Better inspection + for _, pNode := range proofNodes { + pNode.PrettyPrint() + } + require.Equal(t, len(expectedProofNodes), len(proofNodes)) + for i, proof := range expectedProofNodes { + if proof.Binary != nil { + fmt.Println(proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) + fmt.Println(proof.Binary.RightHash.String(), expectedProofNodes[i].Binary.RightHash.String()) + require.Equal(t, proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) + require.Equal(t, proof.Binary.RightHash, expectedProofNodes[i].Binary.RightHash) + } else { + fmt.Println(proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) + fmt.Println(proof.Edge.Path.String(), expectedProofNodes[i].Edge.Path.String()) + require.Equal(t, proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) + require.Equal(t, proof.Edge.Path, expectedProofNodes[i].Edge.Path) + } + } + require.Equal(t, expectedProofNodes, proofNodes) + }) + + t.Run("Simple Trie - simple double binary 2", func(t *testing.T) { + tempTrie := buildSimpleDoubleBinaryTrie(t) + + zero := trie.NewKey(249, []byte{0}) + value3 := new(felt.Felt).SetUint64(5) + expectedProofNodes := []trie.ProofNode{ + { + Edge: &trie.Edge{ + Path: &zero, // Todo: 0x7469c4000fe0 ??? + Child: utils.HexToFelt(t, "0x055C81F6A791FD06FC2E2CCAD922397EC76C3E35F2E06C0C0D43D551005A8DEA"), + }, + }, + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), + RightHash: utils.HexToFelt(t, "0x07C5BC1CC68B7BC8CA2F632DE98297E6DA9594FA23EDE872DD2ABEAFDE353B43"), + }, + }, + { + Edge: &trie.Edge{ + Child: value3, + }, + }, + } + + proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(3), tempTrie) + require.NoError(t, err) + + // Better inspection + for _, pNode := range proofNodes { + pNode.PrettyPrint() + } + require.Equal(t, len(expectedProofNodes), len(proofNodes)) + for i, proof := range expectedProofNodes { + if proof.Binary != nil { + fmt.Println(proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) + fmt.Println(proof.Binary.RightHash.String(), expectedProofNodes[i].Binary.RightHash.String()) + require.Equal(t, proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) + require.Equal(t, proof.Binary.RightHash, expectedProofNodes[i].Binary.RightHash) + } else { + fmt.Println(proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) + fmt.Println(proof.Edge.Path.String(), expectedProofNodes[i].Edge.Path.String()) + require.Equal(t, proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) + require.Equal(t, proof.Edge.Path, expectedProofNodes[i].Edge.Path) + } + } + require.Equal(t, expectedProofNodes, proofNodes) + }) } func TestVerifyProofs(t *testing.T) { From be5a195e54cfed8217dd6d87e4fe8dcdf87d00a0 Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 6 May 2024 16:58:08 +0300 Subject: [PATCH 16/31] GetProof juno-trie to pathfinder-trie.. --- core/trie/proof.go | 91 +++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 3503c338d3..502e02edf2 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -1,6 +1,7 @@ package trie import ( + "errors" "fmt" "github.com/NethermindEth/juno/core/crypto" @@ -64,7 +65,7 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] proofNodes := []ProofNode{} - getHash := func(key *Key) (*felt.Felt, error) { + getValue := func(key *Key) (*felt.Felt, error) { node, err := tri.GetNodeFromKey(key) if err != nil { return nil, err @@ -73,47 +74,44 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // return node.Hash(key, crypto.Pedersen), nil } - // Edge nodes are defined as having a child with len greater than 1 from the parent - isEdge := func(sNode *storageNode) bool { - sNodeLen := sNode.key.len - if sNodeLen == 250 { // todo: What about edge leaf?? - return false - } - if sNode.node.Right != nil { - if sNode.node.Right.len-sNodeLen > 1 { - return true - } - } - - if sNode.node.Left != nil { - if sNode.node.Left.len-sNodeLen > 1 { - return true - } - } - return false - } - height := uint8(0) - rootHack := true - for _, sNode := range nodesExcludingLeaf { + // 1. If it's an edge-node in pathfinders impl, we need to expand the node into an edge + binary + // -> Child should be internal node (len<251). Distance between child and parent should be > 1. + // 2. If it's a binary-node, we store binary + // -> Child should be internal node (len<251). Distance between child and parent should be 1. + // 3. If it's a binary leaf, we store binary leaf + // -> Child should be leaf (len=251). Distance between child and parent should be 1. + // 4. If it's an edge leaf, we store an edge leaf + // -> Child should be leaf (len=251). Distance between child and parent should be > 1. + + for i, sNode := range nodesExcludingLeaf { height += uint8(sNode.key.len) - leftHash, err := getHash(sNode.node.Left) + leftHash, err := getValue(sNode.node.Left) if err != nil { return nil, err } - rightHash, err := getHash(sNode.node.Right) + rightHash, err := getValue(sNode.node.Right) if err != nil { return nil, err } + fmt.Println("LeftHash", leftHash.String()) + fmt.Println("rightHash", rightHash.String()) - if isEdge(&sNode) || rootHack { // Split into Edge + Binary // Todo: always split root?? - rootHack = false - // edgePath := NewKey(sNode.key.len, sNode.key.bitset[:]) - // edgePath.RemoveLastBit() // Todo: make sure we remove it from the correct side + child := nodesToLeaf[i+1] + parentChildDistance := child.key.len - sNode.key.len + if child.key.len < 251 && parentChildDistance == 1 { // Internal Binary + proofNodes = append(proofNodes, ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + }) + height++ + } else if child.key.len < 251 && parentChildDistance > 1 { // Internal Edge proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ Path: sNode.key, // Todo: Path from that node to the leaf? @@ -121,16 +119,35 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // Value: value, // Todo: ?? }, }) + height += sNode.key.len + proofNodes = append(proofNodes, ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + }) + height++ + } else if child.key.len == 251 && parentChildDistance == 1 { // Leaf binary + proofNodes = append(proofNodes, ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + }) + height++ + } else if child.key.len == 251 && parentChildDistance > 1 { // lead Edge + proofNodes = append(proofNodes, ProofNode{ + Edge: &Edge{ + // Path: sNode.key, // Todo: Path from that node to the leaf? + Child: sNode.node.Value, + // Value: value, // Todo: ?? + }, + }) + height += sNode.key.len + } else { + return nil, errors.New("unexpected error in GetProof") } - fmt.Println("LeftHash", leftHash.String()) - fmt.Println("rightHash", rightHash.String()) - proofNodes = append(proofNodes, ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, - }, - }) } return proofNodes, nil From e9d28dd4685adfe63eda83c1ca0f4df558fc0f8d Mon Sep 17 00:00:00 2001 From: rian Date: Tue, 7 May 2024 11:27:55 +0300 Subject: [PATCH 17/31] GetProof update - both tests pass --- core/trie/proof.go | 75 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 502e02edf2..026f236b43 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -14,6 +14,7 @@ type ProofNode struct { Edge *Edge } +// Note: does not work for leaves func (pn *ProofNode) Hash() *felt.Felt { switch { case pn.Binary != nil: @@ -85,6 +86,23 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // 4. If it's an edge leaf, we store an edge leaf // -> Child should be leaf (len=251). Distance between child and parent should be > 1. + // Edge nodes are defined as having a child with len greater than 1 from the parent + isEdge := func(sNode *storageNode, nodeNumFromRoot int) (bool, error) { + sNodeLen := sNode.key.len + leftKey := sNode.node.Left.len + rightKey := sNode.node.Right.len + if nodeNumFromRoot == 0 { // Is root + if sNodeLen != 1 { + return true, nil + } + return false, nil + } + if (leftKey-sNodeLen > 1) || (rightKey-sNodeLen > 1) { + return true, nil + } + return false, nil + } + for i, sNode := range nodesExcludingLeaf { height += uint8(sNode.key.len) @@ -103,15 +121,11 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { child := nodesToLeaf[i+1] parentChildDistance := child.key.len - sNode.key.len - if child.key.len < 251 && parentChildDistance == 1 { // Internal Binary - proofNodes = append(proofNodes, ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, - }, - }) - height++ - } else if child.key.len < 251 && parentChildDistance > 1 { // Internal Edge + isEdgeBool, err := isEdge(&sNode, i) + if err != nil { + return nil, err + } + if child.key.len < 251 && isEdgeBool { // Internal Edge proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ Path: sNode.key, // Todo: Path from that node to the leaf? @@ -120,6 +134,47 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { }, }) height += sNode.key.len + + // Todo: If the child is an edge, we need to the hash of it's edge form. Both children. + leftNode, err := tri.GetNodeFromKey(sNode.node.Left) + if err != nil { + return nil, err + } + leftIsEdgeBool, err := isEdge(&storageNode{node: leftNode, key: sNode.node.Left}, i) + if err != nil { + return nil, err + } + if leftIsEdgeBool { + leftEdge := ProofNode{Edge: &Edge{ + Path: sNode.node.Left, + Child: leftNode.Value, + }} + leftHash = leftEdge.Hash() + } + rightNode, err := tri.GetNodeFromKey(sNode.node.Right) + if err != nil { + return nil, err + } + rightIsEdgeBool, err := isEdge(&storageNode{node: rightNode, key: sNode.node.Right}, i) + if err != nil { + return nil, err + } + if rightIsEdgeBool { + rightEdge := ProofNode{Edge: &Edge{ + Path: sNode.node.Right, + Child: rightNode.Value, + }} + rightHash = rightEdge.Hash() + } + proofNodes = append(proofNodes, ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + }) + height++ + + } else if child.key.len < 251 && parentChildDistance == 1 { // Internal Binary proofNodes = append(proofNodes, ProofNode{ Binary: &Binary{ LeftHash: leftHash, @@ -135,7 +190,7 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { }, }) height++ - } else if child.key.len == 251 && parentChildDistance > 1 { // lead Edge + } else if child.key.len == 251 && isEdgeBool { // lead Edge proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ // Path: sNode.key, // Todo: Path from that node to the leaf? From c343913019285b23cf93890e441eed884e54240f Mon Sep 17 00:00:00 2001 From: rian Date: Tue, 7 May 2024 12:03:50 +0300 Subject: [PATCH 18/31] tidy --- core/trie/key.go | 2 +- core/trie/proof.go | 158 ++++++++++++++-------------------------- core/trie/proof_test.go | 65 ----------------- 3 files changed, 57 insertions(+), 168 deletions(-) diff --git a/core/trie/key.go b/core/trie/key.go index 618698c34f..dc946a5ef6 100644 --- a/core/trie/key.go +++ b/core/trie/key.go @@ -29,7 +29,7 @@ func (k *Key) SubKey(n uint8) *Key { } newKey := &Key{len: n} - copy(newKey.bitset[:], k.bitset[len(k.bitset)-int((k.len+7)/8):]) + copy(newKey.bitset[:], k.bitset[len(k.bitset)-int((k.len+7)/8):]) //nolint:gomnd // Shift right by the number of bits that are not needed shift := k.len - n diff --git a/core/trie/proof.go b/core/trie/proof.go index 026f236b43..854afc394f 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -20,8 +20,8 @@ func (pn *ProofNode) Hash() *felt.Felt { case pn.Binary != nil: return crypto.Pedersen(pn.Binary.LeftHash, pn.Binary.RightHash) case pn.Edge != nil: - length := make([]byte, 32) - length[31] = pn.Edge.Path.len + length := make([]byte, len(pn.Edge.Path.bitset)) + length[len(pn.Edge.Path.bitset)-1] = pn.Edge.Path.len pathFelt := pn.Edge.Path.Felt() lengthFelt := new(felt.Felt).SetBytes(length) return new(felt.Felt).Add(crypto.Pedersen(pn.Edge.Child, &pathFelt), lengthFelt) @@ -31,7 +31,6 @@ func (pn *ProofNode) Hash() *felt.Felt { } func (pn *ProofNode) PrettyPrint() { - if pn.Binary != nil { fmt.Printf(" Binary:\n") fmt.Printf(" LeftHash: %v\n", pn.Binary.LeftHash) @@ -56,6 +55,37 @@ type Edge struct { Value *felt.Felt } +func isEdge(sNode storageNode, nodeNumFromRoot int) bool { + sNodeLen := sNode.key.len + leftKey := sNode.node.Left.len + rightKey := sNode.node.Right.len + if nodeNumFromRoot == 0 { // Is root + return sNodeLen != 1 + } + if (leftKey-sNodeLen > 1) || (rightKey-sNodeLen > 1) { + return true + } + return false +} + +// The binary node uses the hash of children. If the child is an edge, we first need to represent it +// as an edge node, and then take its hash. +func getChildHash(tri *Trie, sNode storageNode, childKey *Key, nodeNumFromRoot int) (*felt.Felt, error) { + childNode, err := tri.GetNodeFromKey(childKey) + if err != nil { + return nil, err + } + leftIsEdgeBool := isEdge(storageNode{node: childNode, key: childKey}, nodeNumFromRoot) + if leftIsEdgeBool { + leftEdge := ProofNode{Edge: &Edge{ + Path: sNode.node.Left, + Child: childNode.Value, + }} + return leftEdge.Hash(), nil + } + return childNode.Value, nil +} + // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L514 func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { leafKey := tri.feltToKey(leaf) @@ -66,17 +96,6 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] proofNodes := []ProofNode{} - getValue := func(key *Key) (*felt.Felt, error) { - node, err := tri.GetNodeFromKey(key) - if err != nil { - return nil, err - } - return node.Value, nil - // return node.Hash(key, crypto.Pedersen), nil - } - - height := uint8(0) - // 1. If it's an edge-node in pathfinders impl, we need to expand the node into an edge + binary // -> Child should be internal node (len<251). Distance between child and parent should be > 1. // 2. If it's a binary-node, we store binary @@ -86,133 +105,67 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // 4. If it's an edge leaf, we store an edge leaf // -> Child should be leaf (len=251). Distance between child and parent should be > 1. - // Edge nodes are defined as having a child with len greater than 1 from the parent - isEdge := func(sNode *storageNode, nodeNumFromRoot int) (bool, error) { - sNodeLen := sNode.key.len - leftKey := sNode.node.Left.len - rightKey := sNode.node.Right.len - if nodeNumFromRoot == 0 { // Is root - if sNodeLen != 1 { - return true, nil - } - return false, nil - } - if (leftKey-sNodeLen > 1) || (rightKey-sNodeLen > 1) { - return true, nil - } - return false, nil - } - for i, sNode := range nodesExcludingLeaf { - height += uint8(sNode.key.len) - leftHash, err := getValue(sNode.node.Left) + leftHash, err := getChildHash(tri, sNode, sNode.node.Left, i) if err != nil { return nil, err } - - rightHash, err := getValue(sNode.node.Right) + rightHash, err := getChildHash(tri, sNode, sNode.node.Left, i) if err != nil { return nil, err } - fmt.Println("LeftHash", leftHash.String()) - fmt.Println("rightHash", rightHash.String()) - child := nodesToLeaf[i+1] - parentChildDistance := child.key.len - sNode.key.len + childIsInternal := nodesToLeaf[i+1].key.len < 251 + isEdgeBool := isEdge(sNode, i) - isEdgeBool, err := isEdge(&sNode, i) - if err != nil { - return nil, err - } - if child.key.len < 251 && isEdgeBool { // Internal Edge + if childIsInternal && isEdgeBool { // Internal Edge + // Juno node is split into an edge + binary. proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ - Path: sNode.key, // Todo: Path from that node to the leaf? + Path: sNode.key, Child: sNode.node.Value, // Value: value, // Todo: ?? }, - }) - height += sNode.key.len - - // Todo: If the child is an edge, we need to the hash of it's edge form. Both children. - leftNode, err := tri.GetNodeFromKey(sNode.node.Left) - if err != nil { - return nil, err - } - leftIsEdgeBool, err := isEdge(&storageNode{node: leftNode, key: sNode.node.Left}, i) - if err != nil { - return nil, err - } - if leftIsEdgeBool { - leftEdge := ProofNode{Edge: &Edge{ - Path: sNode.node.Left, - Child: leftNode.Value, - }} - leftHash = leftEdge.Hash() - } - rightNode, err := tri.GetNodeFromKey(sNode.node.Right) - if err != nil { - return nil, err - } - rightIsEdgeBool, err := isEdge(&storageNode{node: rightNode, key: sNode.node.Right}, i) - if err != nil { - return nil, err - } - if rightIsEdgeBool { - rightEdge := ProofNode{Edge: &Edge{ - Path: sNode.node.Right, - Child: rightNode.Value, - }} - rightHash = rightEdge.Hash() - } + }, + ProofNode{ + Binary: &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + }, + }) + } else if childIsInternal && !isEdgeBool { // Internal Binary proofNodes = append(proofNodes, ProofNode{ Binary: &Binary{ LeftHash: leftHash, RightHash: rightHash, }, }) - height++ - - } else if child.key.len < 251 && parentChildDistance == 1 { // Internal Binary + } else if !childIsInternal && isEdgeBool { // Leaf Edge proofNodes = append(proofNodes, ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, + Edge: &Edge{ + Child: sNode.node.Value, + // Value: value, // Todo: ?? }, }) - height++ - } else if child.key.len == 251 && parentChildDistance == 1 { // Leaf binary + } else if !childIsInternal && !isEdgeBool { // Leaf binary proofNodes = append(proofNodes, ProofNode{ Binary: &Binary{ LeftHash: leftHash, RightHash: rightHash, }, }) - height++ - } else if child.key.len == 251 && isEdgeBool { // lead Edge - proofNodes = append(proofNodes, ProofNode{ - Edge: &Edge{ - // Path: sNode.key, // Todo: Path from that node to the leaf? - Child: sNode.node.Value, - // Value: value, // Todo: ?? - }, - }) - height += sNode.key.len } else { return nil, errors.New("unexpected error in GetProof") } - } - return proofNodes, nil } // 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) bool { - - if key.Len() != 251 { + if key.Len() != key.len { return false } @@ -233,7 +186,8 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode remainingPath.RemoveLastBit() case proofNode.Edge != nil: // The next "proofNode.Edge.len" bits must match - if !proofNode.Edge.Path.Equal(remainingPath.SubKey(proofNode.Edge.Path.Len())) { // Todo: Isn't edge.path from root? and remaining from edge to leaf?? + // Todo: Isn't edge.path from root? and remaining from edge to leaf?? + if !proofNode.Edge.Path.Equal(remainingPath.SubKey(proofNode.Edge.Path.Len())) { return false } expectedHash = proofNode.Edge.Child diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index e697628fde..88d8f44dcb 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -1,7 +1,6 @@ package trie_test import ( - "fmt" "testing" "github.com/NethermindEth/juno/core/felt" @@ -67,28 +66,6 @@ func buildSimpleDoubleBinaryTrie(t *testing.T) *trie.Trie { return tempTrie } -// func getProofNodeBinary(t *testing.T, tri *trie.Trie, node *trie.Node) trie.ProofNode { -// getHash := func(tri *trie.Trie, key *trie.Key) (*felt.Felt, error) { -// keyFelt := key.Felt() -// node2, err := tri.GetNode(&keyFelt) -// if err != nil { -// return nil, err -// } -// return node2.Hash(key, crypto.Pedersen), nil -// } - -// left, err := getHash(tri, node.Left) -// require.NoError(t, err) -// right, err := getHash(tri, node.Right) -// require.NoError(t, err) - -// return trie.ProofNode{ -// Binary: &trie.Binary{ -// LeftHash: left, RightHash: right}, -// } - -// } - func TestGetProofs(t *testing.T) { t.Run("Simple Trie - simple binary", func(t *testing.T) { tempTrie := buildSimpleTrie(t) @@ -116,20 +93,6 @@ func TestGetProofs(t *testing.T) { for _, pNode := range proofNodes { pNode.PrettyPrint() } - require.Equal(t, len(expectedProofNodes), len(proofNodes)) - for i, proof := range expectedProofNodes { - if proof.Binary != nil { - fmt.Println(proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) - fmt.Println(proof.Binary.RightHash.String(), expectedProofNodes[i].Binary.RightHash.String()) - require.Equal(t, proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) - require.Equal(t, proof.Binary.RightHash, expectedProofNodes[i].Binary.RightHash) - } else { - fmt.Println(proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) - fmt.Println(proof.Edge.Path.String(), expectedProofNodes[i].Edge.Path.String()) - require.Equal(t, proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) - require.Equal(t, proof.Edge.Path, expectedProofNodes[i].Edge.Path) - } - } require.Equal(t, expectedProofNodes, proofNodes) }) @@ -165,20 +128,6 @@ func TestGetProofs(t *testing.T) { for _, pNode := range proofNodes { pNode.PrettyPrint() } - require.Equal(t, len(expectedProofNodes), len(proofNodes)) - for i, proof := range expectedProofNodes { - if proof.Binary != nil { - fmt.Println(proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) - fmt.Println(proof.Binary.RightHash.String(), expectedProofNodes[i].Binary.RightHash.String()) - require.Equal(t, proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) - require.Equal(t, proof.Binary.RightHash, expectedProofNodes[i].Binary.RightHash) - } else { - fmt.Println(proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) - fmt.Println(proof.Edge.Path.String(), expectedProofNodes[i].Edge.Path.String()) - require.Equal(t, proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) - require.Equal(t, proof.Edge.Path, expectedProofNodes[i].Edge.Path) - } - } require.Equal(t, expectedProofNodes, proofNodes) }) @@ -214,20 +163,6 @@ func TestGetProofs(t *testing.T) { for _, pNode := range proofNodes { pNode.PrettyPrint() } - require.Equal(t, len(expectedProofNodes), len(proofNodes)) - for i, proof := range expectedProofNodes { - if proof.Binary != nil { - fmt.Println(proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) - fmt.Println(proof.Binary.RightHash.String(), expectedProofNodes[i].Binary.RightHash.String()) - require.Equal(t, proof.Binary.LeftHash.String(), expectedProofNodes[i].Binary.LeftHash.String()) - require.Equal(t, proof.Binary.RightHash, expectedProofNodes[i].Binary.RightHash) - } else { - fmt.Println(proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) - fmt.Println(proof.Edge.Path.String(), expectedProofNodes[i].Edge.Path.String()) - require.Equal(t, proof.Edge.Child.String(), expectedProofNodes[i].Edge.Child.String()) - require.Equal(t, proof.Edge.Path, expectedProofNodes[i].Edge.Path) - } - } require.Equal(t, expectedProofNodes, proofNodes) }) } From 05761ef8e999b34f9f53daffe3fa2da3ced49ca2 Mon Sep 17 00:00:00 2001 From: rian Date: Tue, 7 May 2024 13:02:02 +0300 Subject: [PATCH 19/31] fix getProof test 1 --- core/trie/proof.go | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 854afc394f..4badbeec35 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -55,33 +55,40 @@ type Edge struct { Value *felt.Felt } -func isEdge(sNode storageNode, nodeNumFromRoot int) bool { +func isEdge(parentKey *Key, sNode storageNode) bool { sNodeLen := sNode.key.len - leftKey := sNode.node.Left.len - rightKey := sNode.node.Right.len - if nodeNumFromRoot == 0 { // Is root + if parentKey == nil { // Root return sNodeLen != 1 } - if (leftKey-sNodeLen > 1) || (rightKey-sNodeLen > 1) { + if sNodeLen-parentKey.len > 1 { return true } + + // leftKey := sNode.node.Left.len + // rightKey := sNode.node.Right.len + // if (leftKey-sNodeLen > 1) || (rightKey-sNodeLen > 1) { + // return true + // } return false } // The binary node uses the hash of children. If the child is an edge, we first need to represent it // as an edge node, and then take its hash. -func getChildHash(tri *Trie, sNode storageNode, childKey *Key, nodeNumFromRoot int) (*felt.Felt, error) { +func getChildHash(tri *Trie, parentKey *Key, childKey *Key) (*felt.Felt, error) { childNode, err := tri.GetNodeFromKey(childKey) if err != nil { return nil, err } - leftIsEdgeBool := isEdge(storageNode{node: childNode, key: childKey}, nodeNumFromRoot) - if leftIsEdgeBool { - leftEdge := ProofNode{Edge: &Edge{ - Path: sNode.node.Left, + + childIsEdgeBool := isEdge(parentKey, storageNode{node: childNode, key: childKey}) + if childIsEdgeBool { + fmt.Println("childKey", childKey) + fmt.Println("childNode.Value", childNode.Value) + edgeNode := ProofNode{Edge: &Edge{ + Path: childKey, Child: childNode.Value, }} - return leftEdge.Hash(), nil + return edgeNode.Hash(), nil } return childNode.Value, nil } @@ -107,19 +114,24 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { for i, sNode := range nodesExcludingLeaf { - leftHash, err := getChildHash(tri, sNode, sNode.node.Left, i) + leftHash, err := getChildHash(tri, sNode.key, sNode.node.Left) if err != nil { return nil, err } - rightHash, err := getChildHash(tri, sNode, sNode.node.Left, i) + rightHash, err := getChildHash(tri, sNode.key, sNode.node.Right) if err != nil { return nil, err } - childIsInternal := nodesToLeaf[i+1].key.len < 251 - isEdgeBool := isEdge(sNode, i) + childIsInternal := nodesToLeaf[i+1].key.len < tri.height + var isEdgeBool bool + if i == 0 { + isEdgeBool = isEdge(nil, sNode) + } else { + isEdgeBool = isEdge(nodesToLeaf[i-1].key, sNode) + } - if childIsInternal && isEdgeBool { // Internal Edge + if (childIsInternal && isEdgeBool) || (isEdgeBool && i == 0) { // Internal Edge // Juno node is split into an edge + binary. proofNodes = append(proofNodes, ProofNode{ Edge: &Edge{ From 096ee27a630b02f551395f2a35811b8629b62c00 Mon Sep 17 00:00:00 2001 From: rian Date: Tue, 7 May 2024 17:14:28 +0300 Subject: [PATCH 20/31] test error source todo --- core/trie/proof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 4badbeec35..3f447d361b 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -84,7 +84,7 @@ func getChildHash(tri *Trie, parentKey *Key, childKey *Key) (*felt.Felt, error) if childIsEdgeBool { fmt.Println("childKey", childKey) fmt.Println("childNode.Value", childNode.Value) - edgeNode := ProofNode{Edge: &Edge{ + edgeNode := ProofNode{Edge: &Edge{ // Todo: this is wrong for the key3,val 0x5 edge node in the double binary..hash is incorrect.. Path: childKey, Child: childNode.Value, }} From 57c64b6fbe9475c374582754cae6617a2fedb579 Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 10:52:57 +0300 Subject: [PATCH 21/31] add motes tests for GetProof - fail --- core/trie/key.go | 17 +++++++ core/trie/proof_test.go | 107 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/core/trie/key.go b/core/trie/key.go index dc946a5ef6..d6c4d8c36c 100644 --- a/core/trie/key.go +++ b/core/trie/key.go @@ -23,6 +23,23 @@ func NewKey(length uint8, keyBytes []byte) Key { return k } +func NewKeyFromBits(bits []int) Key { + if len(bits) > len(Key{}.bitset)*8 { + panic("bits does not fit in bitset") + } + + var keyBytes []byte + for i := 0; i < len(bits); i += 8 { + var byteVal byte + for j := 0; j < 8 && i+j < len(bits); j++ { + byteVal |= byte(bits[i+j]) << (7 - j) + } + keyBytes = append(keyBytes, byteVal) + } + + return NewKey(uint8(len(bits)), keyBytes) +} + func (k *Key) SubKey(n uint8) *Key { if n > k.len { panic("n is greater than the length of the key") diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 88d8f44dcb..8960b570fb 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -1,6 +1,7 @@ package trie_test import ( + "fmt" "testing" "github.com/NethermindEth/juno/core/felt" @@ -36,6 +37,36 @@ func buildSimpleTrie(t *testing.T) *trie.Trie { return tempTrie } +func buildSimpleBinaryRootTrie(t *testing.T) *trie.Trie { + + // (0, 0, x) + // / \ + // (250, 0, cc) (250, 11111.., dd) + // | | + // (cc) (dd) + // Build trie + memdb := pebble.NewMemTest(t) + txn, err := memdb.NewTransaction(true) + require.NoError(t, err) + + tempTrie, err := trie.NewTriePedersen(trie.NewStorage(txn, []byte{1}), 251) + require.NoError(t, err) + + key1 := new(felt.Felt).SetUint64(0) + key2 := utils.HexToFelt(t, "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + value1 := utils.HexToFelt(t, "0xcc") + value2 := utils.HexToFelt(t, "0xdd") + + _, err = tempTrie.Put(key1, value1) + require.NoError(t, err) + + _, err = tempTrie.Put(key2, value2) + require.NoError(t, err) + + require.NoError(t, tempTrie.Commit()) + return tempTrie +} + func buildSimpleDoubleBinaryTrie(t *testing.T) *trie.Trie { // Build trie memdb := pebble.NewMemTest(t) @@ -165,6 +196,82 @@ func TestGetProofs(t *testing.T) { } require.Equal(t, expectedProofNodes, proofNodes) }) + + t.Run("Simple Trie - simple binary root", func(t *testing.T) { + tempTrie := buildSimpleBinaryRootTrie(t) + value := utils.HexToFelt(t, "0xcc") + expectedProofNodes := []trie.ProofNode{ + { + Binary: &trie.Binary{ + LeftHash: utils.HexToFelt(t, "0x06E08BF82793229338CE60B65D1845F836C8E2FBFE2BC59FF24AEDBD8BA219C4"), + RightHash: utils.HexToFelt(t, "0x04F9B8E66212FB528C0C1BD02F43309C53B895AA7D9DC91180001BDD28A588FA"), + }, + }, + { + Edge: &trie.Edge{ + Child: value, + }, + }, + } + + proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(0), tempTrie) + require.NoError(t, err) + + // Better inspection + for _, pNode := range proofNodes { + pNode.PrettyPrint() + } + require.Equal(t, expectedProofNodes, proofNodes) + }) + + t.Run("Simple Trie - left-right edge", func(t *testing.T) { + // (251,0xff,0xaa) + // / + // \ + // (0xaa) + memdb := pebble.NewMemTest(t) + txn, err := memdb.NewTransaction(true) + require.NoError(t, err) + + tempTrie, err := trie.NewTriePedersen(trie.NewStorage(txn, []byte{1}), 251) + require.NoError(t, err) + + key1 := utils.HexToFelt(t, "0xff") + value1 := utils.HexToFelt(t, "0xaa") + + _, err = tempTrie.Put(key1, value1) + require.NoError(t, err) + + require.NoError(t, tempTrie.Commit()) + + path1 := trie.NewKeyFromBits([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}) + path2 := trie.NewKeyFromBits([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}) + child := utils.HexToFelt(t, "0x00000000000000000000000000000000000000000000000000000000000000AA") + qwe := path2.Felt() + fmt.Println(qwe.String()) + expectedProofNodes := []trie.ProofNode{ + { + Edge: &trie.Edge{ + Path: &path1, + }, + }, + { + Edge: &trie.Edge{ + Path: &path2, + Child: child, + }, + }, + } + + proofNodes, err := trie.GetProof(new(felt.Felt).SetUint64(0), tempTrie) + require.NoError(t, err) + + // Better inspection + for _, pNode := range proofNodes { + pNode.PrettyPrint() + } + require.Equal(t, expectedProofNodes, proofNodes) + }) } func TestVerifyProofs(t *testing.T) { From b4a76d1f8e7503cd7c57111accf2427f0928e6f7 Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 13:54:28 +0300 Subject: [PATCH 22/31] wip - path seems correct, rightHash is incorrect. Todo: edge-dge --- core/trie/proof.go | 211 +++++++++++++++++++++++++++------------- core/trie/proof_test.go | 7 +- 2 files changed, 150 insertions(+), 68 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 3f447d361b..9f2e89b701 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -1,7 +1,6 @@ package trie import ( - "errors" "fmt" "github.com/NethermindEth/juno/core/crypto" @@ -58,7 +57,7 @@ type Edge struct { func isEdge(parentKey *Key, sNode storageNode) bool { sNodeLen := sNode.key.len if parentKey == nil { // Root - return sNodeLen != 1 + return sNodeLen != 0 } if sNodeLen-parentKey.len > 1 { return true @@ -74,33 +73,86 @@ func isEdge(parentKey *Key, sNode storageNode) bool { // The binary node uses the hash of children. If the child is an edge, we first need to represent it // as an edge node, and then take its hash. -func getChildHash(tri *Trie, parentKey *Key, childKey *Key) (*felt.Felt, error) { - childNode, err := tri.GetNodeFromKey(childKey) +// func getChildHash(tri *Trie, parentKey *Key, childKey *Key) (*felt.Felt, error) { +// childNode, err := tri.GetNodeFromKey(childKey) +// if err != nil { +// return nil, err +// } + +// childIsEdgeBool := isEdge(parentKey, storageNode{node: childNode, key: childKey}) +// if childIsEdgeBool { +// fmt.Println("childKey", childKey) +// fmt.Println("childNode.Value", childNode.Value) +// edgeNode := ProofNode{Edge: &Edge{ // Todo: this is wrong for the key3,val 0x5 edge node in the double binary..hash is incorrect.. +// Path: childKey, +// Child: childNode.Value, +// }} +// return edgeNode.Hash(), nil +// } +// return childNode.Value, nil +// } + +// transformNode takes a node and splits it into an edge+binary if it's an edge node +func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary, error) { + // Internal Edge + isEdgeBool := isEdge(parentKey, sNode) + + var edge *Edge + if isEdgeBool { + edge = &Edge{ + Path: sNode.key, + Child: sNode.node.Value, + } + } + if sNode.key.len == tri.height { + return edge, nil, nil + } + lNode, err := tri.GetNodeFromKey(sNode.node.Left) if err != nil { - return nil, err + return nil, nil, err + } + rNode, err := tri.GetNodeFromKey(sNode.node.Right) + if err != nil { + return nil, nil, err } - childIsEdgeBool := isEdge(parentKey, storageNode{node: childNode, key: childKey}) - if childIsEdgeBool { - fmt.Println("childKey", childKey) - fmt.Println("childNode.Value", childNode.Value) - edgeNode := ProofNode{Edge: &Edge{ // Todo: this is wrong for the key3,val 0x5 edge node in the double binary..hash is incorrect.. - Path: childKey, - Child: childNode.Value, + rightHash := rNode.Value + if isEdge(sNode.key, storageNode{node: rNode, key: sNode.node.Right}) { + edge := ProofNode{Edge: &Edge{ + Path: sNode.node.Right, + Child: rNode.Value, + }} + fmt.Println("rightHash", rightHash) + rightHash = edge.Hash() + } + leftHash := lNode.Value + if isEdge(sNode.key, storageNode{node: lNode, key: sNode.node.Left}) { + edge := ProofNode{Edge: &Edge{ + Path: sNode.node.Left, + Child: lNode.Value, }} - return edgeNode.Hash(), nil + leftHash = edge.Hash() } - return childNode.Value, nil + binary := &Binary{ + LeftHash: leftHash, + RightHash: rightHash, + } + + return edge, binary, nil + } // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L514 +// Note: Juno nodes are Edge AND Binary, whereas pathfinders are Edge XOR Binary, so +// we need to perform a transformation as we progress along Junos Trie +// If a node is an edge transform it into edge + binary, AND do this for its children func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { leafKey := tri.feltToKey(leaf) nodesToLeaf, err := tri.nodesFromRoot(&leafKey) if err != nil { return nil, err } - nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] + // nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] proofNodes := []ProofNode{} // 1. If it's an edge-node in pathfinders impl, we need to expand the node into an edge + binary @@ -111,65 +163,90 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // -> Child should be leaf (len=251). Distance between child and parent should be 1. // 4. If it's an edge leaf, we store an edge leaf // -> Child should be leaf (len=251). Distance between child and parent should be > 1. - - for i, sNode := range nodesExcludingLeaf { - - leftHash, err := getChildHash(tri, sNode.key, sNode.node.Left) + var parentKey *Key + i := 0 + childIsLeaf := false + // for i, sNode := range nodesExcludingLeaf { // Todo: wpould be easier to loop of transformed nodes + for i < len(nodesToLeaf) { + sNode := nodesToLeaf[i] + sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) if err != nil { return nil, err } - rightHash, err := getChildHash(tri, sNode.key, sNode.node.Right) - if err != nil { - return nil, err + if sNodeEdge == nil && sNodeBinary == nil { + break } - childIsInternal := nodesToLeaf[i+1].key.len < tri.height - var isEdgeBool bool - if i == 0 { - isEdgeBool = isEdge(nil, sNode) - } else { - isEdgeBool = isEdge(nodesToLeaf[i-1].key, sNode) + if sNodeEdge != nil && !childIsLeaf { // Internal Edge + // Todo: child can be an edge + proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}, {Binary: sNodeBinary}}...) + } else if sNodeEdge == nil && !childIsLeaf { // Internal Binary + proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) + } else if sNodeEdge != nil && childIsLeaf { // pre-leaf Edge + proofNodes = append(proofNodes, []ProofNode{{Edge: &Edge{Child: sNodeEdge.Child}}}...) + } else if sNodeEdge == nil && childIsLeaf { // pre-leaf binary + proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) } - - if (childIsInternal && isEdgeBool) || (isEdgeBool && i == 0) { // Internal Edge - // Juno node is split into an edge + binary. - proofNodes = append(proofNodes, ProofNode{ - Edge: &Edge{ - Path: sNode.key, - Child: sNode.node.Value, - // Value: value, // Todo: ?? - }, - }, - ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, - }, - }) - } else if childIsInternal && !isEdgeBool { // Internal Binary - proofNodes = append(proofNodes, ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, - }, - }) - } else if !childIsInternal && isEdgeBool { // Leaf Edge - proofNodes = append(proofNodes, ProofNode{ - Edge: &Edge{ - Child: sNode.node.Value, - // Value: value, // Todo: ?? - }, - }) - } else if !childIsInternal && !isEdgeBool { // Leaf binary - proofNodes = append(proofNodes, ProofNode{ - Binary: &Binary{ - LeftHash: leftHash, - RightHash: rightHash, - }, - }) - } else { - return nil, errors.New("unexpected error in GetProof") + i++ + if i == len(nodesToLeaf) { + break } + parentKey = nodesToLeaf[i-1].key + childIsLeaf = nodesToLeaf[i].key.len == 251 + // lNode, err := tri.GetNodeFromKey(sNode.node.Left) + // if err != nil { + // return nil, err + // } + // lNodeEdge, _, err := transformNode(tri, sNode.key, storageNode{key: sNode.node.Left, node: lNode}) + // if err != nil { + // return nil, err + // } + // rNode, err := tri.GetNodeFromKey(sNode.node.Right) + // if err != nil { + // return nil, err + // } + // rNodeEdge, _, err := transformNode(tri, sNode.key, storageNode{key: sNode.node.Right, node: rNode}) + // if err != nil { + // return nil, err + // } + + // var leftHash *felt.Felt + // if lNodeEdge != nil { + // tmp := ProofNode{Edge: lNodeEdge} + // leftHash = tmp.Hash() + // } else { + // leftHash = lNode.Value + // } + // var rightHash *felt.Felt + // if rNodeEdge != nil { + // rightHash = rNodeEdge.Value + // } else { + // tmp := ProofNode{Edge: lNodeEdge} + // rightHash = tmp.Hash() + // } + // sNodeBinary = &Binary{ + // LeftHash: leftHash, + // RightHash: rightHash, + // } + + // else if !childIsInternal && isEdgeBool { // Leaf Edge eg Binary -> edge -> leaf + // proofNodes = append(proofNodes, ProofNode{ + // Edge: &Edge{ + // Child: sNode.node.Value, + // // Value: value, // Todo: ?? + // }, + // }) + // } else if !childIsInternal && !isEdgeBool { // Leaf binary + // proofNodes = append(proofNodes, ProofNode{ + // Binary: &Binary{ + // LeftHash: leftHash, + // RightHash: rightHash, + // }, + // }) + // } else { + // return nil, errors.New("unexpected error in GetProof") + // } + } return proofNodes, nil } diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 8960b570fb..bd23e68af8 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -13,6 +13,11 @@ import ( ) func buildSimpleTrie(t *testing.T) *trie.Trie { + // (250, 0, x1) + // | + // (0,0,x1) + // / \ + // (2) (3) // Build trie memdb := pebble.NewMemTest(t) txn, err := memdb.NewTransaction(true) @@ -162,7 +167,7 @@ func TestGetProofs(t *testing.T) { require.Equal(t, expectedProofNodes, proofNodes) }) - t.Run("Simple Trie - simple double binary 2", func(t *testing.T) { + t.Run("Simple Trie - simple double binary edge", func(t *testing.T) { tempTrie := buildSimpleDoubleBinaryTrie(t) zero := trie.NewKey(249, []byte{0}) From 97f24072b1ad3652c1f483583ff73dfd3136d4f4 Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 16:00:42 +0300 Subject: [PATCH 23/31] test left-right edge passes --- core/trie/proof.go | 20 ++++++++++++++++---- core/trie/proof_test.go | 17 ++++++----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 9f2e89b701..c94175b17a 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -103,6 +103,9 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary Path: sNode.key, Child: sNode.node.Value, } + if sNode.key.len == tri.height { + return edge, nil, nil + } } if sNode.key.len == tri.height { return edge, nil, nil @@ -165,8 +168,18 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { // -> Child should be leaf (len=251). Distance between child and parent should be > 1. var parentKey *Key i := 0 - childIsLeaf := false - // for i, sNode := range nodesExcludingLeaf { // Todo: wpould be easier to loop of transformed nodes + childIsLeaf := nodesToLeaf[0].key.len == tri.height + if nodesToLeaf[0].node.Left != nil { + if nodesToLeaf[0].node.Left.len == tri.height { + childIsLeaf = true + } + } + if nodesToLeaf[0].node.Right != nil { + if nodesToLeaf[0].node.Right.len == tri.height { + childIsLeaf = true + } + } + // for i, sNode := range nodesExcludingLeaf { // Todo: would be easier to loop of transformed nodes for i < len(nodesToLeaf) { sNode := nodesToLeaf[i] sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) @@ -183,7 +196,7 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { } else if sNodeEdge == nil && !childIsLeaf { // Internal Binary proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) } else if sNodeEdge != nil && childIsLeaf { // pre-leaf Edge - proofNodes = append(proofNodes, []ProofNode{{Edge: &Edge{Child: sNodeEdge.Child}}}...) + proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}}...) } else if sNodeEdge == nil && childIsLeaf { // pre-leaf binary proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) } @@ -192,7 +205,6 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { break } parentKey = nodesToLeaf[i-1].key - childIsLeaf = nodesToLeaf[i].key.len == 251 // lNode, err := tri.GetNodeFromKey(sNode.node.Left) // if err != nil { // return nil, err diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index bd23e68af8..a9723cfe3f 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -249,20 +249,14 @@ func TestGetProofs(t *testing.T) { require.NoError(t, tempTrie.Commit()) - path1 := trie.NewKeyFromBits([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}) - path2 := trie.NewKeyFromBits([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}) + key1Bytes := key1.Bytes() + path1 := trie.NewKey(251, key1Bytes[:]) + child := utils.HexToFelt(t, "0x00000000000000000000000000000000000000000000000000000000000000AA") - qwe := path2.Felt() - fmt.Println(qwe.String()) expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &path1, - }, - }, - { - Edge: &trie.Edge{ - Path: &path2, + Path: &path1, Child: child, }, }, @@ -272,7 +266,8 @@ func TestGetProofs(t *testing.T) { require.NoError(t, err) // Better inspection - for _, pNode := range proofNodes { + for i, pNode := range proofNodes { + fmt.Println(i) pNode.PrettyPrint() } require.Equal(t, expectedProofNodes, proofNodes) From c1b64193ebac140b2a3a62fcf7c467c25bef9f5c Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 16:24:26 +0300 Subject: [PATCH 24/31] edge-edge seems to work, only rightHash error --- core/trie/proof.go | 127 ++++------------------------------------ core/trie/proof_test.go | 13 ++-- 2 files changed, 19 insertions(+), 121 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index c94175b17a..ff2bae31b6 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -62,36 +62,9 @@ func isEdge(parentKey *Key, sNode storageNode) bool { if sNodeLen-parentKey.len > 1 { return true } - - // leftKey := sNode.node.Left.len - // rightKey := sNode.node.Right.len - // if (leftKey-sNodeLen > 1) || (rightKey-sNodeLen > 1) { - // return true - // } return false } -// The binary node uses the hash of children. If the child is an edge, we first need to represent it -// as an edge node, and then take its hash. -// func getChildHash(tri *Trie, parentKey *Key, childKey *Key) (*felt.Felt, error) { -// childNode, err := tri.GetNodeFromKey(childKey) -// if err != nil { -// return nil, err -// } - -// childIsEdgeBool := isEdge(parentKey, storageNode{node: childNode, key: childKey}) -// if childIsEdgeBool { -// fmt.Println("childKey", childKey) -// fmt.Println("childNode.Value", childNode.Value) -// edgeNode := ProofNode{Edge: &Edge{ // Todo: this is wrong for the key3,val 0x5 edge node in the double binary..hash is incorrect.. -// Path: childKey, -// Child: childNode.Value, -// }} -// return edgeNode.Hash(), nil -// } -// return childNode.Value, nil -// } - // transformNode takes a node and splits it into an edge+binary if it's an edge node func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary, error) { // Internal Edge @@ -125,7 +98,6 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary Path: sNode.node.Right, Child: rNode.Value, }} - fmt.Println("rightHash", rightHash) rightHash = edge.Hash() } leftHash := lNode.Value @@ -142,45 +114,25 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary } return edge, binary, nil - } // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L514 // Note: Juno nodes are Edge AND Binary, whereas pathfinders are Edge XOR Binary, so -// we need to perform a transformation as we progress along Junos Trie -// If a node is an edge transform it into edge + binary, AND do this for its children +// we need to perform a transformation as we progress along Junos Trie. func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { leafKey := tri.feltToKey(leaf) nodesToLeaf, err := tri.nodesFromRoot(&leafKey) if err != nil { return nil, err } - // nodesExcludingLeaf := nodesToLeaf[:len(nodesToLeaf)-1] proofNodes := []ProofNode{} - // 1. If it's an edge-node in pathfinders impl, we need to expand the node into an edge + binary - // -> Child should be internal node (len<251). Distance between child and parent should be > 1. - // 2. If it's a binary-node, we store binary - // -> Child should be internal node (len<251). Distance between child and parent should be 1. - // 3. If it's a binary leaf, we store binary leaf - // -> Child should be leaf (len=251). Distance between child and parent should be 1. - // 4. If it's an edge leaf, we store an edge leaf - // -> Child should be leaf (len=251). Distance between child and parent should be > 1. var parentKey *Key - i := 0 - childIsLeaf := nodesToLeaf[0].key.len == tri.height - if nodesToLeaf[0].node.Left != nil { - if nodesToLeaf[0].node.Left.len == tri.height { - childIsLeaf = true - } - } - if nodesToLeaf[0].node.Right != nil { - if nodesToLeaf[0].node.Right.len == tri.height { - childIsLeaf = true + + for i := 0; i < len(nodesToLeaf); i++ { + if i != 0 { + parentKey = nodesToLeaf[i-1].key } - } - // for i, sNode := range nodesExcludingLeaf { // Todo: would be easier to loop of transformed nodes - for i < len(nodesToLeaf) { sNode := nodesToLeaf[i] sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) if err != nil { @@ -190,75 +142,16 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { break } - if sNodeEdge != nil && !childIsLeaf { // Internal Edge - // Todo: child can be an edge + isLeaf := sNode.key.len == tri.height + if sNodeEdge != nil && !isLeaf { // Internal Edge proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}, {Binary: sNodeBinary}}...) - } else if sNodeEdge == nil && !childIsLeaf { // Internal Binary + } else if sNodeEdge == nil && !isLeaf { // Internal Binary proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) - } else if sNodeEdge != nil && childIsLeaf { // pre-leaf Edge + } else if sNodeEdge != nil && isLeaf { // pre-leaf Edge proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}}...) - } else if sNodeEdge == nil && childIsLeaf { // pre-leaf binary + } else if sNodeEdge == nil && isLeaf { // pre-leaf binary proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) } - i++ - if i == len(nodesToLeaf) { - break - } - parentKey = nodesToLeaf[i-1].key - // lNode, err := tri.GetNodeFromKey(sNode.node.Left) - // if err != nil { - // return nil, err - // } - // lNodeEdge, _, err := transformNode(tri, sNode.key, storageNode{key: sNode.node.Left, node: lNode}) - // if err != nil { - // return nil, err - // } - // rNode, err := tri.GetNodeFromKey(sNode.node.Right) - // if err != nil { - // return nil, err - // } - // rNodeEdge, _, err := transformNode(tri, sNode.key, storageNode{key: sNode.node.Right, node: rNode}) - // if err != nil { - // return nil, err - // } - - // var leftHash *felt.Felt - // if lNodeEdge != nil { - // tmp := ProofNode{Edge: lNodeEdge} - // leftHash = tmp.Hash() - // } else { - // leftHash = lNode.Value - // } - // var rightHash *felt.Felt - // if rNodeEdge != nil { - // rightHash = rNodeEdge.Value - // } else { - // tmp := ProofNode{Edge: lNodeEdge} - // rightHash = tmp.Hash() - // } - // sNodeBinary = &Binary{ - // LeftHash: leftHash, - // RightHash: rightHash, - // } - - // else if !childIsInternal && isEdgeBool { // Leaf Edge eg Binary -> edge -> leaf - // proofNodes = append(proofNodes, ProofNode{ - // Edge: &Edge{ - // Child: sNode.node.Value, - // // Value: value, // Todo: ?? - // }, - // }) - // } else if !childIsInternal && !isEdgeBool { // Leaf binary - // proofNodes = append(proofNodes, ProofNode{ - // Binary: &Binary{ - // LeftHash: leftHash, - // RightHash: rightHash, - // }, - // }) - // } else { - // return nil, errors.New("unexpected error in GetProof") - // } - } return proofNodes, nil } diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index a9723cfe3f..8d015d5cef 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -43,7 +43,6 @@ func buildSimpleTrie(t *testing.T) *trie.Trie { } func buildSimpleBinaryRootTrie(t *testing.T) *trie.Trie { - // (0, 0, x) // / \ // (250, 0, cc) (250, 11111.., dd) @@ -110,7 +109,7 @@ func TestGetProofs(t *testing.T) { expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &zero, // Todo: pathfinder returns 0? But shouldn't be zero?... + Path: &zero, Child: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), }, }, @@ -172,6 +171,8 @@ func TestGetProofs(t *testing.T) { zero := trie.NewKey(249, []byte{0}) value3 := new(felt.Felt).SetUint64(5) + key3Bytes := new(felt.Felt).SetUint64(3).Bytes() + path3 := trie.NewKey(251, key3Bytes[:]) expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ @@ -187,6 +188,7 @@ func TestGetProofs(t *testing.T) { }, { Edge: &trie.Edge{ + Path: &path3, Child: value3, }, }, @@ -204,7 +206,9 @@ func TestGetProofs(t *testing.T) { t.Run("Simple Trie - simple binary root", func(t *testing.T) { tempTrie := buildSimpleBinaryRootTrie(t) - value := utils.HexToFelt(t, "0xcc") + + key1Bytes := new(felt.Felt).SetUint64(0).Bytes() + path1 := trie.NewKey(250, key1Bytes[:]) expectedProofNodes := []trie.ProofNode{ { Binary: &trie.Binary{ @@ -214,7 +218,8 @@ func TestGetProofs(t *testing.T) { }, { Edge: &trie.Edge{ - Child: value, + Path: &path1, + Child: utils.HexToFelt(t, "0xcc"), }, }, } From 7959476c9b1e2f41eb73a884cda6e75df78d3d55 Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 16:45:01 +0300 Subject: [PATCH 25/31] progress! use path(node,parent) --- core/trie/proof.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index ff2bae31b6..6127b99e1b 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -72,8 +72,9 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary var edge *Edge if isEdgeBool { + edgePath := path(sNode.key, parentKey) edge = &Edge{ - Path: sNode.key, + Path: &edgePath, Child: sNode.node.Value, } if sNode.key.len == tri.height { @@ -94,19 +95,21 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary rightHash := rNode.Value if isEdge(sNode.key, storageNode{node: rNode, key: sNode.node.Right}) { - edge := ProofNode{Edge: &Edge{ - Path: sNode.node.Right, + edgePath := path(sNode.node.Right, sNode.key) + rEdge := ProofNode{Edge: &Edge{ + Path: &edgePath, Child: rNode.Value, }} - rightHash = edge.Hash() + rightHash = rEdge.Hash() } leftHash := lNode.Value if isEdge(sNode.key, storageNode{node: lNode, key: sNode.node.Left}) { - edge := ProofNode{Edge: &Edge{ - Path: sNode.node.Left, + edgePath := path(sNode.node.Left, sNode.key) + lEdge := ProofNode{Edge: &Edge{ + Path: &edgePath, Child: lNode.Value, }} - leftHash = edge.Hash() + leftHash = lEdge.Hash() } binary := &Binary{ LeftHash: leftHash, @@ -134,6 +137,7 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { parentKey = nodesToLeaf[i-1].key } sNode := nodesToLeaf[i] + // Todo: should return path from parent, not from root? sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) if err != nil { return nil, err From a02b13c7b73641499231d35c4a692acf87617a02 Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 16:50:11 +0300 Subject: [PATCH 26/31] tidy --- core/trie/proof.go | 7 ++----- core/trie/proof_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 6127b99e1b..6f47398b5f 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -65,9 +65,9 @@ func isEdge(parentKey *Key, sNode storageNode) bool { return false } -// transformNode takes a node and splits it into an edge+binary if it's an edge node +// Note: we need to account for the fact that Junos Trie has nodes that are Binary AND Edge, +// whereas the protocol requires nodes that are BINARY XOR Edge func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary, error) { - // Internal Edge isEdgeBool := isEdge(parentKey, sNode) var edge *Edge @@ -120,8 +120,6 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary } // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L514 -// Note: Juno nodes are Edge AND Binary, whereas pathfinders are Edge XOR Binary, so -// we need to perform a transformation as we progress along Junos Trie. func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { leafKey := tri.feltToKey(leaf) nodesToLeaf, err := tri.nodesFromRoot(&leafKey) @@ -137,7 +135,6 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { parentKey = nodesToLeaf[i-1].key } sNode := nodesToLeaf[i] - // Todo: should return path from parent, not from root? sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) if err != nil { return nil, err diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 8d015d5cef..f08494a16f 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -138,7 +138,7 @@ func TestGetProofs(t *testing.T) { expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &zero, // Todo: 0x7469c4000fe0 ??? + Path: &zero, Child: utils.HexToFelt(t, "0x055C81F6A791FD06FC2E2CCAD922397EC76C3E35F2E06C0C0D43D551005A8DEA"), }, }, @@ -171,12 +171,12 @@ func TestGetProofs(t *testing.T) { zero := trie.NewKey(249, []byte{0}) value3 := new(felt.Felt).SetUint64(5) - key3Bytes := new(felt.Felt).SetUint64(3).Bytes() - path3 := trie.NewKey(251, key3Bytes[:]) + key3Bytes := new(felt.Felt).SetUint64(1).Bytes() + path3 := trie.NewKey(1, key3Bytes[:]) expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &zero, // Todo: 0x7469c4000fe0 ??? + Path: &zero, Child: utils.HexToFelt(t, "0x055C81F6A791FD06FC2E2CCAD922397EC76C3E35F2E06C0C0D43D551005A8DEA"), }, }, From a862473f7143b1961c9dafe9868dcc1be29e4fe0 Mon Sep 17 00:00:00 2001 From: rian Date: Wed, 8 May 2024 16:55:42 +0300 Subject: [PATCH 27/31] remove uneded methods --- core/trie/key.go | 17 ----------------- core/trie/trie.go | 10 ---------- 2 files changed, 27 deletions(-) diff --git a/core/trie/key.go b/core/trie/key.go index d6c4d8c36c..dc946a5ef6 100644 --- a/core/trie/key.go +++ b/core/trie/key.go @@ -23,23 +23,6 @@ func NewKey(length uint8, keyBytes []byte) Key { return k } -func NewKeyFromBits(bits []int) Key { - if len(bits) > len(Key{}.bitset)*8 { - panic("bits does not fit in bitset") - } - - var keyBytes []byte - for i := 0; i < len(bits); i += 8 { - var byteVal byte - for j := 0; j < 8 && i+j < len(bits); j++ { - byteVal |= byte(bits[i+j]) << (7 - j) - } - keyBytes = append(keyBytes, byteVal) - } - - return NewKey(uint8(len(bits)), keyBytes) -} - func (k *Key) SubKey(n uint8) *Key { if n > k.len { panic("n is greater than the length of the key") diff --git a/core/trie/trie.go b/core/trie/trie.go index 80f51beede..2496b724d4 100644 --- a/core/trie/trie.go +++ b/core/trie/trie.go @@ -182,21 +182,11 @@ func (t *Trie) Get(key *felt.Felt) (*felt.Felt, error) { return &leafValue, nil } -func (t *Trie) GetRootNode() (*Node, error) { - return t.storage.Get(t.rootKey) -} - // GetNodeFromKey returns the node for a given key. func (t *Trie) GetNodeFromKey(key *Key) (*Node, error) { return t.storage.Get(key) } -// GetNode returns the node for a given key. Note: it doesn't work for root nodes. -func (t *Trie) GetNode(key *felt.Felt) (*Node, error) { - storageKey := t.feltToKey(key) - return t.storage.Get(&storageKey) -} - // check if we are updating an existing leaf, if yes avoid traversing the trie func (t *Trie) updateLeaf(nodeKey Key, node *Node, value *felt.Felt) (*felt.Felt, error) { // Check if we are updating an existing leaf From 338df4cc10e23e95e09866e499fbc99f7157297f Mon Sep 17 00:00:00 2001 From: rian Date: Thu, 9 May 2024 10:16:47 +0300 Subject: [PATCH 28/31] tidy few things --- core/trie/proof.go | 27 ++++++++------------------- core/trie/proof_test.go | 4 ++-- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 6f47398b5f..ba05d65f0c 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -59,14 +59,11 @@ func isEdge(parentKey *Key, sNode storageNode) bool { if parentKey == nil { // Root return sNodeLen != 0 } - if sNodeLen-parentKey.len > 1 { - return true - } - return false + return sNodeLen-parentKey.len > 1 } // Note: we need to account for the fact that Junos Trie has nodes that are Binary AND Edge, -// whereas the protocol requires nodes that are BINARY XOR Edge +// whereas the protocol requires nodes that are Binary XOR Edge func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary, error) { isEdgeBool := isEdge(parentKey, sNode) @@ -77,11 +74,8 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary Path: &edgePath, Child: sNode.node.Value, } - if sNode.key.len == tri.height { - return edge, nil, nil - } } - if sNode.key.len == tri.height { + if sNode.key.len == tri.height { // Leaf return edge, nil, nil } lNode, err := tri.GetNodeFromKey(sNode.node.Left) @@ -131,27 +125,24 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { var parentKey *Key for i := 0; i < len(nodesToLeaf); i++ { + sNode := nodesToLeaf[i] + isLeaf := sNode.key.len == tri.height if i != 0 { parentKey = nodesToLeaf[i-1].key } - sNode := nodesToLeaf[i] sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) if err != nil { return nil, err } - if sNodeEdge == nil && sNodeBinary == nil { - break - } - isLeaf := sNode.key.len == tri.height if sNodeEdge != nil && !isLeaf { // Internal Edge proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}, {Binary: sNodeBinary}}...) } else if sNodeEdge == nil && !isLeaf { // Internal Binary proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) - } else if sNodeEdge != nil && isLeaf { // pre-leaf Edge + } else if sNodeEdge != nil && isLeaf { // Leaf Edge proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}}...) - } else if sNodeEdge == nil && isLeaf { // pre-leaf binary - proofNodes = append(proofNodes, []ProofNode{{Binary: sNodeBinary}}...) + } else if sNodeEdge == nil && sNodeBinary == nil { // sNode is a binary leaf + break } } return proofNodes, nil @@ -180,8 +171,6 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode } remainingPath.RemoveLastBit() case proofNode.Edge != nil: - // The next "proofNode.Edge.len" bits must match - // Todo: Isn't edge.path from root? and remaining from edge to leaf?? if !proofNode.Edge.Path.Equal(remainingPath.SubKey(proofNode.Edge.Path.Len())) { return false } diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index f08494a16f..3353154208 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -287,7 +287,7 @@ func TestVerifyProofs(t *testing.T) { expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &zero, // Todo: pathfinder returns 0? But shouldn't be zero?... + Path: &zero, Child: utils.HexToFelt(t, "0x05774FA77B3D843AE9167ABD61CF80365A9B2B02218FC2F628494B5BDC9B33B8"), }, }, @@ -314,7 +314,7 @@ func TestVerifyProofs(t *testing.T) { expectedProofNodes := []trie.ProofNode{ { Edge: &trie.Edge{ - Path: &zero, // Todo: 0x7469c4000fe0 ??? + Path: &zero, Child: utils.HexToFelt(t, "0x055C81F6A791FD06FC2E2CCAD922397EC76C3E35F2E06C0C0D43D551005A8DEA"), }, }, From f405556968bda75b1ac2676e382c909434092b0b Mon Sep 17 00:00:00 2001 From: rian Date: Thu, 9 May 2024 10:19:26 +0300 Subject: [PATCH 29/31] small tidy --- core/trie/proof.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index ba05d65f0c..51b9b5c78e 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -126,14 +126,11 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { for i := 0; i < len(nodesToLeaf); i++ { sNode := nodesToLeaf[i] - isLeaf := sNode.key.len == tri.height - if i != 0 { - parentKey = nodesToLeaf[i-1].key - } sNodeEdge, sNodeBinary, err := transformNode(tri, parentKey, sNode) if err != nil { return nil, err } + isLeaf := sNode.key.len == tri.height if sNodeEdge != nil && !isLeaf { // Internal Edge proofNodes = append(proofNodes, []ProofNode{{Edge: sNodeEdge}, {Binary: sNodeBinary}}...) @@ -144,6 +141,7 @@ func GetProof(leaf *felt.Felt, tri *Trie) ([]ProofNode, error) { } else if sNodeEdge == nil && sNodeBinary == nil { // sNode is a binary leaf break } + parentKey = nodesToLeaf[i].key } return proofNodes, nil } From 2e3d34b73b29c1c2b991d264b6fd86db534e142e Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 13 May 2024 11:24:45 +0300 Subject: [PATCH 30/31] VerifyProof expects keys.len=251, and takes hashFun as parameter --- core/trie/proof.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 51b9b5c78e..542ff68368 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -14,16 +14,16 @@ type ProofNode struct { } // Note: does not work for leaves -func (pn *ProofNode) Hash() *felt.Felt { +func (pn *ProofNode) Hash(hash hashFunc) *felt.Felt { switch { case pn.Binary != nil: - return crypto.Pedersen(pn.Binary.LeftHash, pn.Binary.RightHash) + return hash(pn.Binary.LeftHash, pn.Binary.RightHash) case pn.Edge != nil: length := make([]byte, len(pn.Edge.Path.bitset)) length[len(pn.Edge.Path.bitset)-1] = pn.Edge.Path.len pathFelt := pn.Edge.Path.Felt() lengthFelt := new(felt.Felt).SetBytes(length) - return new(felt.Felt).Add(crypto.Pedersen(pn.Edge.Child, &pathFelt), lengthFelt) + return new(felt.Felt).Add(hash(pn.Edge.Child, &pathFelt), lengthFelt) default: return nil } @@ -94,7 +94,7 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary Path: &edgePath, Child: rNode.Value, }} - rightHash = rEdge.Hash() + rightHash = rEdge.Hash(tri.hash) } leftHash := lNode.Value if isEdge(sNode.key, storageNode{node: lNode, key: sNode.node.Left}) { @@ -103,7 +103,7 @@ func transformNode(tri *Trie, parentKey *Key, sNode storageNode) (*Edge, *Binary Path: &edgePath, Child: lNode.Value, }} - leftHash = lEdge.Hash() + leftHash = lEdge.Hash(tri.hash) } binary := &Binary{ LeftHash: leftHash, @@ -149,7 +149,7 @@ func GetProof(leaf *felt.Felt, 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) bool { - if key.Len() != key.len { + if key.Len() != 251 { return false } @@ -157,7 +157,7 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode remainingPath := key for _, proofNode := range proofs { - if !proofNode.Hash().Equal(expectedHash) { + if !proofNode.Hash(crypto.Pedersen).Equal(expectedHash) { return false } switch { From a07d3d501539bc2dc1410bdf10f0604ba3ff807e Mon Sep 17 00:00:00 2001 From: rian Date: Mon, 13 May 2024 11:26:20 +0300 Subject: [PATCH 31/31] turn off golint flag --- core/trie/proof.go | 7 +++---- core/trie/proof_test.go | 5 +++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/trie/proof.go b/core/trie/proof.go index 542ff68368..57e6bdeac0 100644 --- a/core/trie/proof.go +++ b/core/trie/proof.go @@ -3,7 +3,6 @@ package trie import ( "fmt" - "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" ) @@ -148,8 +147,8 @@ func GetProof(leaf *felt.Felt, 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) bool { - if key.Len() != 251 { +func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode, hash hashFunc) bool { + if key.Len() != 251 { //nolint:gomnd return false } @@ -157,7 +156,7 @@ func VerifyProof(root *felt.Felt, key *Key, value *felt.Felt, proofs []ProofNode remainingPath := key for _, proofNode := range proofs { - if !proofNode.Hash(crypto.Pedersen).Equal(expectedHash) { + if !proofNode.Hash(hash).Equal(expectedHash) { return false } switch { diff --git a/core/trie/proof_test.go b/core/trie/proof_test.go index 3353154208..0ae9c78513 100644 --- a/core/trie/proof_test.go +++ b/core/trie/proof_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/core/trie" "github.com/NethermindEth/juno/db/pebble" @@ -304,7 +305,7 @@ func TestVerifyProofs(t *testing.T) { key1Bytes := new(felt.Felt).SetUint64(0).Bytes() key1 := trie.NewKey(251, key1Bytes[:]) val1 := new(felt.Felt).SetUint64(2) - assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes)) + assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes, crypto.Pedersen)) }) // https://github.com/eqlabs/pathfinder/blob/main/crates/merkle-tree/src/tree.rs#L2167 @@ -337,6 +338,6 @@ func TestVerifyProofs(t *testing.T) { key1Bytes := new(felt.Felt).SetUint64(0).Bytes() key1 := trie.NewKey(251, key1Bytes[:]) val1 := new(felt.Felt).SetUint64(2) - assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes)) + assert.True(t, trie.VerifyProof(root, &key1, val1, expectedProofNodes, crypto.Pedersen)) }) }