// the game is: walk the tree from the root to the target leaf func (t *Tree) membershipProof(target, index, layer, version int, proof *MembershipProof) (err error) { if layer == 0 { return } // the number of events to the left of the node n := index + util.Pow(2, layer-1) if target < n { // go left, but should we save right first? We need to save right if there are any leaf nodes // fixed by the right node (otherwise we know it's hash is nil), dictated by the version of the // tree we are generating if version >= n { p := new(ProofPosition) p.Index = n p.Layer = layer - 1 p.Hash, err = t.getHashedNode(p.Index, p.Layer, version, false) if err != nil { return } proof.Nodes = append(proof.Nodes, *p) } return t.membershipProof(target, index, layer-1, version, proof) } // go right, once we have saved the left node p := new(ProofPosition) p.Index = index p.Layer = layer - 1 p.Hash, err = t.getHashedNode(p.Index, p.Layer, version, false) if err != nil { return } proof.Nodes = append(proof.Nodes, *p) return t.membershipProof(target, n, layer-1, version, proof) }
// layeR, Index, Version // See Figure 5 in "Efficient Data Structures for Tamper-Evident Logging" func (t *Tree) getHashedNode(index, layer, version int, proofMode bool) (value []byte, err error) { // always prefer frozen hashes, if we have calculated them if proofMode || version >= index+util.Pow(2, layer)-1 { if t.isFrozenHash(index, layer) { return t.getFrozenHash(index, layer) } } // special case for child nodes if layer == 0 && version >= index { event, err := t.getEvent(index) if err != nil { return nil, errors.New("no event with the provided index") } value = util.Hash(prefixZero, event) // have version determine if the right node is there or not } else if version >= index+util.Pow(2, layer-1) { a1, err := t.getHashedNode(index, layer-1, version, proofMode) if err != nil { return nil, errors.New("failed to get internal node with index " + strconv.Itoa(index)) } a2, err := t.getHashedNode(index+util.Pow(2, layer-1), layer-1, version, proofMode) if err != nil { return nil, errors.New("failed to get internal node with index " + strconv.Itoa(index)) } value = util.Hash(prefixOne, a1, a2) } else { a, err := t.getHashedNode(index, layer-1, version, proofMode) if err != nil { return nil, errors.New("failed to get internal node with index " + strconv.Itoa(index)) } value = util.Hash(prefixOne, a) } // should we add this to the frozen hash cache? if version >= index+util.Pow(2, layer)-1 { t.setFrozenHash(index, layer, value) } return }