github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/mpt/node_test.go (about) 1 package mpt 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "github.com/nspcc-dev/neo-go/internal/random" 8 "github.com/nspcc-dev/neo-go/internal/testserdes" 9 "github.com/nspcc-dev/neo-go/pkg/io" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func getTestFuncEncode(ok bool, expected, actual Node) func(t *testing.T) { 15 return func(t *testing.T) { 16 t.Run("IO", func(t *testing.T) { 17 bs, err := testserdes.EncodeBinary(expected) 18 require.NoError(t, err) 19 if hn, ok := actual.(*HashNode); ok { 20 hn.hashValid = true // this field is set during NodeObject decoding 21 } 22 err = testserdes.DecodeBinary(bs, actual) 23 if !ok { 24 require.Error(t, err) 25 return 26 } 27 require.NoError(t, err) 28 require.Equal(t, expected.Type(), actual.Type()) 29 require.Equal(t, expected.Hash(), actual.Hash()) 30 require.Equal(t, 1+expected.Size(), len(expected.Bytes())) 31 }) 32 t.Run("JSON", func(t *testing.T) { 33 bs, err := json.Marshal(expected) 34 require.NoError(t, err) 35 err = json.Unmarshal(bs, actual) 36 if !ok { 37 require.Error(t, err) 38 return 39 } 40 require.NoError(t, err) 41 require.Equal(t, expected.Type(), actual.Type()) 42 require.Equal(t, expected.Hash(), actual.Hash()) 43 }) 44 } 45 } 46 47 func TestNode_Serializable(t *testing.T) { 48 t.Run("Leaf", func(t *testing.T) { 49 t.Run("Good", func(t *testing.T) { 50 l := NewLeafNode(random.Bytes(123)) 51 t.Run("Raw", getTestFuncEncode(true, l, new(LeafNode))) 52 t.Run("WithType", getTestFuncEncode(true, &NodeObject{l}, new(NodeObject))) 53 }) 54 t.Run("BigValue", getTestFuncEncode(false, 55 NewLeafNode(random.Bytes(MaxValueLength+1)), new(LeafNode))) 56 }) 57 58 t.Run("Extension", func(t *testing.T) { 59 t.Run("Good", func(t *testing.T) { 60 e := NewExtensionNode(random.Bytes(42), NewLeafNode(random.Bytes(10))) 61 t.Run("Raw", getTestFuncEncode(true, e, new(ExtensionNode))) 62 t.Run("WithType", getTestFuncEncode(true, &NodeObject{e}, new(NodeObject))) 63 }) 64 t.Run("BigKey", getTestFuncEncode(false, 65 NewExtensionNode(random.Bytes(maxPathLength+1), NewLeafNode(random.Bytes(10))), new(ExtensionNode))) 66 }) 67 68 t.Run("Branch", func(t *testing.T) { 69 b := NewBranchNode() 70 b.Children[0] = NewLeafNode(random.Bytes(10)) 71 b.Children[lastChild] = NewHashNode(random.Uint256()) 72 t.Run("Raw", getTestFuncEncode(true, b, new(BranchNode))) 73 t.Run("WithType", getTestFuncEncode(true, &NodeObject{b}, new(NodeObject))) 74 }) 75 76 t.Run("Hash", func(t *testing.T) { 77 t.Run("Good", func(t *testing.T) { 78 h := NewHashNode(random.Uint256()) 79 t.Run("Raw", getTestFuncEncode(true, h, new(HashNode))) 80 t.Run("WithType", getTestFuncEncode(true, &NodeObject{h}, new(NodeObject))) 81 }) 82 t.Run("InvalidSize", func(t *testing.T) { 83 buf := io.NewBufBinWriter() 84 buf.BinWriter.WriteBytes(make([]byte, 13)) 85 require.Error(t, testserdes.DecodeBinary(buf.Bytes(), &HashNode{BaseNode: BaseNode{hashValid: true}})) 86 }) 87 }) 88 89 t.Run("Invalid", func(t *testing.T) { 90 require.Error(t, testserdes.DecodeBinary([]byte{0xFF}, new(NodeObject))) 91 }) 92 } 93 94 // https://github.com/neo-project/neo/blob/neox-2.x/neo.UnitTests/UT_MPTTrie.cs#L198 95 func TestJSONSharp(t *testing.T) { 96 tr := NewTrie(nil, ModeAll, newTestStore()) 97 require.NoError(t, tr.Put([]byte{0xac, 0x11}, []byte{0xac, 0x11})) 98 require.NoError(t, tr.Put([]byte{0xac, 0x22}, []byte{0xac, 0x22})) 99 require.NoError(t, tr.Put([]byte{0xac}, []byte{0xac})) 100 require.NoError(t, tr.Delete([]byte{0xac, 0x11})) 101 require.NoError(t, tr.Delete([]byte{0xac, 0x22})) 102 103 js, err := tr.root.MarshalJSON() 104 require.NoError(t, err) 105 require.JSONEq(t, `{"key":"0a0c", "next":{"value":"ac"}}`, string(js)) 106 } 107 108 func TestInvalidJSON(t *testing.T) { 109 t.Run("InvalidChildrenCount", func(t *testing.T) { 110 var cs [childrenCount + 1]Node 111 for i := range cs { 112 cs[i] = EmptyNode{} 113 } 114 data, err := json.Marshal(cs) 115 require.NoError(t, err) 116 117 var n NodeObject 118 require.Error(t, json.Unmarshal(data, &n)) 119 }) 120 121 testCases := []struct { 122 name string 123 data []byte 124 }{ 125 {"WrongFieldCount", []byte(`{"key":"0102", "next": {}, "field": {}}`)}, 126 {"InvalidField1", []byte(`{"next":{}}`)}, 127 {"InvalidField2", []byte(`{"key":"0102", "hash":{}}`)}, 128 {"InvalidKey", []byte(`{"key":"xy", "next":{}}`)}, 129 {"InvalidNext", []byte(`{"key":"01", "next":[]}`)}, 130 {"InvalidHash", []byte(`{"hash":"01"}`)}, 131 {"InvalidValue", []byte(`{"value":1}`)}, 132 {"InvalidBranch", []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]`)}, 133 } 134 for _, tc := range testCases { 135 var n NodeObject 136 assert.Errorf(t, json.Unmarshal(tc.data, &n), "no error in "+tc.name) 137 } 138 } 139 140 // C# interoperability test 141 // https://github.com/neo-project/neo/blob/neox-2.x/neo.UnitTests/UT_MPTTrie.cs#L135 142 func TestRootHash(t *testing.T) { 143 b := NewBranchNode() 144 r := NewExtensionNode([]byte{0x0A, 0x0C}, b) 145 146 v1 := NewLeafNode([]byte{0xAB, 0xCD}) 147 l1 := NewExtensionNode([]byte{0x01}, v1) 148 b.Children[0] = l1 149 150 v2 := NewLeafNode([]byte{0x22, 0x22}) 151 l2 := NewExtensionNode([]byte{0x09}, v2) 152 b.Children[9] = l2 153 154 r1 := NewExtensionNode([]byte{0x0A, 0x0C, 0x00, 0x01}, v1) 155 require.Equal(t, "cedd9897dd1559fbd5dfe5cfb223464da6de438271028afb8d647e950cbd18e0", r1.Hash().StringLE()) 156 require.Equal(t, "1037e779c8a0313bd0d99c4151fa70a277c43c53a549b6444079f2e67e8ffb7b", r.Hash().StringLE()) 157 }