github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/merkler/path.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package merkler 7 8 import ( 9 "math" 10 "math/bits" 11 12 "github.com/insolar/vanilla/args" 13 ) 14 15 // PathBuilder allows to build a merkle-proof by a linear hashing log produced by StackedCalculator. 16 // PathBuilder should be initialized by NewPathBuilder, then use WalkFor to get positions of the hashing log entries 17 // required to be included into a merkle-proof. 18 // 19 // Complexity (by a number of hashes/leafs): 20 // - NewPathBuilder(leafCount, stubbed) is O(k * log(leafCount)), k ~ 1 when !stubbed and k ~ 2 otherwise 21 // - WalkFor(index) is O(log(leafCount)) 22 // - Memory is O(log(leafCount)) 23 24 type PathBuilder struct { 25 count uint 26 levels []pathLevel 27 stubbed bool 28 } 29 30 type pathLevel struct { 31 bitMask uint 32 nodeValueL uint 33 nodeValueR uint 34 } 35 36 func NewPathBuilder(leafCount uint, stubbed bool) PathBuilder { 37 switch { 38 case leafCount > 2: 39 // jump to the below 40 case leafCount == 0: 41 panic("illegal value") 42 default: 43 return PathBuilder{leafCount, nil, stubbed} 44 } 45 46 depth := uint8(bits.Len(leafCount - 1)) 47 levels := make([]pathLevel, depth-1) 48 49 for i := range levels { 50 bitMask := uint(2) << uint8(i) 51 levels[i].bitMask = bitMask 52 } 53 54 switch { 55 case args.IsPowerOfTwo(leafCount): 56 perfectPath(leafCount, levels) 57 case stubbed: 58 stubbedPath(leafCount, levels) 59 default: 60 unstubbedPath(leafCount, levels) 61 } 62 63 return PathBuilder{leafCount, levels, stubbed} 64 } 65 66 func perfectPath(leafCount uint, levels []pathLevel) { 67 nodeCount := leafCount 68 for i := 1; i < len(levels); i++ { 69 levels[i].nodeValueR = nodeCount 70 nodeCount++ 71 } 72 } 73 74 func unstubbedPath(leafCount uint, levels []pathLevel) { 75 lastLeafIndex := leafCount - 1 76 77 invertMask := ^(uint(math.MaxUint64&^1) << UnbalancedBitCount(lastLeafIndex, uint8(len(levels)+1))) 78 invertedLastBalanced := lastLeafIndex ^ invertMask // | invertMask>>1 79 //if invertedLastBalanced >= leafCount { 80 // panic("illegal state") 81 //} 82 83 lastRightmost := lastLeafIndex 84 nodeIndex := leafCount 85 86 for i, level := range levels { 87 if lastLeafIndex&level.bitMask == 0 { 88 levels[i].nodeValueL = lastRightmost 89 90 leftGapSibling := invertedLastBalanced ^ level.bitMask 91 //if leftGapSibling >= leafCount { 92 // panic("illegal state") 93 //} 94 if indexR := siblingIndex(leftGapSibling, level.bitMask); indexR >= leafCount { 95 levels[i].nodeValueR = nodeIndex 96 nodeIndex++ 97 } 98 continue 99 } 100 101 indexL := siblingIndex(lastLeafIndex, level.bitMask) 102 if indexL >= leafCount { 103 levels[i].nodeValueL = nodeIndex 104 nodeIndex++ 105 } 106 107 levels[i].nodeValueR = lastRightmost 108 lastRightmost = nodeIndex 109 nodeIndex++ 110 } 111 112 if nodeCount := leafCount + uint(StackSequenceUnused(leafCount)) - 1; nodeCount != nodeIndex { 113 panic("illegal state") 114 } 115 } 116 117 func stubbedPath(leafCount uint, levels []pathLevel) { 118 lastLeafIndex := leafCount - 1 119 120 invertMask := ^(uint(math.MaxUint64&^1) << UnbalancedBitCount(lastLeafIndex, uint8(len(levels)+1))) 121 invertedLastBalanced := lastLeafIndex ^ invertMask // | invertMask>>1 122 //if invertedLastBalanced >= leafCount { 123 // panic("illegal state") 124 //} 125 126 nodeIndex := leafCount 127 128 for i, level := range levels { 129 if i == 0 && leafCount&1 == 0 { 130 continue 131 } 132 133 if lastLeafIndex&level.bitMask == 0 { 134 leftGapSibling := invertedLastBalanced ^ level.bitMask 135 //if leftGapSibling >= leafCount { 136 // panic("illegal state") 137 //} 138 if indexR := siblingIndex(leftGapSibling, level.bitMask); indexR >= leafCount { 139 levels[i].nodeValueR = nodeIndex 140 nodeIndex++ 141 } 142 143 levels[i].nodeValueL = nodeIndex 144 nodeIndex++ 145 continue 146 } 147 148 indexL := siblingIndex(lastLeafIndex, level.bitMask) 149 if indexL >= leafCount { 150 levels[i].nodeValueL = nodeIndex 151 nodeIndex++ 152 } 153 154 levels[i].nodeValueR = nodeIndex 155 nodeIndex++ 156 } 157 158 // TODO check count 159 } 160 161 const StubNodeIndex = 0 162 163 // Is called in a sequence of merkle-proof items, starting from leafs. 164 // (index) is an index of a relevant entry of StackCalculator hashing log. 165 // (isLeaf) indicates is this should be a leaf value or a node value. 166 // (isRight) indicates position of this entry for a hashing operation. Either left or right. 167 // 168 // NB! Leaf index is [0, leafCount-1], node index is [1, leafCount+N], node index = 0 means stub value. 169 // 170 type PathEntryFunc func(index uint, isLeaf, isRight bool) 171 172 // For the given (index) WalkFor will call (nodeFn) for each level of tree that has to be included into a merkle-proof, starting from leafs. 173 // The (nodeFn) is called with a relevant index of a hash value in the merkler log, and with flags about the value - leaf-or-node and right-or-left. 174 // 175 // NB! When stub is used, then (nodeFn) is called as (StubNodeIndex, false, _) as StackCalculator can't produce a node value at index StubNodeIndex. 176 // NB! The (nodeFn) is not called for the requested leaf itself. 177 func (v PathBuilder) WalkFor(index uint, nodeFn PathEntryFunc) { 178 if v.count <= index { 179 panic("illegal value") 180 } 181 182 // sibling leaf 183 switch sibling := index ^ 1; { 184 case sibling < v.count: 185 nodeFn(sibling, true, index&1 == 0) 186 case v.stubbed: 187 nodeFn(StubNodeIndex, false, true) 188 } 189 190 for _, level := range v.levels { 191 tIndex := siblingIndex(index, level.bitMask) 192 193 isRightSibling := index&level.bitMask == 0 194 195 switch { 196 case tIndex < v.count: 197 // the tree node was calculated as a part of the normal flow 198 nodeFn(tIndex, false, isRightSibling) 199 continue 200 case isRightSibling: 201 lastOfLeftBranch := index | (level.bitMask - 1) 202 if lastOfLeftBranch >= v.count-1 { 203 // right branch doesn't exist 204 if v.stubbed { 205 nodeFn(StubNodeIndex, false, true) 206 } 207 continue 208 } 209 // right branch is delayed 210 tIndex = level.nodeValueR 211 default: 212 // left branch is delayed 213 tIndex = level.nodeValueL 214 } 215 216 if tIndex == 0 { 217 panic("illegal state") 218 } 219 nodeFn(tIndex, tIndex == v.count-1 && v.count&1 != 0, isRightSibling) 220 } 221 } 222 223 func siblingIndex(index uint, bitMask uint) uint { 224 tIndex := index ^ bitMask 225 bitMask-- 226 tIndex |= bitMask 227 tIndex += bitMask >> 1 228 return tIndex 229 }