github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/common/proof/proof.go (about)

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