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  }