-
Notifications
You must be signed in to change notification settings - Fork 175
/
proof.go
61 lines (54 loc) · 2.65 KB
/
proof.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package proof
import (
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/ledger/common/bitutils"
"github.com/onflow/flow-go/ledger/common/hash"
)
// TODO move this to proof itself
// VerifyTrieProof verifies the proof, by constructing all the
// hash from the leaf to the root and comparing the rootHash
func VerifyTrieProof(p *ledger.TrieProof, expectedState ledger.State) bool {
treeHeight := ledger.NodeMaxHeight
leafHeight := treeHeight - int(p.Steps) // p.Steps is the number of edges we are traversing until we hit the compactified leaf.
if !(0 <= leafHeight && leafHeight <= treeHeight) { // sanity check
return false
}
// We start with the leaf and hash our way upwards towards the root
proofIndex := len(p.Interims) - 1 // the index of the last non-default value furthest down the tree (-1 if there is none)
computed := ledger.ComputeCompactValue(hash.Hash(p.Path), p.Payload.Value(), leafHeight) // we first compute the hash of the compact leaf (at height leafHeight)
for h := leafHeight + 1; h <= treeHeight; h++ { // then, we hash our way upwards until we hit the root (at height `treeHeight`)
// we are currently at a node n (initially the leaf). In this iteration, we want to compute the
// parent's hash. Here, h is the height of the parent, whose hash want to compute.
// The parent has two children: child n, whose hash we have already computed (aka `computed`);
// and the sibling to node n, whose hash (aka `siblingHash`) must be defined by the Proof.
var siblingHash hash.Hash
flag := bitutils.ReadBit(p.Flags, treeHeight-h)
if flag == 1 { // if flag is set, siblingHash is stored in the proof
if proofIndex < 0 { // proof invalid: too few values
return false
}
siblingHash = p.Interims[proofIndex]
proofIndex--
} else { // otherwise, siblingHash is a default hash
siblingHash = ledger.GetDefaultHashForHeight(h - 1)
}
bit := bitutils.ReadBit(p.Path[:], treeHeight-h)
// hashing is order dependent
if bit == 1 { // we hash our way up to the parent along the parent's right branch
computed = hash.HashInterNode(siblingHash, computed)
} else { // we hash our way up to the parent along the parent's left branch
computed = hash.HashInterNode(computed, siblingHash)
}
}
return computed == hash.Hash(expectedState)
}
// VerifyTrieBatchProof verifies all the proof inside the batchproof
func VerifyTrieBatchProof(bp *ledger.TrieBatchProof, expectedState ledger.State) bool {
for _, p := range bp.Proofs {
// any invalid proof
if !VerifyTrieProof(p, expectedState) {
return false
}
}
return true
}