github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/merkler/path_test.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  	"fmt"
    10  	"math"
    11  	"math/bits"
    12  	"testing"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/insolar/vanilla/cryptkit"
    19  	"github.com/insolar/vanilla/longbits"
    20  )
    21  
    22  type item = uint64
    23  
    24  func buildTrace(t *testing.T, count uint, unbalancedStub cryptkit.Digest, skipEmpty bool) (result []item, sum uint64) {
    25  	result = make([]item, 0, int(count)<<1+bits.Len(count))
    26  
    27  	md := NewStackedCalculator(xorPairDigester{}, unbalancedStub, func(item longbits.FoldableReader, _ bool) {
    28  		switch {
    29  		case item != nil:
    30  			u := item.FoldToUint64()
    31  			if count <= 64 {
    32  				require.NotEqual(t, uint64(0), u)
    33  			}
    34  			result = append(result, u)
    35  		case skipEmpty:
    36  			return
    37  		default:
    38  			result = append(result, 0)
    39  		}
    40  	})
    41  
    42  	for i := uint(0); i < count; i++ {
    43  		bit := bits.RotateLeft64(1, int(i))
    44  		md.AddNext(newBits64(bit))
    45  	}
    46  
    47  	sum = md.FinishSequence().FoldToUint64()
    48  
    49  	var mask uint64
    50  
    51  	require.Equal(t, int(count), md.Count())
    52  	count &= 0x7F
    53  	switch {
    54  	case count < 64:
    55  		mask = ^uint64(math.MaxUint64 << count)
    56  	case count > 64:
    57  		mask = math.MaxUint64 << (count - 64)
    58  	default:
    59  		mask = ^uint64(0)
    60  	}
    61  	if unbalancedStub.IsEmpty() {
    62  		require.Equal(t, mask, sum)
    63  	}
    64  
    65  	return
    66  }
    67  
    68  //nolint
    69  func printTrace(trace []item, count uint) {
    70  	for i, item := range trace {
    71  		switch index := uint(i >> 1); {
    72  		case index >= count:
    73  			index = uint(i) - count
    74  			fmt.Printf("%3d# ", index)
    75  		case i&1 == 1:
    76  			fmt.Printf("%3d* ", index)
    77  		default:
    78  			fmt.Printf("%3d  ", index)
    79  		}
    80  		if item == 0 {
    81  			fmt.Println()
    82  			continue
    83  		}
    84  		fmt.Printf("%064b\n", item)
    85  	}
    86  }
    87  
    88  func TestPathBuilder_WalkUnstubbed(t *testing.T) {
    89  	for count := uint(0); count < 64; count++ {
    90  		leafCount := 1 + count
    91  		t.Run(fmt.Sprint(leafCount), func(t *testing.T) {
    92  			testWalk(t, leafCount, cryptkit.Digest{})
    93  		})
    94  	}
    95  }
    96  
    97  func TestPathBuilder_WalkStubbed(t *testing.T) {
    98  	stubDigest := cryptkit.NewDigest(newBits64(uint64(1)<<63), "x")
    99  
   100  	for count := uint(0); count < 64; count++ {
   101  		leafCount := 1 + count
   102  		t.Run(fmt.Sprint(leafCount), func(t *testing.T) {
   103  			testWalk(t, leafCount, stubDigest)
   104  		})
   105  	}
   106  }
   107  
   108  func testWalk(t *testing.T, leafCount uint, stubDigest cryptkit.Digest) {
   109  
   110  	var stub uint64
   111  	if !stubDigest.IsEmpty() {
   112  		stub = stubDigest.FoldToUint64()
   113  	}
   114  
   115  	trace, expected := buildTrace(t, leafCount, stubDigest, false)
   116  	// fmt.Println("\nTrace: ", leafCount, " ============================================================== ")
   117  	// printTrace(trace, leafCount)
   118  
   119  	pb := NewPathBuilder(leafCount, !stubDigest.IsEmpty())
   120  
   121  	// fmt.Println()
   122  	// for i, level := range pb.levels {
   123  	// 	fmt.Printf("L%d	%3d %3d\n", i+1, level.nodeValueL, level.nodeValueR)
   124  	// }
   125  	//
   126  	// fmt.Println()
   127  
   128  	for i := uint(0); i < pb.count; i++ {
   129  		// fmt.Println("\nIndex: ", i, "/", leafCount, " ======== ")
   130  
   131  		total := uint64(1) << i
   132  		// fmt.Printf("     %064b\n", total)
   133  		pb.WalkFor(i, func(index uint, isLeaf, _ bool) {
   134  
   135  			switch {
   136  			case isLeaf:
   137  				// fmt.Printf("%3d  ", index)
   138  				index <<= 1
   139  			case pb.stubbed && index == 0:
   140  				// fmt.Printf("stub %064b\n", stub)
   141  				total ^= stub
   142  				return
   143  			case index < pb.count:
   144  				// fmt.Printf("%3d* ", index)
   145  				index = index<<1 + 1
   146  			default:
   147  				// fmt.Printf("%3d# ", index)
   148  				index = pb.count + index
   149  			}
   150  			if index >= uint(len(trace)) {
   151  				// fmt.Println("-")
   152  				return
   153  			}
   154  
   155  			u := trace[index]
   156  			switch {
   157  			case leafCount > 64:
   158  				//
   159  			case u == 0:
   160  				require.Fail(t, "empty entry")
   161  			default:
   162  				v := u &^ stub
   163  				assert.Zero(t, v&total)
   164  			}
   165  			total ^= u
   166  			// fmt.Printf("%064b\n", u)
   167  		})
   168  		// fmt.Printf("ALL  %064b\n", total)
   169  		assert.Equal(t, expected, total)
   170  	}
   171  }