github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/complete/mtrie/flattener/encoding_v4_test.go (about)

     1  package flattener_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/onflow/flow-go/ledger"
    13  	"github.com/onflow/flow-go/ledger/common/hash"
    14  	"github.com/onflow/flow-go/ledger/common/testutils"
    15  	"github.com/onflow/flow-go/ledger/complete/mtrie/flattener"
    16  	"github.com/onflow/flow-go/ledger/complete/mtrie/node"
    17  	"github.com/onflow/flow-go/ledger/complete/mtrie/trie"
    18  )
    19  
    20  func TestLeafNodeV4Decoding(t *testing.T) {
    21  
    22  	// Leaf node with nil payload
    23  	path1 := testutils.PathByUint8(0)
    24  	payload1 := (*ledger.Payload)(nil)
    25  	hashValue1 := hash.Hash([32]byte{1, 1, 1})
    26  	leafNodeNilPayload := node.NewNode(255, nil, nil, ledger.Path(path1), payload1, hashValue1)
    27  
    28  	// Leaf node with empty payload (not nil)
    29  	// EmptyPayload() not used because decoded playload's value is empty slice (not nil)
    30  	path2 := testutils.PathByUint8(1)
    31  	payload2 := ledger.NewPayload(ledger.Key{}, []byte{})
    32  	hashValue2 := hash.Hash([32]byte{2, 2, 2})
    33  	leafNodeEmptyPayload := node.NewNode(255, nil, nil, ledger.Path(path2), payload2, hashValue2)
    34  
    35  	// Leaf node with payload
    36  	path3 := testutils.PathByUint8(2)
    37  	payload3 := testutils.LightPayload8('A', 'a')
    38  	hashValue3 := hash.Hash([32]byte{3, 3, 3})
    39  	leafNodePayload := node.NewNode(255, nil, nil, ledger.Path(path3), payload3, hashValue3)
    40  
    41  	encodedLeafNodeNilPayload := []byte{
    42  		0x00,       // node type
    43  		0x00, 0xff, // height
    44  		0x00, 0x00, // max depth
    45  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // reg count
    46  		0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    47  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    48  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    49  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hash data
    50  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    51  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    52  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    53  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // path data
    54  		0x00, 0x00, 0x00, 0x00, // payload data len
    55  	}
    56  
    57  	encodedLeafNodeEmptyPayload := []byte{
    58  		0x00,       // node type
    59  		0x00, 0xff, // height
    60  		0x00, 0x00, // max depth
    61  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // reg count
    62  		0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
    63  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    64  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    65  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hash data
    66  		0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    67  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    68  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    69  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // path data
    70  		0x00, 0x00, 0x00, 0x0e, // payload data len
    71  		0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
    72  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // payload data
    73  	}
    74  
    75  	encodedLeafNodePayload := []byte{
    76  		0x00,       // node type
    77  		0x00, 0xff, // height
    78  		0x00, 0x00, // max depth
    79  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // reg count
    80  		0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
    81  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    82  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    83  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hash data
    84  		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    85  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    86  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    87  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // path data
    88  		0x00, 0x00, 0x00, 0x16, // payload data len
    89  		0x00, 0x00, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00,
    90  		0x00, 0x03, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
    91  		0x00, 0x00, 0x00, 0x00, 0x01, 0x61, // payload data
    92  	}
    93  
    94  	testCases := []struct {
    95  		name        string
    96  		node        *node.Node
    97  		encodedNode []byte
    98  	}{
    99  		{"nil payload", leafNodeNilPayload, encodedLeafNodeNilPayload},
   100  		{"empty payload", leafNodeEmptyPayload, encodedLeafNodeEmptyPayload},
   101  		{"payload", leafNodePayload, encodedLeafNodePayload},
   102  	}
   103  
   104  	for _, tc := range testCases {
   105  		t.Run("decode "+tc.name, func(t *testing.T) {
   106  			scratchBuffers := [][]byte{
   107  				nil,
   108  				make([]byte, 0),
   109  				make([]byte, 16),
   110  				make([]byte, 1024),
   111  			}
   112  
   113  			for _, scratch := range scratchBuffers {
   114  				reader := bytes.NewReader(tc.encodedNode)
   115  				newNode, regCount, regSize, err := flattener.ReadNodeFromCheckpointV4(reader, scratch, func(nodeIndex uint64) (*node.Node, uint64, uint64, error) {
   116  					return nil, 0, 0, fmt.Errorf("no call expected")
   117  				})
   118  				require.NoError(t, err)
   119  				assert.Equal(t, tc.node, newNode)
   120  				assert.Equal(t, 0, reader.Len())
   121  				require.Equal(t, uint64(1), regCount)
   122  				require.Equal(t, uint64(tc.node.Payload().Size()), regSize)
   123  			}
   124  		})
   125  	}
   126  }
   127  
   128  func TestInterimNodeV4Decoding(t *testing.T) {
   129  
   130  	const lchildIndex = 1
   131  	const rchildIndex = 2
   132  
   133  	// Child node
   134  	path1 := testutils.PathByUint8(0)
   135  	payload1 := testutils.LightPayload8('A', 'a')
   136  	hashValue1 := hash.Hash([32]byte{1, 1, 1})
   137  	leafNode1 := node.NewNode(255, nil, nil, ledger.Path(path1), payload1, hashValue1)
   138  
   139  	// Child node
   140  	path2 := testutils.PathByUint8(1)
   141  	payload2 := testutils.LightPayload8('B', 'b')
   142  	hashValue2 := hash.Hash([32]byte{2, 2, 2})
   143  	leafNode2 := node.NewNode(255, nil, nil, ledger.Path(path2), payload2, hashValue2)
   144  
   145  	// Interim node
   146  	hashValue3 := hash.Hash([32]byte{3, 3, 3})
   147  	interimNode := node.NewNode(256, leafNode1, leafNode2, ledger.DummyPath, nil, hashValue3)
   148  
   149  	encodedInterimNode := []byte{
   150  		0x01,       // node type
   151  		0x01, 0x00, // height
   152  		0x00, 0x01, // max depth
   153  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // reg count
   154  		0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
   155  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   156  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   157  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hash data
   158  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // LIndex
   159  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // RIndex
   160  	}
   161  
   162  	t.Run("decode", func(t *testing.T) {
   163  		scratchBuffers := [][]byte{
   164  			nil,
   165  			make([]byte, 0),
   166  			make([]byte, 16),
   167  			make([]byte, 1024),
   168  		}
   169  
   170  		for _, scratch := range scratchBuffers {
   171  			reader := bytes.NewReader(encodedInterimNode)
   172  			newNode, regCount, regSize, err := flattener.ReadNodeFromCheckpointV4(reader, scratch, func(nodeIndex uint64) (*node.Node, uint64, uint64, error) {
   173  				switch nodeIndex {
   174  				case lchildIndex:
   175  					return leafNode1, 1, uint64(leafNode1.Payload().Size()), nil
   176  				case rchildIndex:
   177  					return leafNode2, 1, uint64(leafNode2.Payload().Size()), nil
   178  				default:
   179  					return nil, 0, 0, fmt.Errorf("unexpected child node index %d ", nodeIndex)
   180  				}
   181  			})
   182  			require.NoError(t, err)
   183  			assert.Equal(t, interimNode, newNode)
   184  			assert.Equal(t, 0, reader.Len())
   185  			require.Equal(t, uint64(2), regCount)
   186  			require.Equal(t, uint64(leafNode1.Payload().Size()+leafNode2.Payload().Size()), regSize)
   187  		}
   188  	})
   189  
   190  	t.Run("decode child node not found error", func(t *testing.T) {
   191  		nodeNotFoundError := errors.New("failed to find node by index")
   192  		scratch := make([]byte, 1024)
   193  
   194  		reader := bytes.NewReader(encodedInterimNode)
   195  		newNode, regCount, regSize, err := flattener.ReadNodeFromCheckpointV4(reader, scratch, func(nodeIndex uint64) (*node.Node, uint64, uint64, error) {
   196  			return nil, 0, 0, nodeNotFoundError
   197  		})
   198  		require.Nil(t, newNode)
   199  		require.ErrorIs(t, err, nodeNotFoundError)
   200  		require.Equal(t, uint64(0), regCount)
   201  		require.Equal(t, uint64(0), regSize)
   202  	})
   203  }
   204  
   205  func TestTrieV4Decoding(t *testing.T) {
   206  	// Trie with nil root node
   207  	emptyTrieRootNodeIndex := uint64(20)
   208  	emptyTrie := trie.NewEmptyMTrie()
   209  
   210  	// Trie with not nil root node
   211  	hashValue := hash.Hash([32]byte{2, 2, 2})
   212  	rootNode := node.NewNode(256, nil, nil, ledger.DummyPath, nil, hashValue)
   213  	notEmptyTrieRootNodeIndex := uint64(21)
   214  	notEmptyTrie, err := trie.NewMTrie(rootNode, 7, 5000)
   215  	require.NoError(t, err)
   216  
   217  	encodedEmptyTrie := []byte{
   218  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, // RootIndex
   219  		0x56, 0x8f, 0x4e, 0xc7, 0x40, 0xfe, 0x3b, 0x5d,
   220  		0xe8, 0x80, 0x34, 0xcb, 0x7b, 0x1f, 0xbd, 0xdb,
   221  		0x41, 0x54, 0x8b, 0x06, 0x8f, 0x31, 0xae, 0xbc,
   222  		0x8a, 0xe9, 0x18, 0x9e, 0x42, 0x9c, 0x57, 0x49, // RootHash data
   223  	}
   224  
   225  	encodedTrie := []byte{
   226  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, // RootIndex
   227  		0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
   228  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   229  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   230  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // hash data
   231  	}
   232  
   233  	testCases := []struct {
   234  		name          string
   235  		tr            *trie.MTrie
   236  		rootNodeIndex uint64
   237  		encodedTrie   []byte
   238  	}{
   239  		{"empty trie", emptyTrie, emptyTrieRootNodeIndex, encodedEmptyTrie},
   240  		{"trie", notEmptyTrie, notEmptyTrieRootNodeIndex, encodedTrie},
   241  	}
   242  
   243  	for _, tc := range testCases {
   244  		t.Run("decode "+tc.name, func(t *testing.T) {
   245  			scratchBuffers := [][]byte{
   246  				nil,
   247  				make([]byte, 0),
   248  				make([]byte, 16),
   249  				make([]byte, 1024),
   250  			}
   251  
   252  			for _, scratch := range scratchBuffers {
   253  				reader := bytes.NewReader(tc.encodedTrie)
   254  				tr, err := flattener.ReadTrieFromCheckpointV4(reader, scratch, func(nodeIndex uint64) (*node.Node, uint64, uint64, error) {
   255  					switch nodeIndex {
   256  					case emptyTrieRootNodeIndex, notEmptyTrieRootNodeIndex:
   257  						return tc.tr.RootNode(), tc.tr.AllocatedRegCount(), tc.tr.AllocatedRegSize(), nil
   258  					default:
   259  						return nil, 0, 0, fmt.Errorf("unexpected root node index %d ", nodeIndex)
   260  					}
   261  				})
   262  
   263  				require.NoError(t, err)
   264  				assert.Equal(t, tc.tr.RootNode(), tr.RootNode())
   265  				assert.Equal(t, tc.tr.AllocatedRegCount(), tr.AllocatedRegCount())
   266  				assert.Equal(t, tc.tr.AllocatedRegSize(), tr.AllocatedRegSize())
   267  				assert.Equal(t, 0, reader.Len())
   268  			}
   269  		})
   270  
   271  		t.Run("decode "+tc.name+" node not found error", func(t *testing.T) {
   272  			nodeNotFoundError := errors.New("failed to find node by index")
   273  			scratch := make([]byte, 1024)
   274  
   275  			reader := bytes.NewReader(tc.encodedTrie)
   276  			tr, err := flattener.ReadTrieFromCheckpointV4(reader, scratch, func(nodeIndex uint64) (*node.Node, uint64, uint64, error) {
   277  				return nil, 0, 0, nodeNotFoundError
   278  			})
   279  			require.Nil(t, tr)
   280  			require.ErrorIs(t, err, nodeNotFoundError)
   281  		})
   282  	}
   283  }