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 }