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

     1  package merkletree2
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestEncoding(t *testing.T) {
    13  
    14  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
    15  
    16  	encodingTests := []struct {
    17  		c   Config
    18  		bin string
    19  		p   Position
    20  	}{
    21  		{config1bit, "1", *config1bit.GetRootPosition()},
    22  		{config2bits, "1", *config2bits.GetRootPosition()},
    23  		{config3bits, "1", *config3bits.GetRootPosition()},
    24  		{config1bit, "10", *config1bit.GetChild(config1bit.GetRootPosition(), 0)},
    25  		{config2bits, "100", *config2bits.GetChild(config2bits.GetRootPosition(), 0)},
    26  		{config3bits, "1000", *config3bits.GetChild(config3bits.GetRootPosition(), 0)},
    27  		{config1bit, "101", *config1bit.GetChild(config1bit.GetChild(config1bit.GetRootPosition(), 0), 1)},
    28  		{config2bits, "10011", *config2bits.GetChild(config2bits.GetChild(config2bits.GetRootPosition(), 0), 3)},
    29  		{config3bits, "1000101", *config3bits.GetChild(config3bits.GetChild(config3bits.GetRootPosition(), 0), 5)},
    30  	}
    31  
    32  	for _, et := range encodingTests {
    33  		t.Run(fmt.Sprintf("%v bits: %s", et.c.BitsPerIndex, et.bin), func(t *testing.T) {
    34  			exp, err := strconv.ParseInt(et.bin, 2, 64)
    35  			require.NoError(t, err)
    36  			require.Equal(t, exp, (*big.Int)(&et.p).Int64())
    37  		})
    38  	}
    39  }
    40  
    41  func TestGetAndUpdateParentAndGetChild(t *testing.T) {
    42  
    43  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
    44  
    45  	parentChildTests := []struct {
    46  		c      Config
    47  		parent string
    48  		child  string
    49  		i      ChildIndex
    50  	}{
    51  		{config1bit, "1", "10", 0},
    52  		{config1bit, "1", "11", 1},
    53  		{config1bit, "11", "111", 1},
    54  		{config2bits, "1", "100", 0},
    55  		{config2bits, "1", "101", 1},
    56  		{config2bits, "1000100", "100010011", 3},
    57  		{config3bits, "1", "1100", 4},
    58  		{config3bits, "1111", "1111101", 5},
    59  	}
    60  
    61  	for _, test := range parentChildTests {
    62  		t.Run(fmt.Sprintf("%v bits: %s -(%v)-> %s", test.c.BitsPerIndex, test.parent, test.i, test.child), func(t *testing.T) {
    63  			child, err := makePositionFromStringForTesting(test.child)
    64  			require.NoError(t, err)
    65  			parent, err := makePositionFromStringForTesting(test.parent)
    66  			require.NoError(t, err)
    67  			require.True(t, test.c.getParent(&child).Equals(&parent))
    68  			require.True(t, test.c.GetChild(&parent, test.i).Equals(&child))
    69  			parentInPlace := child.Clone()
    70  			test.c.updateToParent(parentInPlace)
    71  			require.True(t, parentInPlace.Equals(&parent))
    72  		})
    73  	}
    74  }
    75  
    76  func TestUpdateToParentAtLevel(t *testing.T) {
    77  
    78  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
    79  
    80  	tests := []struct {
    81  		c      Config
    82  		parent string
    83  		child  string
    84  		level  uint
    85  	}{
    86  		{config1bit, "1", "1", 0},
    87  		{config1bit, "1", "10", 0},
    88  		{config1bit, "1", "1100100", 0},
    89  		{config2bits, "1", "1100", 0},
    90  		{config3bits, "1", "1111101", 0},
    91  		{config1bit, "111", "111", 2},
    92  		{config1bit, "110", "11010", 2},
    93  		{config2bits, "110", "11001", 1},
    94  		{config3bits, "1111", "1111101", 1},
    95  		{config3bits, "1111001000", "1111001000001000", 3},
    96  	}
    97  
    98  	for _, test := range tests {
    99  		t.Run(fmt.Sprintf("%v bits: %s -(%v)-> %s", test.c.BitsPerIndex, test.child, test.level, test.parent), func(t *testing.T) {
   100  			childToUpdate, err := makePositionFromStringForTesting(test.child)
   101  			require.NoError(t, err)
   102  			parent, err := makePositionFromStringForTesting(test.parent)
   103  			require.NoError(t, err)
   104  			test.c.updateToParentAtLevel(&childToUpdate, test.level)
   105  			require.True(t, parent.Equals(&childToUpdate), "expected: %x actual: %x", parent, childToUpdate)
   106  		})
   107  	}
   108  
   109  }
   110  
   111  func TestUpdateToParentAndAllSiblings(t *testing.T) {
   112  
   113  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   114  
   115  	tests := []struct {
   116  		c            Config
   117  		pStr         string
   118  		expParentStr string
   119  		expSiblings  []string
   120  	}{
   121  		{config1bit, "1001111", "100111", []string{"1001110"}},
   122  		{config1bit, "1001", "100", []string{"1000"}},
   123  		{config2bits, "1001111", "10011", []string{"1001100", "1001101", "1001110"}},
   124  		{config3bits, "1001111", "1001", []string{"1001000", "1001001", "1001010", "1001011", "1001100", "1001101", "1001110"}},
   125  	}
   126  
   127  	for _, test := range tests {
   128  		t.Run(fmt.Sprintf("%v bits: %v", test.c.BitsPerIndex, test.pStr), func(t *testing.T) {
   129  			p, err := makePositionFromStringForTesting(test.pStr)
   130  			require.NoError(t, err)
   131  			parent, err := makePositionFromStringForTesting(test.expParentStr)
   132  			require.NoError(t, err)
   133  			siblings := make([]Position, test.c.ChildrenPerNode-1)
   134  
   135  			test.c.updateToParentAndAllSiblings(&p, siblings)
   136  			require.True(t, p.Equals(&parent))
   137  			for i, expPosStr := range test.expSiblings {
   138  				expPos, err := makePositionFromStringForTesting(expPosStr)
   139  				require.NoError(t, err)
   140  				require.True(t, expPos.Equals(&siblings[i]), "Error at sibling %v, got %v", expPosStr, siblings[i])
   141  			}
   142  		})
   143  	}
   144  }
   145  
   146  func TestNewConfigError(t *testing.T) {
   147  
   148  	_, err := NewConfig(nil, false, 5, 8, 6, ConstructStringValueContainer)
   149  	require.Error(t, err)
   150  	require.IsType(t, InvalidConfigError{}, err)
   151  
   152  	c, err := NewConfig(nil, false, 2, 4, 32, ConstructStringValueContainer)
   153  	require.NoError(t, err)
   154  
   155  	require.Equal(t, 1<<c.BitsPerIndex, c.ChildrenPerNode)
   156  }
   157  
   158  func TestPositionIsOnPathToKey(t *testing.T) {
   159  
   160  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   161  
   162  	tests := []struct {
   163  		c        Config
   164  		p        string
   165  		k        Key
   166  		expected bool
   167  	}{
   168  		{config1bit, "1", []byte{0x00, 0x01, 0x02}, true},
   169  		{config2bits, "1", []byte{0x00, 0x01, 0x02}, true},
   170  		{config3bits, "1", []byte{0x00, 0x01, 0x02}, true},
   171  		{config1bit, "10", []byte{0x00, 0x01, 0x02}, true},
   172  		{config2bits, "100", []byte{0x00, 0x01, 0x02}, true},
   173  		{config3bits, "1000", []byte{0x00, 0x01, 0x02}, true},
   174  		{config1bit, "11", []byte{0x00, 0x01, 0x02}, false},
   175  		{config2bits, "110", []byte{0x00, 0x01, 0x02}, false},
   176  		{config3bits, "1100", []byte{0x00, 0x01, 0x02}, false},
   177  		{config1bit, "101", []byte{0x00, 0x01, 0x02}, false},
   178  		{config2bits, "1000000000000000100", []byte{0x00, 0x01, 0x02}, true},
   179  		{config2bits, "1000000000000000000", []byte{0x00, 0x01, 0x02}, false},
   180  	}
   181  
   182  	for _, test := range tests {
   183  		t.Run(fmt.Sprintf("%v bits: %v %v", test.c.BitsPerIndex, test.p, test.k), func(t *testing.T) {
   184  			pos, err := makePositionFromStringForTesting(test.p)
   185  			require.NoError(t, err)
   186  			require.Equal(t, test.expected, pos.isOnPathToKey(test.k))
   187  		})
   188  	}
   189  
   190  }
   191  
   192  func TestGetDeepestPositionAtLevelAndSiblingsOnPathToKey(t *testing.T) {
   193  
   194  	config1bit, config2bits, _ := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   195  
   196  	tests := []struct {
   197  		c            Config
   198  		lastLevel    int
   199  		firstLevel   int
   200  		k            Key
   201  		expPosOnPath []string
   202  	}{
   203  		{config1bit, 8, 1, []byte{0xf0}, []string{"111110000", "111110001", "11111001", "1111101", "111111", "11110", "1110", "110", "10"}},
   204  		{config1bit, 2, 1, []byte{0xf0}, []string{"111", "110", "10"}},
   205  		{config1bit, 3, 1, []byte{0xf0}, []string{"1111", "1110", "110", "10"}},
   206  		{config1bit, 3, 2, []byte{0xf0}, []string{"1111", "1110", "110"}},
   207  		{config1bit, 4, 2, []byte{0xf0}, []string{"11111", "11110", "1110", "110"}},
   208  		{config1bit, 8, 1, []byte{0x00}, []string{"100000000", "100000001", "10000001", "1000001", "100001", "10001", "1001", "101", "11"}},
   209  		{config1bit, 2, 1, []byte{0x00}, []string{"100", "101", "11"}},
   210  		{config1bit, 3, 1, []byte{0x00}, []string{"1000", "1001", "101", "11"}},
   211  		{config1bit, 3, 2, []byte{0x00}, []string{"1000", "1001", "101"}},
   212  		{config1bit, 4, 2, []byte{0x00}, []string{"10000", "10001", "1001", "101"}},
   213  		{config1bit, 1, 1, []byte{0x00}, []string{"10", "11"}},
   214  		{config2bits, 4, 1, []byte{0xf1}, []string{"111110001", "111110000", "111110010", "111110011", "1111101", "1111110", "1111111", "11100", "11101", "11110", "100", "101", "110"}},
   215  	}
   216  
   217  	for _, test := range tests {
   218  		t.Run(fmt.Sprintf("%v bits: %v", test.c.BitsPerIndex, test.k), func(t *testing.T) {
   219  			posOnPath := test.c.getDeepestPositionAtLevelAndSiblingsOnPathToKey(test.k, test.lastLevel, test.firstLevel)
   220  			require.Equal(t, len(test.expPosOnPath), len(posOnPath))
   221  			for i, expPosStr := range test.expPosOnPath {
   222  				expPos, err := makePositionFromStringForTesting(expPosStr)
   223  				require.NoError(t, err)
   224  				require.True(t, expPos.Equals(&posOnPath[i]), "Error at position %v, got %v", expPosStr, posOnPath[i])
   225  			}
   226  		})
   227  	}
   228  }
   229  
   230  func TestGetLevel(t *testing.T) {
   231  
   232  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   233  
   234  	tests := []struct {
   235  		c   Config
   236  		pos string
   237  		lev uint
   238  	}{
   239  		{config1bit, "1", 0},
   240  		{config2bits, "1", 0},
   241  		{config3bits, "1", 0},
   242  		{config1bit, "10", 1},
   243  		{config2bits, "100", 1},
   244  		{config3bits, "1001", 1},
   245  		{config1bit, "1001000", 6},
   246  		{config2bits, "1001000", 3},
   247  		{config3bits, "1001000", 2},
   248  		{config3bits, "1001000001000", 4},
   249  	}
   250  
   251  	for _, test := range tests {
   252  		t.Run(fmt.Sprintf("%v bits: pos %v lev %v", test.c.BitsPerIndex, test.pos, test.lev), func(t *testing.T) {
   253  			pos, err := makePositionFromStringForTesting(test.pos)
   254  			require.NoError(t, err)
   255  
   256  			require.Equal(t, int(test.lev), test.c.getLevel(&pos))
   257  		})
   258  	}
   259  
   260  }
   261  
   262  func TestGetParentAtLevel(t *testing.T) {
   263  
   264  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   265  
   266  	tests := []struct {
   267  		c   Config
   268  		pos string
   269  		par string
   270  		lev uint
   271  	}{
   272  		{config1bit, "1001000", "1", 0},
   273  		{config2bits, "1001000", "1", 0},
   274  		{config3bits, "1001000", "1", 0},
   275  		{config1bit, "1001000", "10", 1},
   276  		{config2bits, "1001000", "100", 1},
   277  		{config3bits, "1001000", "1001", 1},
   278  		{config1bit, "1001000", "100", 2},
   279  		{config2bits, "1001000", "10010", 2},
   280  		{config3bits, "1001000", "1001000", 2},
   281  		{config3bits, "1001000001000", "1001000", 2},
   282  	}
   283  
   284  	for _, test := range tests {
   285  		t.Run(fmt.Sprintf("%v bits: pos %v lev %v", test.c.BitsPerIndex, test.pos, test.lev), func(t *testing.T) {
   286  			pos, err := makePositionFromStringForTesting(test.pos)
   287  			require.NoError(t, err)
   288  			expParent, err := makePositionFromStringForTesting(test.par)
   289  			require.NoError(t, err)
   290  
   291  			require.True(t, expParent.Equals(test.c.getParentAtLevel(&pos, test.lev)))
   292  		})
   293  	}
   294  
   295  }
   296  
   297  func TestPositionToChildIndexPath(t *testing.T) {
   298  
   299  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   300  
   301  	tests := []struct {
   302  		c   Config
   303  		pos string
   304  		p   []ChildIndex
   305  	}{
   306  		{config1bit, "1", []ChildIndex{}},
   307  		{config2bits, "1", []ChildIndex{}},
   308  		{config3bits, "1", []ChildIndex{}},
   309  		{config1bit, "10", []ChildIndex{0}},
   310  		{config1bit, "11", []ChildIndex{1}},
   311  		{config2bits, "100", []ChildIndex{0}},
   312  		{config2bits, "111", []ChildIndex{3}},
   313  		{config3bits, "1001", []ChildIndex{1}},
   314  		{config1bit, "1001000", []ChildIndex{0, 0, 0, 1, 0, 0}},
   315  		{config2bits, "1001001", []ChildIndex{1, 2, 0}},
   316  		{config2bits, "1001010", []ChildIndex{2, 2, 0}},
   317  		{config3bits, "1001000", []ChildIndex{0, 1}},
   318  		{config3bits, "1001000001000", []ChildIndex{0, 1, 0, 1}},
   319  	}
   320  
   321  	for _, test := range tests {
   322  		t.Run(fmt.Sprintf("%v bits: pos %v", test.c.BitsPerIndex, test.pos), func(t *testing.T) {
   323  			pos, err := makePositionFromStringForTesting(test.pos)
   324  			require.NoError(t, err)
   325  
   326  			require.Equal(t, test.p, test.c.positionToChildIndexPath(&pos))
   327  		})
   328  	}
   329  
   330  }
   331  
   332  func TestGetDeepestChildIndex(t *testing.T) {
   333  
   334  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   335  
   336  	tests := []struct {
   337  		c   Config
   338  		pos string
   339  		lev ChildIndex
   340  	}{
   341  		{config1bit, "1", 0},
   342  		{config2bits, "1", 0},
   343  		{config3bits, "1", 0},
   344  		{config1bit, "10", 0},
   345  		{config1bit, "11", 1},
   346  		{config2bits, "100", 0},
   347  		{config2bits, "111", 3},
   348  		{config3bits, "1001", 1},
   349  		{config1bit, "1001000", 0},
   350  		{config2bits, "1001001", 1},
   351  		{config2bits, "1001010", 2},
   352  		{config3bits, "1001000", 0},
   353  		{config3bits, "1001000001110", 6},
   354  	}
   355  
   356  	for _, test := range tests {
   357  		t.Run(fmt.Sprintf("%v bits: pos %v lev %v", test.c.BitsPerIndex, test.pos, test.lev), func(t *testing.T) {
   358  			pos, err := makePositionFromStringForTesting(test.pos)
   359  			require.NoError(t, err)
   360  
   361  			require.Equal(t, test.lev, test.c.getDeepestChildIndex(&pos))
   362  		})
   363  	}
   364  
   365  }
   366  
   367  func TestGetKeyIntervalUnderPosition(t *testing.T) {
   368  
   369  	config1bit, config2bits, config3bits := getTreeCfgsWith1_2_3BitsPerIndexUnblinded(t)
   370  
   371  	tests := []struct {
   372  		c        Config
   373  		position string
   374  		minKey   Key
   375  		maxKey   Key
   376  	}{
   377  		{config1bit, "1", Key([]byte{0x00}), Key([]byte{0xff})},
   378  		{config1bit, "11", Key([]byte{0x80}), Key([]byte{0xff})},
   379  		{config1bit, "101", Key([]byte{0x40}), Key([]byte{0x7f})},
   380  		{config1bit, "10100", Key([]byte{0x40}), Key([]byte{0x4f})},
   381  		{config2bits, "1", Key([]byte{0x00}), Key([]byte{0xff})},
   382  		{config2bits, "101", Key([]byte{0x40}), Key([]byte{0x7f})},
   383  		{config2bits, "10100", Key([]byte{0x40}), Key([]byte{0x4f})},
   384  		{config3bits, "1", Key([]byte{0x00, 0x00, 0x00}), Key([]byte{0xff, 0xff, 0xff})},
   385  		{config3bits, "1010", Key([]byte{0x40, 0x00, 0x00}), Key([]byte{0x5f, 0xff, 0xff})},
   386  	}
   387  
   388  	for _, test := range tests {
   389  		t.Run(fmt.Sprintf("%v bits: %s", test.c.BitsPerIndex, test.position), func(t *testing.T) {
   390  			p, err := makePositionFromStringForTesting(test.position)
   391  			require.NoError(t, err)
   392  			min, max := test.c.GetKeyIntervalUnderPosition(&p)
   393  			require.Equal(t, test.minKey, min)
   394  			require.Equal(t, test.maxKey, max)
   395  		})
   396  	}
   397  
   398  }