github.com/koko1123/flow-go-1@v0.29.6/ledger/complete/mtrie/forest_test.go (about) 1 package mtrie 2 3 import ( 4 "bytes" 5 "math/rand" 6 "sort" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/koko1123/flow-go-1/ledger" 14 prf "github.com/koko1123/flow-go-1/ledger/common/proof" 15 "github.com/koko1123/flow-go-1/ledger/common/testutils" 16 "github.com/koko1123/flow-go-1/ledger/complete/mtrie/trie" 17 "github.com/koko1123/flow-go-1/ledger/partial/ptrie" 18 "github.com/koko1123/flow-go-1/module/metrics" 19 ) 20 21 // TestTrieOperations tests adding removing and retrieving Trie from Forest 22 func TestTrieOperations(t *testing.T) { 23 24 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 25 require.NoError(t, err) 26 27 // Make new Trie (independently of MForest): 28 nt := trie.NewEmptyMTrie() 29 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 30 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 31 32 updatedTrie, _, err := trie.NewTrieWithUpdatedRegisters(nt, []ledger.Path{p1}, []ledger.Payload{*v1}, true) 33 require.NoError(t, err) 34 35 // Add trie 36 err = forest.AddTrie(updatedTrie) 37 require.NoError(t, err) 38 39 // Get trie 40 retnt, err := forest.GetTrie(updatedTrie.RootHash()) 41 require.NoError(t, err) 42 require.Equal(t, retnt.RootHash(), updatedTrie.RootHash()) 43 require.Equal(t, 2, forest.Size()) 44 } 45 46 // TestTrieUpdate updates the empty trie with some values and verifies that the 47 // written values can be retrieved from the updated trie. 48 func TestTrieUpdate(t *testing.T) { 49 50 metricsCollector := &metrics.NoopCollector{} 51 forest, err := NewForest(5, metricsCollector, nil) 52 require.NoError(t, err) 53 rootHash := forest.GetEmptyRootHash() 54 55 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 56 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 57 58 paths := []ledger.Path{p1} 59 payloads := []*ledger.Payload{v1} 60 update := &ledger.TrieUpdate{RootHash: rootHash, Paths: paths, Payloads: payloads} 61 updatedRoot, err := forest.Update(update) 62 require.NoError(t, err) 63 64 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 65 retValues, err := forest.Read(read) 66 require.NoError(t, err) 67 require.Equal(t, retValues[0], payloads[0].Value()) 68 } 69 70 // TestLeftEmptyInsert tests inserting a new value into an empty sub-trie: 71 // 1. we first construct a baseTrie holding a couple of values on the right branch [~] 72 // 2. we update a previously non-existent register on the left branch (X) 73 // 74 // We verify that values for _all_ paths in the updated Trie have correct payloads 75 func TestLeftEmptyInsert(t *testing.T) { 76 ////////////////////// 77 // insert X // 78 // () // 79 // / \ // 80 // (X) [~] // 81 ////////////////////// 82 83 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 84 require.NoError(t, err) 85 86 // path: 1000... 87 p1 := pathByUint8s([]uint8{uint8(129), uint8(1)}) 88 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 89 90 // path: 1100... 91 p2 := pathByUint8s([]uint8{uint8(193), uint8(1)}) 92 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 93 94 paths := []ledger.Path{p1, p2} 95 payloads := []*ledger.Payload{v1, v2} 96 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 97 baseRoot, err := forest.Update(update) 98 require.NoError(t, err) 99 100 baseTrie, err := forest.GetTrie(baseRoot) 101 require.NoError(t, err) 102 require.Equal(t, uint64(2), baseTrie.AllocatedRegCount()) 103 require.Equal(t, uint64(v1.Size()+v2.Size()), baseTrie.AllocatedRegSize()) 104 105 p3 := pathByUint8s([]uint8{uint8(1), uint8(1)}) 106 v3 := payloadBySlices([]byte{'C'}, []byte{'C'}) 107 108 paths = []ledger.Path{p3} 109 payloads = []*ledger.Payload{v3} 110 update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads} 111 updatedRoot, err := forest.Update(update) 112 require.NoError(t, err) 113 114 updatedTrie, err := forest.GetTrie(updatedRoot) 115 require.NoError(t, err) 116 require.Equal(t, uint64(3), updatedTrie.AllocatedRegCount()) 117 require.Equal(t, uint64(v1.Size()+v2.Size()+v3.Size()), updatedTrie.AllocatedRegSize()) 118 paths = []ledger.Path{p1, p2, p3} 119 payloads = []*ledger.Payload{v1, v2, v3} 120 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 121 retValues, err := forest.Read(read) 122 require.NoError(t, err) 123 for i := range paths { 124 require.Equal(t, retValues[i], payloads[i].Value()) 125 } 126 } 127 128 // TestRightEmptyInsert tests inserting a new value into an empty sub-trie: 129 // 1. we first construct a baseTrie holding a couple of values on the left branch [~] 130 // 2. we update a previously non-existent register on the right branch (X) 131 // 132 // We verify that values for _all_ paths in the updated Trie have correct payloads 133 func TestRightEmptyInsert(t *testing.T) { 134 /////////////////////// 135 // insert X // 136 // () // 137 // / \ // 138 // [~] (X) // 139 /////////////////////// 140 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 141 require.NoError(t, err) 142 143 // path: 0000... 144 p1 := pathByUint8s([]uint8{uint8(1), uint8(1)}) 145 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 146 147 // path: 0100... 148 p2 := pathByUint8s([]uint8{uint8(64), uint8(1)}) 149 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 150 151 paths := []ledger.Path{p1, p2} 152 payloads := []*ledger.Payload{v1, v2} 153 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 154 baseRoot, err := forest.Update(update) 155 require.NoError(t, err) 156 157 baseTrie, err := forest.GetTrie(baseRoot) 158 require.NoError(t, err) 159 require.Equal(t, uint64(2), baseTrie.AllocatedRegCount()) 160 require.Equal(t, uint64(v1.Size()+v2.Size()), baseTrie.AllocatedRegSize()) 161 162 // path: 1000... 163 p3 := pathByUint8s([]uint8{uint8(129), uint8(1)}) 164 v3 := payloadBySlices([]byte{'C'}, []byte{'C'}) 165 166 paths = []ledger.Path{p3} 167 payloads = []*ledger.Payload{v3} 168 update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads} 169 updatedRoot, err := forest.Update(update) 170 require.NoError(t, err) 171 172 updatedTrie, err := forest.GetTrie(updatedRoot) 173 require.NoError(t, err) 174 require.Equal(t, uint64(3), updatedTrie.AllocatedRegCount()) 175 require.Equal(t, uint64(v1.Size()+v2.Size()+v3.Size()), updatedTrie.AllocatedRegSize()) 176 177 paths = []ledger.Path{p1, p2, p3} 178 payloads = []*ledger.Payload{v1, v2, v3} 179 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 180 retValues, err := forest.Read(read) 181 require.NoError(t, err) 182 for i := range paths { 183 require.Equal(t, retValues[i], payloads[i].Value()) 184 } 185 } 186 187 // TestExpansionInsert tests inserting a new value into a populated sub-trie, where a 188 // leaf (holding a single value) would be replaced by an expanded sub-trie holding multiple value 189 // 1. we first construct a baseTrie holding a couple of values on the right branch [~] 190 // 2. we update a previously non-existent register on the right branch turning [~] to [~'] 191 // 192 // We verify that values for _all_ paths in the updated Trie have correct payloads 193 func TestExpansionInsert(t *testing.T) { 194 //////////////////////// 195 // modify [~] -> [~'] // 196 // () // 197 // / \ // 198 // [~] // 199 //////////////////////// 200 201 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 202 require.NoError(t, err) 203 204 // path: 100000... 205 p1 := pathByUint8s([]uint8{uint8(129), uint8(1)}) 206 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 207 208 paths := []ledger.Path{p1} 209 payloads := []*ledger.Payload{v1} 210 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 211 baseRoot, err := forest.Update(update) 212 require.NoError(t, err) 213 214 baseTrie, err := forest.GetTrie(baseRoot) 215 require.NoError(t, err) 216 require.Equal(t, uint64(1), baseTrie.AllocatedRegCount()) 217 require.Equal(t, uint64(v1.Size()), baseTrie.AllocatedRegSize()) 218 219 // path: 1000001... 220 p2 := pathByUint8s([]uint8{uint8(130), uint8(1)}) 221 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 222 223 paths = []ledger.Path{p2} 224 payloads = []*ledger.Payload{v2} 225 update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads} 226 updatedRoot, err := forest.Update(update) 227 require.NoError(t, err) 228 229 updatedTrie, err := forest.GetTrie(updatedRoot) 230 require.NoError(t, err) 231 require.Equal(t, uint64(2), updatedTrie.AllocatedRegCount()) 232 require.Equal(t, uint64(v1.Size()+v2.Size()), updatedTrie.AllocatedRegSize()) 233 234 paths = []ledger.Path{p1, p2} 235 payloads = []*ledger.Payload{v1, v2} 236 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 237 retValues, err := forest.Read(read) 238 require.NoError(t, err) 239 for i := range paths { 240 require.Equal(t, retValues[i], payloads[i].Value()) 241 } 242 } 243 244 // TestFullHouseInsert tests inserting a new value into a populated sub-trie, where a 245 // leaf's value is overridden _and_ further values are added which all fall into a subtree that 246 // replaces the leaf: 247 // 1. we first construct a baseTrie holding a couple of values on the right branch [~] 248 // 2. we update a previously non-existent register on the right branch turning [~] to [~'] 249 // 250 // We verify that values for _all_ paths in the updated Trie have correct payloads 251 func TestFullHouseInsert(t *testing.T) { 252 /////////////////////// 253 // insert ~1<X<~2 // 254 // () // 255 // / \ // 256 // [~1] [~2] // 257 /////////////////////// 258 259 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 260 require.NoError(t, err) 261 262 // paths p0 forms [~1]; p1 and p2 form [~2] 263 // path: 0100... 264 p0 := pathByUint8s([]uint8{uint8(64), uint8(1)}) 265 v0 := payloadBySlices([]byte{'0'}, []byte{'0'}) 266 // path: 1000... 267 p1 := pathByUint8s([]uint8{uint8(129), uint8(1)}) 268 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 269 270 // path: 1100... 271 p2 := pathByUint8s([]uint8{uint8(193), uint8(1)}) 272 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 273 274 paths := []ledger.Path{p0, p1, p2} 275 payloads := []*ledger.Payload{v0, v1, v2} 276 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 277 baseRoot, err := forest.Update(update) 278 require.NoError(t, err) 279 280 baseTrie, err := forest.GetTrie(baseRoot) 281 require.NoError(t, err) 282 require.Equal(t, uint64(3), baseTrie.AllocatedRegCount()) 283 require.Equal(t, uint64(v0.Size()+v1.Size()+v2.Size()), baseTrie.AllocatedRegSize()) 284 285 // we update value for path p1 and in addition add p3 that has the same prefix `10` as p1 286 v1 = payloadBySlices([]byte{'X'}, []byte{'X'}) 287 288 // path: 1010... 289 p3 := pathByUint8s([]uint8{uint8(160), uint8(1)}) 290 v3 := payloadBySlices([]byte{'C'}, []byte{'C'}) 291 292 paths = []ledger.Path{p1, p3} 293 payloads = []*ledger.Payload{v1, v3} 294 update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads} 295 updatedRoot, err := forest.Update(update) 296 require.NoError(t, err) 297 298 updatedTrie, err := forest.GetTrie(updatedRoot) 299 require.NoError(t, err) 300 require.Equal(t, uint64(4), updatedTrie.AllocatedRegCount()) 301 require.Equal(t, uint64(v0.Size()+v1.Size()+v2.Size()+v3.Size()), updatedTrie.AllocatedRegSize()) 302 303 paths = []ledger.Path{p1, p2, p3} 304 payloads = []*ledger.Payload{v1, v2, v3} 305 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 306 retValues, err := forest.Read(read) 307 require.NoError(t, err) 308 for i := range paths { 309 require.Equal(t, retValues[i], payloads[i].Value()) 310 } 311 } 312 313 // TestLeafInsert inserts two keys, which only differ in their last bit. 314 // I.e. the trie needs to be expanded to its hull depth 315 // We verify that values for _all_ paths in the updated Trie have correct payloads 316 func TestLeafInsert(t *testing.T) { 317 /////////////////////// 318 // insert 1, 2 // 319 // () // 320 // / \ // 321 // () ... // 322 // / \ // 323 // () () // 324 /////////////////////// 325 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 326 require.NoError(t, err) 327 328 // path: 000...0000000100000000 329 p1 := testutils.PathByUint16LeftPadded(256) 330 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 331 332 // path: 000...0000000100000001 333 p2 := testutils.PathByUint16LeftPadded(257) 334 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 335 336 paths := []ledger.Path{p1, p2} 337 payloads := []*ledger.Payload{v1, v2} 338 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 339 updatedRoot, err := forest.Update(update) 340 require.NoError(t, err) 341 342 updatedTrie, err := forest.GetTrie(updatedRoot) 343 require.NoError(t, err) 344 require.Equal(t, uint64(2), updatedTrie.AllocatedRegCount()) 345 require.Equal(t, uint64(v1.Size()+v2.Size()), updatedTrie.AllocatedRegSize()) 346 347 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 348 retValues, err := forest.Read(read) 349 require.NoError(t, err) 350 for i := range paths { 351 require.Equal(t, retValues[i], payloads[i].Value()) 352 } 353 } 354 355 // TestOverrideValue overrides an existing value in the trie (without any expansion) 356 // We verify that values for _all_ paths in the updated Trie have correct payloads 357 func TestOverrideValue(t *testing.T) { 358 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 359 require.NoError(t, err) 360 361 // path: 1000... 362 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 363 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 364 365 // path: 0111... 366 p2 := pathByUint8s([]uint8{uint8(116), uint8(129)}) 367 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 368 369 paths := []ledger.Path{p1, p2} 370 payloads := []*ledger.Payload{v1, v2} 371 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 372 baseRoot, err := forest.Update(update) 373 require.NoError(t, err) 374 375 // path: 1000... 376 p3 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 377 v3 := payloadBySlices([]byte{'C'}, []byte{'C'}) 378 379 paths = []ledger.Path{p3} 380 payloads = []*ledger.Payload{v3} 381 update = &ledger.TrieUpdate{RootHash: baseRoot, Paths: paths, Payloads: payloads} 382 updatedRoot, err := forest.Update(update) 383 require.NoError(t, err) 384 385 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 386 retValues, err := forest.Read(read) 387 require.NoError(t, err) 388 require.Equal(t, retValues[0], payloads[0].Value()) 389 390 } 391 392 // TestDuplicateOverride tests behaviour when the updates contain two different payloads for the 393 // same path. I.e. we update with (p0, v0) and (p0, v1) 394 // We expect that the _last_ written value is persisted in the Trie 395 func TestDuplicateOverride(t *testing.T) { 396 397 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 398 require.NoError(t, err) 399 400 // path: 1000... 401 p0 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 402 v0 := payloadBySlices([]byte{'A'}, []byte{'A'}) 403 paths := []ledger.Path{p0} 404 payloads := []*ledger.Payload{v0} 405 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 406 baseRoot, err := forest.Update(update) 407 require.NoError(t, err) 408 409 v1 := payloadBySlices([]byte{'B'}, []byte{'B'}) 410 v2 := payloadBySlices([]byte{'C'}, []byte{'C'}) 411 paths = []ledger.Path{p0, p0} 412 payloads = []*ledger.Payload{v1, v2} 413 update = &ledger.TrieUpdate{RootHash: baseRoot, Paths: paths, Payloads: payloads} 414 updatedRoot, err := forest.Update(update) 415 require.NoError(t, err) 416 417 paths = []ledger.Path{p0} 418 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 419 retValues, err := forest.Read(read) 420 require.NoError(t, err) 421 require.Equal(t, retValues[0], v2.Value()) 422 423 } 424 425 // TestReadSafety check if payload returned from a forest are safe against modification, 426 // ie. copy of the data is returned, instead of a slice 427 func TestReadSafety(t *testing.T) { 428 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 429 require.NoError(t, err) 430 431 // path: 1000... 432 p0 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 433 v0 := payloadBySlices([]byte{'A'}, []byte{'A'}) 434 paths := []ledger.Path{p0} 435 payloads := []*ledger.Payload{v0} 436 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 437 baseRoot, err := forest.Update(update) 438 require.NoError(t, err) 439 440 read := &ledger.TrieRead{RootHash: baseRoot, Paths: paths} 441 data, err := forest.Read(read) 442 require.NoError(t, err) 443 444 require.Len(t, data, 1) 445 require.Equal(t, v0.Value(), data[0]) 446 447 // modify returned slice 448 data[0] = []byte("new value") 449 450 // read again 451 data2, err := forest.Read(read) 452 require.NoError(t, err) 453 require.Len(t, data2, 1) 454 require.Equal(t, v0.Value(), data2[0]) 455 } 456 457 // TestReadOrder tests that payloads from reading a trie are delivered in the order as specified by the paths 458 func TestReadOrder(t *testing.T) { 459 460 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 461 require.NoError(t, err) 462 463 p1 := pathByUint8s([]uint8{uint8(116), uint8(74)}) 464 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 465 466 p2 := pathByUint8s([]uint8{uint8(53), uint8(129)}) 467 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 468 469 paths := []ledger.Path{p1, p2} 470 payloads := []*ledger.Payload{v1, v2} 471 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 472 testRoot, err := forest.Update(update) 473 require.NoError(t, err) 474 475 read := &ledger.TrieRead{RootHash: testRoot, Paths: []ledger.Path{p1, p2}} 476 retValues, err := forest.Read(read) 477 require.NoError(t, err) 478 require.Equal(t, len(read.Paths), len(retValues)) 479 require.Equal(t, retValues[0], payloads[0].Value()) 480 require.Equal(t, retValues[1], payloads[1].Value()) 481 482 read = &ledger.TrieRead{RootHash: testRoot, Paths: []ledger.Path{p2, p1}} 483 retValues, err = forest.Read(read) 484 require.NoError(t, err) 485 require.Equal(t, len(read.Paths), len(retValues)) 486 require.Equal(t, retValues[1], payloads[0].Value()) 487 require.Equal(t, retValues[0], payloads[1].Value()) 488 } 489 490 // TestMixRead tests reading a mixture of set and unset registers. 491 // We expect the default payload (nil) to be returned for unset registers. 492 func TestMixRead(t *testing.T) { 493 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 494 require.NoError(t, err) 495 496 // path: 01111101... 497 p1 := pathByUint8s([]uint8{uint8(125), uint8(23)}) 498 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 499 500 // path: 10110010... 501 p2 := pathByUint8s([]uint8{uint8(178), uint8(152)}) 502 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 503 504 paths := []ledger.Path{p1, p2} 505 payloads := []*ledger.Payload{v1, v2} 506 507 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 508 baseRoot, err := forest.Update(update) 509 require.NoError(t, err) 510 511 // path: 01101110... 512 p3 := pathByUint8s([]uint8{uint8(110), uint8(48)}) 513 v3 := ledger.EmptyPayload() 514 515 // path: 00010111... 516 p4 := pathByUint8s([]uint8{uint8(23), uint8(82)}) 517 v4 := ledger.EmptyPayload() 518 519 readPaths := []ledger.Path{p1, p2, p3, p4} 520 expectedPayloads := []*ledger.Payload{v1, v2, v3, v4} 521 522 read := &ledger.TrieRead{RootHash: baseRoot, Paths: readPaths} 523 retValues, err := forest.Read(read) 524 require.NoError(t, err) 525 for i := range paths { 526 require.Equal(t, retValues[i], expectedPayloads[i].Value()) 527 } 528 } 529 530 // TestReadWithDuplicatedKeys reads a the values for two keys, where both keys have the same value. 531 // We expect that we receive the respective value twice in the return. 532 func TestReadWithDuplicatedKeys(t *testing.T) { 533 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 534 require.NoError(t, err) 535 536 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 537 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 538 p2 := pathByUint8s([]uint8{uint8(116), uint8(129)}) 539 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 540 p3 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 541 542 paths := []ledger.Path{p1, p2} 543 payloads := []*ledger.Payload{v1, v2} 544 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 545 updatedRoot, err := forest.Update(update) 546 require.NoError(t, err) 547 548 paths = []ledger.Path{p1, p2, p3} 549 expectedPayloads := []*ledger.Payload{v1, v2, v1} 550 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 551 retValues, err := forest.Read(read) 552 require.NoError(t, err) 553 require.Equal(t, len(read.Paths), len(retValues)) 554 for i := range paths { 555 require.Equal(t, retValues[i], expectedPayloads[i].Value()) 556 } 557 } 558 559 // TestReadNonExistingPath tests reading an unset path. 560 func TestReadNonExistingPath(t *testing.T) { 561 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 562 require.NoError(t, err) 563 564 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 565 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 566 paths := []ledger.Path{p1} 567 payloads := []*ledger.Payload{v1} 568 569 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 570 updatedRoot, err := forest.Update(update) 571 require.NoError(t, err) 572 573 p2 := pathByUint8s([]uint8{uint8(116), uint8(129)}) 574 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: []ledger.Path{p2}} 575 retValues, err := forest.Read(read) 576 require.NoError(t, err) 577 require.Equal(t, 0, len(retValues[0])) 578 } 579 580 // TestReadSinglePayload tests reading a single payload of set/unset register. 581 func TestReadSinglePayload(t *testing.T) { 582 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 583 require.NoError(t, err) 584 585 // path: 01111101... 586 path1 := pathByUint8s([]uint8{uint8(125), uint8(23)}) 587 payload1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 588 589 // path: 10110010... 590 path2 := pathByUint8s([]uint8{uint8(178), uint8(152)}) 591 payload2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 592 593 paths := []ledger.Path{path1, path2} 594 payloads := []*ledger.Payload{payload1, payload2} 595 596 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 597 baseRoot, err := forest.Update(update) 598 require.NoError(t, err) 599 600 // path: 01101110... 601 path3 := pathByUint8s([]uint8{uint8(110), uint8(48)}) 602 payload3 := ledger.EmptyPayload() 603 604 // path: 00010111... 605 path4 := pathByUint8s([]uint8{uint8(23), uint8(82)}) 606 payload4 := ledger.EmptyPayload() 607 608 expectedPayloads := make(map[ledger.Path]*ledger.Payload) 609 expectedPayloads[path1] = payload1 610 expectedPayloads[path2] = payload2 611 expectedPayloads[path3] = payload3 612 expectedPayloads[path4] = payload4 613 614 // Batch read one payload at a time (less efficient) 615 for path, payload := range expectedPayloads { 616 read := &ledger.TrieRead{RootHash: baseRoot, Paths: []ledger.Path{path}} 617 retValues, err := forest.Read(read) 618 require.NoError(t, err) 619 require.Equal(t, 1, len(retValues)) 620 if payload.IsEmpty() { 621 require.Equal(t, 0, len(retValues[0])) 622 } else { 623 require.Equal(t, payload.Value(), retValues[0]) 624 } 625 } 626 627 // Read single value 628 for path, payload := range expectedPayloads { 629 read := &ledger.TrieReadSingleValue{RootHash: baseRoot, Path: path} 630 retValue, err := forest.ReadSingleValue(read) 631 require.NoError(t, err) 632 if payload.IsEmpty() { 633 require.Equal(t, 0, len(retValue)) 634 } else { 635 require.Equal(t, payload.Value(), retValue) 636 } 637 } 638 } 639 640 // TestForkingUpdates updates a base trie in two different ways. We expect 641 // that for each update, a new trie is added to the forest preserving the 642 // updated values independently of the other update. 643 func TestForkingUpdates(t *testing.T) { 644 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 645 require.NoError(t, err) 646 647 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 648 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 649 p2 := pathByUint8s([]uint8{uint8(116), uint8(129)}) 650 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 651 paths := []ledger.Path{p1, p2} 652 payloads := []*ledger.Payload{v1, v2} 653 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 654 baseRoot, err := forest.Update(update) 655 require.NoError(t, err) 656 657 // update baseTrie -> updatedTrieA 658 v1a := payloadBySlices([]byte{'C'}, []byte{'C'}) 659 p3a := pathByUint8s([]uint8{uint8(116), uint8(22)}) 660 v3a := payloadBySlices([]byte{'D'}, []byte{'D'}) 661 pathsA := []ledger.Path{p1, p3a} 662 payloadsA := []*ledger.Payload{v1a, v3a} 663 updateA := &ledger.TrieUpdate{RootHash: baseRoot, Paths: pathsA, Payloads: payloadsA} 664 updatedRootA, err := forest.Update(updateA) 665 require.NoError(t, err) 666 667 // update baseTrie -> updatedTrieB 668 v1b := payloadBySlices([]byte{'E'}, []byte{'E'}) 669 p3b := pathByUint8s([]uint8{uint8(116), uint8(22)}) 670 v3b := payloadBySlices([]byte{'F'}, []byte{'F'}) 671 pathsB := []ledger.Path{p1, p3b} 672 payloadsB := []*ledger.Payload{v1b, v3b} 673 updateB := &ledger.TrieUpdate{RootHash: baseRoot, Paths: pathsB, Payloads: payloadsB} 674 updatedRootB, err := forest.Update(updateB) 675 require.NoError(t, err) 676 677 // Verify payloads are preserved 678 read := &ledger.TrieRead{RootHash: baseRoot, Paths: paths} 679 retValues, err := forest.Read(read) // reading from original Trie 680 require.NoError(t, err) 681 for i := range paths { 682 require.Equal(t, retValues[i], payloads[i].Value()) 683 } 684 685 readA := &ledger.TrieRead{RootHash: updatedRootA, Paths: pathsA} 686 retValues, err = forest.Read(readA) // reading from updatedTrieA 687 require.NoError(t, err) 688 for i := range paths { 689 require.Equal(t, retValues[i], payloadsA[i].Value()) 690 } 691 692 readB := &ledger.TrieRead{RootHash: updatedRootB, Paths: pathsB} 693 retValues, err = forest.Read(readB) // reading from updatedTrieB 694 require.NoError(t, err) 695 for i := range paths { 696 require.Equal(t, retValues[i], payloadsB[i].Value()) 697 } 698 } 699 700 // TestIdenticalUpdateAppliedTwice updates a base trie in the same way twice. 701 // Hence, the forest should de-duplicate the resulting two version of the identical trie 702 // without an error. 703 func TestIdenticalUpdateAppliedTwice(t *testing.T) { 704 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 705 require.NoError(t, err) 706 707 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 708 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 709 p2 := pathByUint8s([]uint8{uint8(116), uint8(129)}) 710 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 711 paths := []ledger.Path{p1, p2} 712 payloads := []*ledger.Payload{v1, v2} 713 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 714 baseRoot, err := forest.Update(update) 715 require.NoError(t, err) 716 717 p3 := pathByUint8s([]uint8{uint8(116), uint8(22)}) 718 v3 := payloadBySlices([]byte{'D'}, []byte{'D'}) 719 720 update = &ledger.TrieUpdate{RootHash: baseRoot, Paths: []ledger.Path{p3}, Payloads: []*ledger.Payload{v3}} 721 updatedRootA, err := forest.Update(update) 722 require.NoError(t, err) 723 updatedRootB, err := forest.Update(update) 724 require.NoError(t, err) 725 require.Equal(t, updatedRootA, updatedRootB) 726 727 paths = []ledger.Path{p1, p2, p3} 728 payloads = []*ledger.Payload{v1, v2, v3} 729 read := &ledger.TrieRead{RootHash: updatedRootA, Paths: paths} 730 retValuesA, err := forest.Read(read) 731 require.NoError(t, err) 732 for i := range paths { 733 require.Equal(t, retValuesA[i], payloads[i].Value()) 734 } 735 736 read = &ledger.TrieRead{RootHash: updatedRootB, Paths: paths} 737 retValuesB, err := forest.Read(read) 738 require.NoError(t, err) 739 for i := range paths { 740 require.Equal(t, retValuesB[i], payloads[i].Value()) 741 } 742 } 743 744 // TestRandomUpdateReadProofValueSizes repeats a sequence of actions update, read, get value sizes, and proof random paths 745 // this simulates the common pattern of actions on flow 746 func TestRandomUpdateReadProofValueSizes(t *testing.T) { 747 748 minPayloadByteSize := 2 749 maxPayloadByteSize := 10 750 rep := 10 751 maxNumPathsPerStep := 10 752 seed := time.Now().UnixNano() 753 rand.Seed(seed) 754 t.Log(seed) 755 756 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 757 require.NoError(t, err) 758 759 activeRoot := forest.GetEmptyRootHash() 760 require.NoError(t, err) 761 latestPayloadByPath := make(map[ledger.Path]*ledger.Payload) // map store 762 763 for e := 0; e < rep; e++ { 764 paths := testutils.RandomPathsRandLen(maxNumPathsPerStep) 765 payloads := testutils.RandomPayloads(len(paths), minPayloadByteSize, maxPayloadByteSize) 766 767 // update map store with key values 768 // we use this at the end of each step to check all existing keys 769 for i, p := range paths { 770 latestPayloadByPath[p] = payloads[i] 771 } 772 773 // test reading for non-existing keys 774 nonExistingPaths := make([]ledger.Path, 0) 775 otherPaths := testutils.RandomPathsRandLen(maxNumPathsPerStep) 776 for _, p := range otherPaths { 777 if _, ok := latestPayloadByPath[p]; !ok { 778 nonExistingPaths = append(nonExistingPaths, p) 779 } 780 } 781 read := &ledger.TrieRead{RootHash: activeRoot, Paths: nonExistingPaths} 782 retValues, err := forest.Read(read) 783 require.NoError(t, err, "error reading - non existing paths") 784 for _, p := range retValues { 785 require.Equal(t, 0, len(p)) 786 } 787 788 // test value sizes for non-existent keys 789 retValueSizes, err := forest.ValueSizes(read) 790 require.NoError(t, err, "error value sizes - non existent paths") 791 require.Equal(t, len(read.Paths), len(retValueSizes)) 792 for _, size := range retValueSizes { 793 require.Equal(t, 0, size) 794 } 795 796 // test update 797 update := &ledger.TrieUpdate{RootHash: activeRoot, Paths: paths, Payloads: payloads} 798 activeRoot, err = forest.Update(update) 799 require.NoError(t, err, "error updating") 800 801 // test read 802 read = &ledger.TrieRead{RootHash: activeRoot, Paths: paths} 803 retValues, err = forest.Read(read) 804 require.NoError(t, err, "error reading") 805 for i := range payloads { 806 require.Equal(t, retValues[i], payloads[i].Value()) 807 } 808 809 // test value sizes for existing keys 810 retValueSizes, err = forest.ValueSizes(read) 811 require.NoError(t, err, "error value sizes") 812 require.Equal(t, len(read.Paths), len(retValueSizes)) 813 for i := range payloads { 814 require.Equal(t, payloads[i].Value().Size(), retValueSizes[i]) 815 } 816 817 // test proof (mix of existing and non existing keys) 818 proofPaths := make([]ledger.Path, 0) 819 proofPaths = append(proofPaths, paths...) 820 proofPaths = append(proofPaths, nonExistingPaths...) 821 822 // shuffle the order of `proofPaths` to run `Proofs` on non-sorted input paths 823 rand.Shuffle(len(proofPaths), func(i, j int) { 824 proofPaths[i], proofPaths[j] = proofPaths[j], proofPaths[i] 825 }) 826 827 // sort `proofPaths` into another slice 828 sortedPaths := sortedCopy(proofPaths) 829 830 read = &ledger.TrieRead{RootHash: activeRoot, Paths: proofPaths} 831 batchProof, err := forest.Proofs(read) 832 require.NoError(t, err, "error generating proofs") 833 assert.True(t, prf.VerifyTrieBatchProof(batchProof, ledger.State(activeRoot))) 834 835 // check `Proofs` has sorted the input paths. 836 // this check is needed to not weaken the SPoCK secret entropy, 837 // when `Proofs` is used to generate chunk data. 838 assert.Equal(t, sortedPaths, read.Paths) 839 840 // build a partial trie from batch proofs and check the root hash is equal 841 psmt, err := ptrie.NewPSMT(activeRoot, batchProof) 842 require.NoError(t, err, "error building partial trie") 843 assert.Equal(t, psmt.RootHash(), activeRoot) 844 845 // check payloads for all existing paths 846 allPaths := make([]ledger.Path, 0, len(latestPayloadByPath)) 847 allPayloads := make([]*ledger.Payload, 0, len(latestPayloadByPath)) 848 for p, v := range latestPayloadByPath { 849 allPaths = append(allPaths, p) 850 allPayloads = append(allPayloads, v) 851 } 852 853 read = &ledger.TrieRead{RootHash: activeRoot, Paths: allPaths} 854 retValues, err = forest.Read(read) 855 require.NoError(t, err) 856 for i, v := range allPayloads { 857 assert.Equal(t, retValues[i], v.Value()) 858 } 859 860 // check value sizes for all existing paths 861 retValueSizes, err = forest.ValueSizes(read) 862 require.NoError(t, err) 863 require.Equal(t, len(read.Paths), len(retValueSizes)) 864 for i, v := range allPayloads { 865 assert.Equal(t, v.Value().Size(), retValueSizes[i]) 866 } 867 } 868 } 869 870 func sortedCopy(paths []ledger.Path) []ledger.Path { 871 sortedPaths := make([]ledger.Path, len(paths)) 872 copy(sortedPaths, paths) 873 sort.Slice(sortedPaths, func(i, j int) bool { 874 return bytes.Compare(sortedPaths[i][:], sortedPaths[j][:]) < 0 875 }) 876 return sortedPaths 877 } 878 879 // TestProofGenerationInclusion tests that inclusion proofs generated by a Trie pass verification 880 func TestProofGenerationInclusion(t *testing.T) { 881 882 metricsCollector := &metrics.NoopCollector{} 883 forest, err := NewForest(5, metricsCollector, nil) 884 require.NoError(t, err) 885 emptyRoot := forest.GetEmptyRootHash() 886 887 p1 := pathByUint8s([]uint8{uint8(1), uint8(74)}) 888 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 889 p2 := pathByUint8s([]uint8{uint8(2), uint8(74)}) 890 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 891 p3 := pathByUint8s([]uint8{uint8(130), uint8(74)}) 892 v3 := payloadBySlices([]byte{'C'}, []byte{'C'}) 893 p4 := pathByUint8s([]uint8{uint8(131), uint8(74)}) 894 v4 := payloadBySlices([]byte{'D'}, []byte{'D'}) 895 paths := []ledger.Path{p1, p2, p3, p4} 896 payloads := []*ledger.Payload{v1, v2, v3, v4} 897 898 // shuffle the order of `proofPaths` to run `Proofs` on non-sorted input paths 899 rand.Shuffle(len(paths), func(i, j int) { 900 paths[i], paths[j] = paths[j], paths[i] 901 }) 902 903 // sort `proofPaths` into another slice 904 sortedPaths := sortedCopy(paths) 905 906 update := &ledger.TrieUpdate{RootHash: emptyRoot, Paths: paths, Payloads: payloads} 907 updatedRoot, err := forest.Update(update) 908 require.NoError(t, err) 909 read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths} 910 proof, err := forest.Proofs(read) 911 require.NoError(t, err) 912 913 // verify batch proofs. 914 assert.True(t, prf.VerifyTrieBatchProof(proof, ledger.State(updatedRoot))) 915 916 // check `Proofs` has sorted the input paths. 917 // this check is needed to not weaken the SPoCK secret entropy, 918 // when `Proofs` is used to generate chunk data. 919 assert.Equal(t, sortedPaths, read.Paths) 920 } 921 922 func payloadBySlices(keydata []byte, valuedata []byte) *ledger.Payload { 923 key := ledger.Key{KeyParts: []ledger.KeyPart{{Type: 0, Value: keydata}}} 924 value := ledger.Value(valuedata) 925 return ledger.NewPayload(key, value) 926 } 927 928 func pathByUint8s(inputs []uint8) ledger.Path { 929 var b ledger.Path 930 copy(b[:], inputs) 931 return b 932 } 933 934 // TestValueSizesOrder tests returned value sizes are in the order as specified by the paths 935 func TestValueSizesOrder(t *testing.T) { 936 937 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 938 require.NoError(t, err) 939 940 // path: 01111101... 941 p1 := pathByUint8s([]uint8{uint8(125), uint8(23)}) 942 v1 := testutils.RandomPayload(1, 100) 943 944 // path: 10110010... 945 p2 := pathByUint8s([]uint8{uint8(178), uint8(152)}) 946 v2 := testutils.RandomPayload(1, 100) 947 948 paths := []ledger.Path{p1, p2} 949 payloads := []*ledger.Payload{v1, v2} 950 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 951 baseRoot, err := forest.Update(update) 952 require.NoError(t, err) 953 954 // Get value sizes for paths {p1, p2} 955 read := &ledger.TrieRead{RootHash: baseRoot, Paths: []ledger.Path{p1, p2}} 956 retValueSizes, err := forest.ValueSizes(read) 957 require.NoError(t, err) 958 require.Equal(t, len(read.Paths), len(retValueSizes)) 959 require.Equal(t, v1.Value().Size(), retValueSizes[0]) 960 require.Equal(t, v2.Value().Size(), retValueSizes[1]) 961 962 // Get value sizes for paths {p2, p1} 963 read = &ledger.TrieRead{RootHash: baseRoot, Paths: []ledger.Path{p2, p1}} 964 retValueSizes, err = forest.ValueSizes(read) 965 require.NoError(t, err) 966 require.Equal(t, len(read.Paths), len(retValueSizes)) 967 require.Equal(t, v2.Value().Size(), retValueSizes[0]) 968 require.Equal(t, v1.Value().Size(), retValueSizes[1]) 969 } 970 971 // TestMixGetValueSizes tests value sizes of a mix of set and unset registers. 972 // We expect value size 0 to be returned for unset registers. 973 func TestMixGetValueSizes(t *testing.T) { 974 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 975 require.NoError(t, err) 976 977 // path: 01111101... 978 p1 := pathByUint8s([]uint8{uint8(125), uint8(23)}) 979 v1 := testutils.RandomPayload(1, 100) 980 981 // path: 10110010... 982 p2 := pathByUint8s([]uint8{uint8(178), uint8(152)}) 983 v2 := testutils.RandomPayload(1, 100) 984 985 paths := []ledger.Path{p1, p2} 986 payloads := []*ledger.Payload{v1, v2} 987 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 988 baseRoot, err := forest.Update(update) 989 require.NoError(t, err) 990 991 // path: 01101110... 992 p3 := pathByUint8s([]uint8{uint8(110), uint8(48)}) 993 994 // path: 00010111... 995 p4 := pathByUint8s([]uint8{uint8(23), uint8(82)}) 996 997 readPaths := []ledger.Path{p1, p2, p3, p4} 998 expectedValueSizes := []int{v1.Value().Size(), v2.Value().Size(), 0, 0} 999 1000 read := &ledger.TrieRead{RootHash: baseRoot, Paths: readPaths} 1001 retValueSizes, err := forest.ValueSizes(read) 1002 require.NoError(t, err) 1003 require.Equal(t, len(read.Paths), len(retValueSizes)) 1004 for i := range read.Paths { 1005 require.Equal(t, expectedValueSizes[i], retValueSizes[i]) 1006 } 1007 } 1008 1009 // TestValueSizesWithDuplicatedKeys gets value sizes for two keys, 1010 // where both keys have the same value. 1011 // We expect to receive same value sizes twice. 1012 func TestValueSizesWithDuplicatedKeys(t *testing.T) { 1013 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 1014 require.NoError(t, err) 1015 1016 // path: 01111101... 1017 p1 := pathByUint8s([]uint8{uint8(125), uint8(23)}) 1018 v1 := testutils.RandomPayload(1, 100) 1019 1020 // path: 10110010... 1021 p2 := pathByUint8s([]uint8{uint8(178), uint8(152)}) 1022 v2 := testutils.RandomPayload(1, 100) 1023 1024 // same path as p1 1025 p3 := pathByUint8s([]uint8{uint8(125), uint8(23)}) 1026 1027 paths := []ledger.Path{p1, p2} 1028 payloads := []*ledger.Payload{v1, v2} 1029 update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads} 1030 baseRoot, err := forest.Update(update) 1031 require.NoError(t, err) 1032 1033 readPaths := []ledger.Path{p1, p2, p3} 1034 expectedValueSizes := []int{v1.Value().Size(), v2.Value().Size(), v1.Value().Size()} 1035 1036 read := &ledger.TrieRead{RootHash: baseRoot, Paths: readPaths} 1037 retValueSizes, err := forest.ValueSizes(read) 1038 require.NoError(t, err) 1039 require.Equal(t, len(read.Paths), len(retValueSizes)) 1040 for i := range read.Paths { 1041 require.Equal(t, expectedValueSizes[i], retValueSizes[i]) 1042 } 1043 } 1044 1045 func TestPurgeCacheExcept(t *testing.T) { 1046 forest, err := NewForest(5, &metrics.NoopCollector{}, nil) 1047 require.NoError(t, err) 1048 1049 nt := trie.NewEmptyMTrie() 1050 p1 := pathByUint8s([]uint8{uint8(53), uint8(74)}) 1051 v1 := payloadBySlices([]byte{'A'}, []byte{'A'}) 1052 1053 updatedTrie1, _, err := trie.NewTrieWithUpdatedRegisters(nt, []ledger.Path{p1}, []ledger.Payload{*v1}, true) 1054 require.NoError(t, err) 1055 1056 err = forest.AddTrie(updatedTrie1) 1057 require.NoError(t, err) 1058 1059 p2 := pathByUint8s([]uint8{uint8(12), uint8(34)}) 1060 v2 := payloadBySlices([]byte{'B'}, []byte{'B'}) 1061 1062 updatedTrie2, _, err := trie.NewTrieWithUpdatedRegisters(nt, []ledger.Path{p2}, []ledger.Payload{*v2}, true) 1063 require.NoError(t, err) 1064 1065 err = forest.AddTrie(updatedTrie2) 1066 require.NoError(t, err) 1067 require.Equal(t, 3, forest.tries.Count()) 1068 1069 err = forest.PurgeCacheExcept(updatedTrie2.RootHash()) 1070 require.NoError(t, err) 1071 require.Equal(t, 1, forest.tries.Count()) 1072 1073 ret, err := forest.GetTrie(updatedTrie2.RootHash()) 1074 require.NoError(t, err) 1075 require.Equal(t, ret, updatedTrie2) 1076 1077 _, err = forest.GetTrie(updatedTrie1.RootHash()) 1078 require.Error(t, err) 1079 1080 // test purge with non existing trie 1081 err = forest.PurgeCacheExcept(updatedTrie1.RootHash()) 1082 require.Error(t, err) 1083 1084 ret, err = forest.GetTrie(updatedTrie2.RootHash()) 1085 require.NoError(t, err) 1086 require.Equal(t, ret, updatedTrie2) 1087 1088 _, err = forest.GetTrie(updatedTrie1.RootHash()) 1089 require.Error(t, err) 1090 1091 // test purge when only a single target trie exist there 1092 err = forest.PurgeCacheExcept(updatedTrie2.RootHash()) 1093 require.NoError(t, err) 1094 require.Equal(t, 1, forest.tries.Count()) 1095 }