github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/merkletree2/skippointers.go (about)

     1  package merkletree2
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  )
     7  
     8  // The SkipPointers struct is constructed for a specific Seqno s (version) of
     9  // the tree and contains the hashes of RootMetadata structs at specific previous
    10  // Seqnos (versions) of the tree. Such versions are fixed given s according to
    11  // the algorithm in GenerateSkipPointersSeqnos so that a "chain" of SkipPointers
    12  // can "connect" any two roots in a logarithmic number of steps.
    13  type SkipPointers []Hash
    14  
    15  func SkipPointersForSeqno(s Seqno) (pointers []Seqno) {
    16  	if s == 1 {
    17  		return []Seqno{}
    18  	}
    19  	n := int(math.Log2(float64(s - 1)))
    20  	x := Seqno(0)
    21  	for i := n; i >= 0; i-- {
    22  		if x+(1<<uint(i)) < s {
    23  			x += 1 << uint(i)
    24  			pointers = append(pointers, x)
    25  		}
    26  	}
    27  	return pointers
    28  }
    29  
    30  // SkipPointersPath takes two seqno 0 < start <= end. It returns a slice of
    31  // Seqno `pointers` such that:
    32  //   - start \in SkipPointersForSeqno(pointers[0]),
    33  //   - pointers[len(pointers)] == end,
    34  //   - pointers[i-1] \in SkipPointersForSeqno(pointers[i])
    35  //     for i = 1...len(pointers)-1.
    36  //
    37  // If start == end, returns [end]. The sequence has length
    38  // at most logarithmic in end - start.
    39  func SkipPointersPath(start, end Seqno) (pointers []Seqno, err error) {
    40  	if start > end {
    41  		return nil, fmt.Errorf("GenerateSkipPointersSequence: start > end: %v > %v", start, end)
    42  	}
    43  
    44  	current := end
    45  	pointers = append(pointers, current)
    46  
    47  	for current > start {
    48  		for _, i := range SkipPointersForSeqno(current) {
    49  			if start <= i {
    50  				current = i
    51  				if start != i {
    52  					pointers = append([]Seqno{i}, pointers...)
    53  				}
    54  				break
    55  			}
    56  		}
    57  	}
    58  	return pointers, nil
    59  }
    60  
    61  func ComputeRootMetadataSeqnosNeededInExtensionProof(start, end Seqno, isPartOfIncExtProof bool) ([]Seqno, error) {
    62  	seqnos, err := SkipPointersPath(start, end)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	// small optimization since in an InclusionExtension proof the root of the
    68  	// end seqno is already part of the Inclusion proof.
    69  	if isPartOfIncExtProof {
    70  		return seqnos[:len(seqnos)-1], nil
    71  	}
    72  
    73  	return seqnos, nil
    74  }
    75  
    76  func ComputeRootHashSeqnosNeededInExtensionProof(start, end Seqno) (ret []Seqno, err error) {
    77  	// this map prevents duplicates, as well as inserting the hashes of
    78  	// SkipPointersPath elements (as those can be recomputed from the rest).
    79  	unnecessarySeqnoMap := make(map[Seqno]bool)
    80  	unnecessarySeqnoMap[start] = true
    81  
    82  	ret = []Seqno{}
    83  
    84  	path, err := SkipPointersPath(start, end)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	for _, s := range path {
    89  		unnecessarySeqnoMap[s] = true
    90  		for _, s2 := range SkipPointersForSeqno(s) {
    91  			if unnecessarySeqnoMap[s2] {
    92  				continue
    93  			}
    94  			ret = append(ret, s2)
    95  			unnecessarySeqnoMap[s2] = true
    96  		}
    97  	}
    98  
    99  	return ret, nil
   100  }