github.com/iotexproject/iotex-core@v1.14.1-rc1/db/trie/mptrie/branchnode_test.go (about)

     1  // Copyright (c) 2020 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package mptrie
     7  
     8  import (
     9  	"bytes"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/iotexproject/iotex-core/db/trie"
    15  	"github.com/iotexproject/iotex-core/db/trie/triepb"
    16  )
    17  
    18  func equals(bn *branchNode, clone *branchNode) bool {
    19  	if bn.isRoot != clone.isRoot {
    20  		return false
    21  	}
    22  	if bn.dirty != clone.dirty {
    23  		return false
    24  	}
    25  	if !bytes.Equal(bn.hashVal, clone.hashVal) || !bytes.Equal(bn.ser, clone.ser) {
    26  		return false
    27  	}
    28  	if len(bn.children) != len(clone.children) {
    29  		return false
    30  	}
    31  	for key, child := range clone.children {
    32  		if bn.children[key] != child {
    33  			return false
    34  		}
    35  	}
    36  	indices := bn.indices.List()
    37  	cloneIndices := clone.indices.List()
    38  	if len(indices) != len(cloneIndices) {
    39  		return false
    40  	}
    41  	for i, value := range cloneIndices {
    42  		if indices[i] != value {
    43  			return false
    44  		}
    45  	}
    46  	return true
    47  }
    48  
    49  func TestBranchNodeClone(t *testing.T) {
    50  	require := require.New(t)
    51  	cli := &merklePatriciaTrie{async: true, hashFunc: DefaultHashFunc}
    52  	t.Run("dirty empty root", func(t *testing.T) {
    53  		children := map[byte]node{}
    54  		indices := NewSortedList(children)
    55  		node, err := newRootBranchNode(cli, children, indices, true)
    56  		require.NoError(err)
    57  		bn, ok := node.(*branchNode)
    58  		require.True(ok)
    59  		clone, err := node.Clone()
    60  		require.NoError(err)
    61  		cbn, ok := clone.(*branchNode)
    62  		require.True(ok)
    63  		require.True(equals(bn, cbn))
    64  	})
    65  	t.Run("clean empty root", func(t *testing.T) {
    66  		children := map[byte]node{}
    67  		indices := NewSortedList(children)
    68  		node, err := newRootBranchNode(cli, children, indices, false)
    69  		require.NoError(err)
    70  		bn, ok := node.(*branchNode)
    71  		require.True(ok)
    72  		clone, err := node.Clone()
    73  		require.NoError(err)
    74  		cbn, ok := clone.(*branchNode)
    75  		require.True(ok)
    76  		require.True(equals(bn, cbn))
    77  	})
    78  	t.Run("normal branch node", func(t *testing.T) {
    79  		children := map[byte]node{}
    80  		children['a'] = &hashNode{hashVal: []byte("a")}
    81  		children['b'] = &hashNode{hashVal: []byte("b")}
    82  		children['c'] = &hashNode{hashVal: []byte("c")}
    83  		children['d'] = &hashNode{hashVal: []byte("d")}
    84  		indices := NewSortedList(children)
    85  		node, err := newBranchNode(cli, children, indices)
    86  		require.NoError(err)
    87  		bn, ok := node.(*branchNode)
    88  		require.True(ok)
    89  		clone, err := bn.Clone()
    90  		require.NoError(err)
    91  		cbn, ok := clone.(*branchNode)
    92  		require.True(ok)
    93  		require.True(equals(bn, cbn))
    94  	})
    95  }
    96  
    97  func TestBranchNodeProto(t *testing.T) {
    98  	require := require.New(t)
    99  	children := map[byte]node{}
   100  	children['a'] = &hashNode{hashVal: []byte("a")}
   101  	children['b'] = &hashNode{hashVal: []byte("b")}
   102  	children['c'] = &hashNode{hashVal: []byte("c")}
   103  	children['d'] = &hashNode{hashVal: []byte("d")}
   104  	indices := NewSortedList(children)
   105  	bnode := &branchNode{
   106  		children: children,
   107  		indices:  indices,
   108  	}
   109  	cli := &merklePatriciaTrie{async: true, hashFunc: DefaultHashFunc}
   110  	proto, err := bnode.proto(cli, true)
   111  	require.NoError(err)
   112  	nodepb, ok := proto.(*triepb.NodePb)
   113  	require.True(ok)
   114  	branch, ok := nodepb.Node.(*triepb.NodePb_Branch)
   115  	require.True(ok)
   116  	bnode1 := newBranchNodeFromProtoPb(branch.Branch, nil)
   117  	for key, child := range bnode1.children {
   118  		h, err := bnode.children[key].Hash(cli)
   119  		require.NoError(err)
   120  		h1, err := child.Hash(cli)
   121  		require.NoError(err)
   122  		require.Equal(h, h1)
   123  	}
   124  	li := bnode.indices.List()
   125  	for i, value := range bnode1.indices.List() {
   126  		require.Equal(li[i], value)
   127  	}
   128  	require.Equal(bnode.isRoot, bnode1.isRoot)
   129  	require.Equal(bnode.dirty, bnode1.dirty)
   130  }
   131  
   132  func TestBranchNodeChildren(t *testing.T) {
   133  	require := require.New(t)
   134  	children := map[byte]node{}
   135  	children['a'] = &hashNode{hashVal: []byte("a")}
   136  	children['b'] = &hashNode{hashVal: []byte("b")}
   137  	children['c'] = &hashNode{hashVal: []byte("c")}
   138  	children['d'] = &hashNode{hashVal: []byte("d")}
   139  	indices := NewSortedList(children)
   140  	bnode := &branchNode{
   141  		children: children,
   142  		indices:  indices,
   143  	}
   144  	childs := bnode.Children()
   145  	li := bnode.indices.List()
   146  	for i, node := range childs {
   147  		require.Equal(bnode.children[li[i]], node)
   148  	}
   149  }
   150  
   151  func TestBranchOperation(t *testing.T) {
   152  	var (
   153  		require = require.New(t)
   154  		cli     = &merklePatriciaTrie{
   155  			hashFunc: DefaultHashFunc,
   156  			kvStore:  trie.NewMemKVStore(),
   157  		}
   158  		children = make(map[byte]node)
   159  	)
   160  
   161  	// create
   162  	node, err := newLeafNode(cli, keyType("iotex"), []byte("coin"))
   163  	require.NoError(err)
   164  	children[byte('i')] = node
   165  	node, err = newBranchNode(cli, children, nil)
   166  	require.NoError(err)
   167  	require.Panics(func() { node.Delete(cli, keyType("iotex"), 0) }, "branch shouldn't have 0 child after deleting")
   168  
   169  	// branch.Upsert child.Upsert -> branch
   170  	node, err = node.Upsert(cli, keyType("ioabc123"), 0, []byte("chabc"))
   171  	require.NoError(err)
   172  	bnode, ok := node.(*branchNode)
   173  	require.True(ok)
   174  	require.Len(bnode.children, 1) // ext
   175  	vn, ok := bnode.children[byte('i')]
   176  	require.True(ok)
   177  	ex1, ok := vn.(*extensionNode)
   178  	require.True(ok)
   179  	require.Equal([]byte("o"), ex1.path)
   180  	bn1, ok := ex1.child.(*branchNode)
   181  	require.True(ok)
   182  	require.Len(bn1.children, 2)
   183  	checkLeaf(require, bn1, keyType("iotex"), 2, []byte("coin"))
   184  	checkLeaf(require, bn1, keyType("ioabc123"), 2, []byte("chabc"))
   185  
   186  	// branch.Upsert newLeafNode -> branch
   187  	node, err = node.Upsert(cli, keyType("block"), 0, []byte("chain"))
   188  	require.NoError(err)
   189  	bnode1, ok := node.(*branchNode)
   190  	require.True(ok)
   191  	require.Len(bnode1.children, 2) // block, ext
   192  	require.Equal(bnode.children[byte('i')], bnode1.children[byte('i')])
   193  	checkLeaf(require, bnode1, keyType("block"), 0, []byte("chain"))
   194  
   195  	// branch.Upsert child.Upsert -> branch
   196  	node, err = node.Upsert(cli, keyType("iotex"), 0, []byte("chain"))
   197  	require.NoError(err)
   198  	bnode2, ok := node.(*branchNode)
   199  	require.True(ok)
   200  	require.Len(bnode2.children, 2) // block, ex2
   201  	require.Equal(bnode1.children[byte('b')], bnode2.children[byte('b')])
   202  	require.NotEqual(bnode.children[byte('i')], bnode2.children[byte('i')])
   203  	ex2, ok := bnode2.children[byte('i')].(*extensionNode)
   204  	require.True(ok)
   205  	bn2, ok := ex2.child.(*branchNode)
   206  	require.True(ok)
   207  	checkLeaf(require, bn2, keyType("iotex"), 2, []byte("chain"))
   208  
   209  	// branch.Upsert child.Upsert -> branch
   210  	node, err = node.Upsert(cli, keyType("ixy"), 0, []byte("dog"))
   211  	require.NoError(err)
   212  	bnode3, ok := node.(*branchNode)
   213  	require.True(ok)
   214  	require.Len(bnode3.children, 2) // block, bn3
   215  	require.Equal(bnode1.children[byte('b')], bnode3.children[byte('b')])
   216  	require.NotEqual(bnode.children[byte('i')], bnode3.children[byte('i')])
   217  	bn3, ok := bnode3.children[byte('i')].(*branchNode)
   218  	require.True(ok)
   219  	require.Len(bn3.children, 2) // ixy, ext
   220  	checkLeaf(require, bn3, keyType("ixy"), 1, []byte("dog"))
   221  
   222  	// branch.Upsert child.Upsert -> branch
   223  	node, err = node.Upsert(cli, keyType("idef"), 0, []byte("cat"))
   224  	require.NoError(err)
   225  	bnode4, ok := node.(*branchNode)
   226  	require.True(ok)
   227  	require.Len(bnode4.children, 2) // block, bn4
   228  	require.Equal(bnode1.children[byte('b')], bnode4.children[byte('b')])
   229  	bn4, ok := bnode4.children[byte('i')].(*branchNode)
   230  	require.True(ok)
   231  	require.Len(bn4.children, 3) // idef, ixy, ext
   232  	checkLeaf(require, bn4, keyType("idef"), 1, []byte("cat"))
   233  
   234  	// check key
   235  	node1, err := node.Search(cli, keyType("iotex"), 0)
   236  	require.NoError(err)
   237  	require.Equal([]byte("chain"), node1.(*leafNode).value)
   238  	node1, err = node.Search(cli, keyType("ioabc123"), 0)
   239  	require.NoError(err)
   240  	require.Equal([]byte("chabc"), node1.(*leafNode).value)
   241  	node1, err = node.Search(cli, keyType("block"), 0)
   242  	require.NoError(err)
   243  	require.Equal([]byte("chain"), node1.(*leafNode).value)
   244  	node1, err = node.Search(cli, keyType("ixy"), 0)
   245  	require.NoError(err)
   246  	require.Equal([]byte("dog"), node1.(*leafNode).value)
   247  	node1, err = node.Search(cli, keyType("idef"), 0)
   248  	require.NoError(err)
   249  	require.Equal([]byte("cat"), node1.(*leafNode).value)
   250  
   251  	// branch.Delete default b.updateChild -> extension
   252  	node, err = node.Delete(cli, keyType("idef"), 0)
   253  	require.NoError(err)
   254  	bnode, ok = node.(*branchNode)
   255  	require.True(ok)
   256  	require.Len(bnode.children, 2)
   257  	node1, err = node.Search(cli, keyType("idef"), 0)
   258  	require.Equal(trie.ErrNotExist, err)
   259  
   260  	// branch.Delete case2 case *leafNode -> branch.Delete b.updateChild
   261  	node, err = node.Delete(cli, keyType("ioabc123"), 0)
   262  	require.NoError(err)
   263  	bnode, ok = node.(*branchNode)
   264  	require.True(ok)
   265  	require.Len(bnode.children, 2)
   266  	node1, err = node.Search(cli, keyType("ioabc123"), 0)
   267  	require.Equal(trie.ErrNotExist, err)
   268  
   269  	// branch.Delete case2 default newExtensionNode
   270  	node, err = node.Delete(cli, keyType("block"), 0)
   271  	require.NoError(err)
   272  	enode, ok := node.(*extensionNode)
   273  	require.True(ok)
   274  	bn1, ok = enode.child.(*branchNode)
   275  	require.True(ok)
   276  	require.Len(bn1.children, 2)
   277  	node1, err = node.Search(cli, keyType("block"), 0)
   278  	require.Equal(trie.ErrNotExist, err)
   279  
   280  	// extension.Delete default
   281  	node, err = node.Delete(cli, keyType("iotex"), 0)
   282  	require.NoError(err)
   283  	_, ok = node.(*leafNode)
   284  	require.True(ok)
   285  	node1, err = node.Search(cli, keyType("iotex"), 0)
   286  	require.Equal(trie.ErrNotExist, err)
   287  
   288  	// leaf.Delete
   289  	node, err = node.Delete(cli, keyType("ixy"), 0)
   290  	require.NoError(err)
   291  	require.Nil(node)
   292  
   293  	// children is nil
   294  	node, err = newRootBranchNode(cli, nil, nil, false)
   295  	require.NoError(err)
   296  	node, err = node.Delete(cli, keyType{1}, 0)
   297  	require.Equal(trie.ErrNotExist, err)
   298  	require.Nil(node)
   299  
   300  	// children is max
   301  	node, err = newRootBranchNode(cli, nil, nil, false)
   302  	for i := byte(0); i < 255; i++ {
   303  		node, err = node.Upsert(cli, keyType{1, i}, 1, []byte{i})
   304  		require.NoError(err)
   305  	}
   306  	require.Len(node.(*branchNode).children, 255)
   307  	n1, err := node.Search(cli, keyType{1, 2}, 1)
   308  	require.NoError(err)
   309  	require.Equal([]byte{2}, n1.(*leafNode).value)
   310  	node, err = node.Upsert(cli, keyType{2, 3, 4, 5}, 0, []byte{2, 3, 4, 5})
   311  	require.NoError(err)
   312  	n1, err = node.Search(cli, keyType{2, 3, 4, 5}, 0)
   313  	require.NoError(err)
   314  	require.Equal([]byte{2, 3, 4, 5}, n1.(*leafNode).value)
   315  	require.Panics(func() { node.Search(cli, keyType{1, 2}, 1) }, "keyType{1, 2} is not exist")
   316  }
   317  
   318  func checkLeaf(require *require.Assertions, bnode *branchNode, key keyType, offset uint8, value []byte) {
   319  	child, ok := bnode.children[key[offset]]
   320  	require.True(ok)
   321  	ln, ok := child.(*leafNode)
   322  	require.True(ok)
   323  	require.Equal(value, ln.value)
   324  }