Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(trie): simplify Verify proof function #2661

Merged
merged 1 commit into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 39 additions & 46 deletions lib/trie/proof/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,60 +57,50 @@ func buildTrie(encodedProofNodes [][]byte, rootHash []byte) (t *trie.Trie, err e
ErrEmptyProof, rootHash)
}

merkleValueToEncoding := make(map[string][]byte, len(encodedProofNodes))
digestToEncoding := make(map[string][]byte, len(encodedProofNodes))

// This loop finds the root node and decodes it.
// The other nodes have their Merkle value (blake2b digest or the encoding itself)
// inserted into a map from merkle value to encoding. They are only decoded
// later if the root or one of its descendant node reference their Merkle value.
// This loop does two things:
// 1. It finds the root node by comparing it with the root hash and decodes it.
// 2. It stores other encoded nodes in a mapping from their encoding digest to
// their encoding. They are only decoded later if the root or one of its
// descendant nodes reference their hash digest.
var root *node.Node
for _, encodedProofNode := range encodedProofNodes {
var digest []byte
if root == nil {
// root node not found yet
digestHash, err := common.Blake2bHash(encodedProofNode)
if err != nil {
return nil, fmt.Errorf("blake2b hash: %w", err)
}
digest = digestHash[:]

if bytes.Equal(digest, rootHash) {
root, err = node.Decode(bytes.NewReader(encodedProofNode))
if err != nil {
return nil, fmt.Errorf("decoding root node: %w", err)
}
continue // no need to add root to map of hash to encoding
}
// Note all encoded proof nodes are one of the following:
// - trie root node
// - child trie root node
// - child node with an encoding larger than 32 bytes
// In all cases, their Merkle value is the encoding hash digest.
digestHash, err := common.Blake2bHash(encodedProofNode)
if err != nil {
return nil, fmt.Errorf("blake2b hash: %w", err)
}
digest := digestHash[:]

var merkleValue []byte
if len(encodedProofNode) <= 32 {
merkleValue = encodedProofNode
} else {
if digest == nil {
digestHash, err := common.Blake2bHash(encodedProofNode)
if err != nil {
return nil, fmt.Errorf("blake2b hash: %w", err)
}
digest = digestHash[:]
}
merkleValue = digest
if root != nil || !bytes.Equal(digest, rootHash) {
// root node already found or the hash doesn't match the root hash.
digestToEncoding[string(digest)] = encodedProofNode
continue
// Note: no need to add the root node to the map of hash to encoding
}

merkleValueToEncoding[string(merkleValue)] = encodedProofNode
root, err = node.Decode(bytes.NewReader(encodedProofNode))
if err != nil {
return nil, fmt.Errorf("decoding root node: %w", err)
}
}

if root == nil {
proofMerkleValues := make([]string, 0, len(merkleValueToEncoding))
for merkleValueString := range merkleValueToEncoding {
merkleValueHex := common.BytesToHex([]byte(merkleValueString))
proofMerkleValues = append(proofMerkleValues, merkleValueHex)
proofHashDigests := make([]string, 0, len(digestToEncoding))
for hashDigestString := range digestToEncoding {
hashDigestHex := common.BytesToHex([]byte(hashDigestString))
proofHashDigests = append(proofHashDigests, hashDigestHex)
}
return nil, fmt.Errorf("%w: for Merkle root hash 0x%x in proof Merkle value(s) %s",
ErrRootNodeNotFound, rootHash, strings.Join(proofMerkleValues, ", "))
return nil, fmt.Errorf("%w: for root hash 0x%x in proof hash digests %s",
ErrRootNodeNotFound, rootHash, strings.Join(proofHashDigests, ", "))
}

err = loadProof(merkleValueToEncoding, root)
err = loadProof(digestToEncoding, root)
if err != nil {
return nil, fmt.Errorf("loading proof: %w", err)
}
Expand All @@ -119,8 +109,8 @@ func buildTrie(encodedProofNodes [][]byte, rootHash []byte) (t *trie.Trie, err e
}

// loadProof is a recursive function that will create all the trie paths based
// on the map from node hash to node starting at the root.
func loadProof(merkleValueToEncoding map[string][]byte, n *node.Node) (err error) {
// on the map from node hash digest to node encoding, starting from the node `n`.
func loadProof(digestToEncoding map[string][]byte, n *node.Node) (err error) {
if n.Type() != node.Branch {
return nil
}
Expand All @@ -131,8 +121,11 @@ func loadProof(merkleValueToEncoding map[string][]byte, n *node.Node) (err error
continue
}

// for inlined child nodes, the hash digest field is the
// encoding itself instead of the encoding hash digest, so we
// use the `merkleValue` variable name below to avoid confusion.
merkleValue := child.HashDigest
encoding, ok := merkleValueToEncoding[string(merkleValue)]
encoding, ok := digestToEncoding[string(merkleValue)]
if !ok {
inlinedChild := len(child.Value) > 0 || child.HasChild()
if !inlinedChild {
Expand All @@ -150,13 +143,13 @@ func loadProof(merkleValueToEncoding map[string][]byte, n *node.Node) (err error

child, err := node.Decode(bytes.NewReader(encoding))
if err != nil {
return fmt.Errorf("decoding child node for Merkle value 0x%x: %w",
return fmt.Errorf("decoding child node for hash digest 0x%x: %w",
merkleValue, err)
}

branch.Children[i] = child
branch.Descendants += child.Descendants
err = loadProof(merkleValueToEncoding, child)
err = loadProof(digestToEncoding, child)
if err != nil {
return err // do not wrap error since this is recursive
}
Expand Down
9 changes: 5 additions & 4 deletions lib/trie/proof/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func Test_buildTrie(t *testing.T) {
scaleEncode(t, blake2b(t, getBadNodeEncoding())), // child hash
})),
errWrapped: node.ErrVariantUnknown,
errMessage: "loading proof: decoding child node for Merkle value " +
errMessage: "loading proof: decoding child node for hash digest " +
"0xcfa21f0ec11a3658d77701b7b1f52fbcb783fe3df662977b6e860252b6c37e1e: " +
"decoding header: decoding header byte: " +
"node variant is unknown: for header byte 00000001",
Expand All @@ -276,7 +276,8 @@ func Test_buildTrie(t *testing.T) {
rootHash: []byte{3},
errWrapped: ErrRootNodeNotFound,
errMessage: "root node not found in proof: " +
"for Merkle root hash 0x03 in proof Merkle value(s) 0x41010402",
"for root hash 0x03 in proof hash digests " +
"0x60516d0bb6e1bbfb1293f1b276ea9505e9f4a4e7d98f620d05115e0b85274ae1",
},
}

Expand Down Expand Up @@ -471,7 +472,7 @@ func Test_loadProof(t *testing.T) {
}),
},
errWrapped: node.ErrVariantUnknown,
errMessage: "decoding child node for Merkle value 0x02: " +
errMessage: "decoding child node for hash digest 0x02: " +
"decoding header: decoding header byte: node variant is unknown: " +
"for header byte 00000001",
},
Expand Down Expand Up @@ -563,7 +564,7 @@ func Test_loadProof(t *testing.T) {
}),
},
errWrapped: node.ErrVariantUnknown,
errMessage: "decoding child node for Merkle value " +
errMessage: "decoding child node for hash digest " +
"0x6888b9403129c11350c6054b46875292c0ffedcfd581e66b79bdf350b775ebf2: " +
"decoding header: decoding header byte: node variant is unknown: " +
"for header byte 00000001",
Expand Down