github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/history_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package merkledb 5 6 import ( 7 "context" 8 "math/rand" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/require" 13 14 "github.com/MetalBlockchain/metalgo/database/memdb" 15 "github.com/MetalBlockchain/metalgo/ids" 16 "github.com/MetalBlockchain/metalgo/utils/maybe" 17 ) 18 19 func Test_History_Simple(t *testing.T) { 20 require := require.New(t) 21 22 db, err := newDB( 23 context.Background(), 24 memdb.New(), 25 newDefaultConfig(), 26 ) 27 require.NoError(err) 28 batch := db.NewBatch() 29 require.NoError(batch.Put([]byte("key"), []byte("value"))) 30 require.NoError(batch.Write()) 31 32 val, err := db.Get([]byte("key")) 33 require.NoError(err) 34 require.Equal([]byte("value"), val) 35 36 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 37 require.NoError(err) 38 require.NotNil(origProof) 39 origRootID := db.rootID 40 require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 41 42 batch = db.NewBatch() 43 require.NoError(batch.Put([]byte("key"), []byte("value0"))) 44 require.NoError(batch.Write()) 45 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 46 require.NoError(err) 47 require.NotNil(newProof) 48 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 49 50 batch = db.NewBatch() 51 require.NoError(batch.Put([]byte("key1"), []byte("value1"))) 52 require.NoError(batch.Put([]byte("key8"), []byte("value8"))) 53 require.NoError(batch.Write()) 54 newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 55 require.NoError(err) 56 require.NotNil(newProof) 57 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 58 59 batch = db.NewBatch() 60 require.NoError(batch.Put([]byte("k"), []byte("v"))) 61 require.NoError(batch.Write()) 62 newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 63 require.NoError(err) 64 require.NotNil(newProof) 65 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 66 67 batch = db.NewBatch() 68 require.NoError(batch.Delete([]byte("k"))) 69 require.NoError(batch.Delete([]byte("ke"))) 70 require.NoError(batch.Delete([]byte("key"))) 71 require.NoError(batch.Delete([]byte("key1"))) 72 require.NoError(batch.Put([]byte("key2"), []byte("value2"))) 73 require.NoError(batch.Delete([]byte("key3"))) 74 require.NoError(batch.Delete([]byte("key4"))) 75 require.NoError(batch.Delete([]byte("key5"))) 76 require.NoError(batch.Delete([]byte("key8"))) 77 require.NoError(batch.Write()) 78 newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 79 require.NoError(err) 80 require.NotNil(newProof) 81 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 82 } 83 84 func Test_History_Large(t *testing.T) { 85 require := require.New(t) 86 87 numIters := 250 88 89 for i := 1; i < 5; i++ { 90 config := newDefaultConfig() 91 // History must be large enough to get the change proof 92 // after this loop. 93 config.HistoryLength = uint(numIters) 94 db, err := New( 95 context.Background(), 96 memdb.New(), 97 config, 98 ) 99 require.NoError(err) 100 roots := []ids.ID{} 101 102 now := time.Now().UnixNano() 103 t.Logf("seed for iter %d: %d", i, now) 104 r := rand.New(rand.NewSource(now)) // #nosec G404 105 // make sure they stay in sync 106 for x := 0; x < numIters; x++ { 107 batch := db.NewBatch() 108 addkey := make([]byte, r.Intn(50)) 109 _, err := r.Read(addkey) 110 require.NoError(err) 111 val := make([]byte, r.Intn(50)) 112 _, err = r.Read(val) 113 require.NoError(err) 114 115 require.NoError(batch.Put(addkey, val)) 116 117 addNilkey := make([]byte, r.Intn(50)) 118 _, err = r.Read(addNilkey) 119 require.NoError(err) 120 require.NoError(batch.Put(addNilkey, nil)) 121 122 deleteKeyStart := make([]byte, r.Intn(50)) 123 _, err = r.Read(deleteKeyStart) 124 require.NoError(err) 125 126 it := db.NewIteratorWithStart(deleteKeyStart) 127 if it.Next() { 128 require.NoError(batch.Delete(it.Key())) 129 } 130 require.NoError(it.Error()) 131 it.Release() 132 133 require.NoError(batch.Write()) 134 root, err := db.GetMerkleRoot(context.Background()) 135 require.NoError(err) 136 roots = append(roots, root) 137 } 138 139 for i := 0; i < numIters; i += numIters / 10 { 140 proof, err := db.GetRangeProofAtRoot(context.Background(), roots[i], maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 10) 141 require.NoError(err) 142 require.NotNil(proof) 143 144 require.NoError(proof.Verify(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), roots[i], BranchFactorToTokenSize[config.BranchFactor], config.Hasher)) 145 } 146 } 147 } 148 149 func Test_History_Bad_GetValueChanges_Input(t *testing.T) { 150 require := require.New(t) 151 152 config := newDefaultConfig() 153 config.HistoryLength = 5 154 155 db, err := newDB( 156 context.Background(), 157 memdb.New(), 158 config, 159 ) 160 require.NoError(err) 161 162 // Do 5 puts (i.e. the history length) 163 batch := db.NewBatch() 164 require.NoError(batch.Put([]byte("key"), []byte("value"))) 165 require.NoError(batch.Write()) 166 167 root1 := db.getMerkleRoot() 168 169 batch = db.NewBatch() 170 require.NoError(batch.Put([]byte("key"), []byte("value0"))) 171 require.NoError(batch.Write()) 172 173 root2 := db.getMerkleRoot() 174 175 batch = db.NewBatch() 176 require.NoError(batch.Put([]byte("key1"), []byte("value0"))) 177 require.NoError(batch.Write()) 178 179 batch = db.NewBatch() 180 require.NoError(batch.Put([]byte("key1"), []byte("value1"))) 181 require.NoError(batch.Write()) 182 183 batch = db.NewBatch() 184 require.NoError(batch.Put([]byte("key2"), []byte("value3"))) 185 require.NoError(batch.Write()) 186 187 root3 := db.getMerkleRoot() 188 189 // ensure these start as valid calls 190 _, err = db.history.getValueChanges(root1, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1) 191 require.NoError(err) 192 _, err = db.history.getValueChanges(root2, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1) 193 require.NoError(err) 194 195 _, err = db.history.getValueChanges(root2, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), -1) 196 require.ErrorIs(err, ErrInvalidMaxLength) 197 198 _, err = db.history.getValueChanges(root3, root2, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1) 199 require.ErrorIs(err, ErrInsufficientHistory) 200 201 // Cause root1 to be removed from the history 202 batch = db.NewBatch() 203 require.NoError(batch.Put([]byte("key2"), []byte("value4"))) 204 require.NoError(batch.Write()) 205 206 _, err = db.history.getValueChanges(root1, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1) 207 require.ErrorIs(err, ErrInsufficientHistory) 208 209 // same start/end roots should yield an empty changelist 210 changes, err := db.history.getValueChanges(root3, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 10) 211 require.NoError(err) 212 require.Empty(changes.values) 213 } 214 215 func Test_History_Trigger_History_Queue_Looping(t *testing.T) { 216 require := require.New(t) 217 218 config := newDefaultConfig() 219 config.HistoryLength = 2 220 221 db, err := newDB( 222 context.Background(), 223 memdb.New(), 224 config, 225 ) 226 require.NoError(err) 227 228 // Do 2 puts (i.e. the history length) 229 batch := db.NewBatch() 230 require.NoError(batch.Put([]byte("key"), []byte("value"))) 231 require.NoError(batch.Write()) 232 origRootID := db.getMerkleRoot() 233 234 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 235 require.NoError(err) 236 require.NotNil(origProof) 237 require.NoError(origProof.Verify( 238 context.Background(), 239 maybe.Some([]byte("k")), 240 maybe.Some([]byte("key3")), 241 origRootID, 242 db.tokenSize, 243 db.hasher, 244 )) 245 246 // write a new value into the db, now there should be 2 roots in the history 247 batch = db.NewBatch() 248 require.NoError(batch.Put([]byte("key"), []byte("value0"))) 249 require.NoError(batch.Write()) 250 251 // ensure that previous root is still present and generates a valid proof 252 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 253 require.NoError(err) 254 require.NotNil(newProof) 255 require.NoError(newProof.Verify( 256 context.Background(), 257 maybe.Some([]byte("k")), 258 maybe.Some([]byte("key3")), 259 origRootID, 260 db.tokenSize, 261 db.hasher, 262 )) 263 264 // trigger a new root to be added to the history, which should cause rollover since there can only be 2 265 batch = db.NewBatch() 266 require.NoError(batch.Put([]byte("key1"), []byte("value1"))) 267 require.NoError(batch.Write()) 268 269 // proof from first root shouldn't be generatable since it should have been removed from the history 270 _, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 271 require.ErrorIs(err, ErrInsufficientHistory) 272 } 273 274 func Test_History_Values_Lookup_Over_Queue_Break(t *testing.T) { 275 require := require.New(t) 276 277 config := newDefaultConfig() 278 config.HistoryLength = 4 279 db, err := newDB( 280 context.Background(), 281 memdb.New(), 282 config, 283 ) 284 require.NoError(err) 285 286 // Do 4 puts (i.e. the history length) 287 batch := db.NewBatch() 288 require.NoError(batch.Put([]byte("key"), []byte("value"))) 289 require.NoError(batch.Write()) 290 291 // write a new value into the db 292 batch = db.NewBatch() 293 require.NoError(batch.Put([]byte("key"), []byte("value0"))) 294 require.NoError(batch.Write()) 295 296 startRoot := db.getMerkleRoot() 297 298 // write a new value into the db 299 batch = db.NewBatch() 300 require.NoError(batch.Put([]byte("key1"), []byte("value0"))) 301 require.NoError(batch.Write()) 302 303 // write a new value into the db that overwrites key1 304 batch = db.NewBatch() 305 require.NoError(batch.Put([]byte("key1"), []byte("value1"))) 306 require.NoError(batch.Write()) 307 308 // trigger a new root to be added to the history, which should cause rollover since there can only be 3 309 batch = db.NewBatch() 310 require.NoError(batch.Put([]byte("key2"), []byte("value3"))) 311 require.NoError(batch.Write()) 312 313 endRoot := db.getMerkleRoot() 314 315 // changes should still be collectable even though the history has had to loop due to hitting max size 316 changes, err := db.history.getValueChanges(startRoot, endRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 10) 317 require.NoError(err) 318 require.Contains(changes.values, ToKey([]byte("key1"))) 319 require.Equal([]byte("value1"), changes.values[ToKey([]byte("key1"))].after.Value()) 320 require.Contains(changes.values, ToKey([]byte("key2"))) 321 require.Equal([]byte("value3"), changes.values[ToKey([]byte("key2"))].after.Value()) 322 } 323 324 func Test_History_RepeatedRoot(t *testing.T) { 325 require := require.New(t) 326 327 db, err := newDB( 328 context.Background(), 329 memdb.New(), 330 newDefaultConfig(), 331 ) 332 require.NoError(err) 333 batch := db.NewBatch() 334 require.NoError(batch.Put([]byte("key1"), []byte("value1"))) 335 require.NoError(batch.Put([]byte("key2"), []byte("value2"))) 336 require.NoError(batch.Put([]byte("key3"), []byte("value3"))) 337 require.NoError(batch.Write()) 338 339 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 340 require.NoError(err) 341 require.NotNil(origProof) 342 origRootID := db.rootID 343 require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 344 345 batch = db.NewBatch() 346 require.NoError(batch.Put([]byte("key1"), []byte("other"))) 347 require.NoError(batch.Put([]byte("key2"), []byte("other"))) 348 require.NoError(batch.Put([]byte("key3"), []byte("other"))) 349 require.NoError(batch.Write()) 350 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 351 require.NoError(err) 352 require.NotNil(newProof) 353 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 354 355 // revert state to be the same as in orig proof 356 batch = db.NewBatch() 357 require.NoError(batch.Put([]byte("key1"), []byte("value1"))) 358 require.NoError(batch.Put([]byte("key2"), []byte("value2"))) 359 require.NoError(batch.Put([]byte("key3"), []byte("value3"))) 360 require.NoError(batch.Write()) 361 362 newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 363 require.NoError(err) 364 require.NotNil(newProof) 365 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 366 } 367 368 func Test_History_ExcessDeletes(t *testing.T) { 369 require := require.New(t) 370 371 db, err := newDB( 372 context.Background(), 373 memdb.New(), 374 newDefaultConfig(), 375 ) 376 require.NoError(err) 377 batch := db.NewBatch() 378 require.NoError(batch.Put([]byte("key"), []byte("value"))) 379 require.NoError(batch.Write()) 380 381 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 382 require.NoError(err) 383 require.NotNil(origProof) 384 origRootID := db.rootID 385 require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 386 387 batch = db.NewBatch() 388 require.NoError(batch.Delete([]byte("key1"))) 389 require.NoError(batch.Delete([]byte("key2"))) 390 require.NoError(batch.Delete([]byte("key3"))) 391 require.NoError(batch.Delete([]byte("key4"))) 392 require.NoError(batch.Delete([]byte("key5"))) 393 require.NoError(batch.Write()) 394 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 395 require.NoError(err) 396 require.NotNil(newProof) 397 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 398 } 399 400 func Test_History_DontIncludeAllNodes(t *testing.T) { 401 require := require.New(t) 402 403 db, err := newDB( 404 context.Background(), 405 memdb.New(), 406 newDefaultConfig(), 407 ) 408 require.NoError(err) 409 batch := db.NewBatch() 410 require.NoError(batch.Put([]byte("key"), []byte("value"))) 411 require.NoError(batch.Write()) 412 413 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 414 require.NoError(err) 415 require.NotNil(origProof) 416 origRootID := db.rootID 417 require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 418 419 batch = db.NewBatch() 420 require.NoError(batch.Put([]byte("z"), []byte("z"))) 421 require.NoError(batch.Write()) 422 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 423 require.NoError(err) 424 require.NotNil(newProof) 425 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 426 } 427 428 func Test_History_Branching2Nodes(t *testing.T) { 429 require := require.New(t) 430 431 db, err := newDB( 432 context.Background(), 433 memdb.New(), 434 newDefaultConfig(), 435 ) 436 require.NoError(err) 437 batch := db.NewBatch() 438 require.NoError(batch.Put([]byte("key"), []byte("value"))) 439 require.NoError(batch.Write()) 440 441 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 442 require.NoError(err) 443 require.NotNil(origProof) 444 origRootID := db.rootID 445 require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 446 447 batch = db.NewBatch() 448 require.NoError(batch.Put([]byte("k"), []byte("v"))) 449 require.NoError(batch.Write()) 450 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 451 require.NoError(err) 452 require.NotNil(newProof) 453 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 454 } 455 456 func Test_History_Branching3Nodes(t *testing.T) { 457 require := require.New(t) 458 459 db, err := newDB( 460 context.Background(), 461 memdb.New(), 462 newDefaultConfig(), 463 ) 464 require.NoError(err) 465 batch := db.NewBatch() 466 require.NoError(batch.Put([]byte("key123"), []byte("value123"))) 467 require.NoError(batch.Write()) 468 469 origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 470 require.NoError(err) 471 require.NotNil(origProof) 472 origRootID := db.rootID 473 require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 474 475 batch = db.NewBatch() 476 require.NoError(batch.Put([]byte("key321"), []byte("value321"))) 477 require.NoError(batch.Write()) 478 newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10) 479 require.NoError(err) 480 require.NotNil(newProof) 481 require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher)) 482 } 483 484 func Test_History_MaxLength(t *testing.T) { 485 require := require.New(t) 486 487 config := newDefaultConfig() 488 config.HistoryLength = 2 489 db, err := newDB( 490 context.Background(), 491 memdb.New(), 492 config, 493 ) 494 require.NoError(err) 495 496 batch := db.NewBatch() 497 require.NoError(batch.Put([]byte("key"), []byte("value"))) 498 require.NoError(batch.Write()) 499 500 oldRoot, err := db.GetMerkleRoot(context.Background()) 501 require.NoError(err) 502 503 batch = db.NewBatch() 504 require.NoError(batch.Put([]byte("k"), []byte("v"))) 505 require.NoError(batch.Write()) 506 507 require.Contains(db.history.lastChanges, oldRoot) 508 509 batch = db.NewBatch() 510 require.NoError(batch.Put([]byte("k1"), []byte("v2"))) // Overwrites oldest element in history 511 require.NoError(batch.Write()) 512 513 require.NotContains(db.history.lastChanges, oldRoot) 514 } 515 516 func Test_Change_List(t *testing.T) { 517 require := require.New(t) 518 519 db, err := newDB( 520 context.Background(), 521 memdb.New(), 522 newDefaultConfig(), 523 ) 524 require.NoError(err) 525 526 emptyRoot, err := db.GetMerkleRoot(context.Background()) 527 require.NoError(err) 528 529 batch := db.NewBatch() 530 require.NoError(batch.Put([]byte("key20"), []byte("value20"))) 531 require.NoError(batch.Put([]byte("key21"), []byte("value21"))) 532 require.NoError(batch.Put([]byte("key22"), []byte("value22"))) 533 require.NoError(batch.Put([]byte("key23"), []byte("value23"))) 534 require.NoError(batch.Put([]byte("key24"), []byte("value24"))) 535 require.NoError(batch.Write()) 536 startRoot, err := db.GetMerkleRoot(context.Background()) 537 require.NoError(err) 538 539 changes, err := db.history.getValueChanges(emptyRoot, startRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 100) 540 require.NoError(err) 541 require.Len(changes.values, 5) 542 543 batch = db.NewBatch() 544 require.NoError(batch.Put([]byte("key25"), []byte("value25"))) 545 require.NoError(batch.Put([]byte("key26"), []byte("value26"))) 546 require.NoError(batch.Put([]byte("key27"), []byte("value27"))) 547 require.NoError(batch.Put([]byte("key28"), []byte("value28"))) 548 require.NoError(batch.Put([]byte("key29"), []byte("value29"))) 549 require.NoError(batch.Write()) 550 551 endRoot, err := db.GetMerkleRoot(context.Background()) 552 require.NoError(err) 553 554 changes, err = db.history.getValueChanges(startRoot, endRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 100) 555 require.NoError(err) 556 require.Len(changes.values, 5) 557 558 batch = db.NewBatch() 559 require.NoError(batch.Put([]byte("key30"), []byte("value30"))) 560 require.NoError(batch.Put([]byte("key31"), []byte("value31"))) 561 require.NoError(batch.Put([]byte("key32"), []byte("value32"))) 562 require.NoError(batch.Delete([]byte("key21"))) 563 require.NoError(batch.Delete([]byte("key22"))) 564 require.NoError(batch.Write()) 565 566 endRoot, err = db.GetMerkleRoot(context.Background()) 567 require.NoError(err) 568 569 changes, err = db.history.getValueChanges(startRoot, endRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 8) 570 require.NoError(err) 571 require.Len(changes.values, 8) 572 } 573 574 func TestHistoryRecord(t *testing.T) { 575 require := require.New(t) 576 577 maxHistoryLen := 3 578 th := newTrieHistory(maxHistoryLen) 579 580 changes := []*changeSummary{} 581 for i := 0; i < maxHistoryLen; i++ { // Fill the history 582 changes = append(changes, &changeSummary{rootID: ids.GenerateTestID()}) 583 584 th.record(changes[i]) 585 require.Equal(uint64(i+1), th.nextInsertNumber) 586 require.Equal(i+1, th.history.Len()) 587 require.Len(th.lastChanges, i+1) 588 require.Contains(th.lastChanges, changes[i].rootID) 589 changeAndIndex := th.lastChanges[changes[i].rootID] 590 require.Equal(uint64(i), changeAndIndex.insertNumber) 591 got, ok := th.history.Index(int(changeAndIndex.insertNumber)) 592 require.True(ok) 593 require.Equal(changes[i], got.changeSummary) 594 } 595 // history is [changes[0], changes[1], changes[2]] 596 597 // Add a new change 598 change3 := &changeSummary{rootID: ids.GenerateTestID()} 599 th.record(change3) 600 // history is [changes[1], changes[2], change3] 601 require.Equal(uint64(maxHistoryLen+1), th.nextInsertNumber) 602 require.Equal(maxHistoryLen, th.history.Len()) 603 require.Len(th.lastChanges, maxHistoryLen) 604 require.Contains(th.lastChanges, change3.rootID) 605 changeAndIndex := th.lastChanges[change3.rootID] 606 require.Equal(uint64(maxHistoryLen), changeAndIndex.insertNumber) 607 got, ok := th.history.PeekRight() 608 require.True(ok) 609 require.Equal(change3, got.changeSummary) 610 611 // // Make sure the oldest change was evicted 612 require.NotContains(th.lastChanges, changes[0].rootID) 613 oldestChange, ok := th.history.PeekLeft() 614 require.True(ok) 615 require.Equal(uint64(1), oldestChange.insertNumber) 616 617 // Add another change which was the same root ID as changes[2] 618 change4 := &changeSummary{rootID: changes[2].rootID} 619 th.record(change4) 620 // history is [changes[2], change3, change4] 621 622 change5 := &changeSummary{rootID: ids.GenerateTestID()} 623 th.record(change5) 624 // history is [change3, change4, change5] 625 626 // Make sure that even though changes[2] was evicted, we still remember 627 // that the most recent change resulting in that change's root ID. 628 require.Len(th.lastChanges, maxHistoryLen) 629 require.Contains(th.lastChanges, changes[2].rootID) 630 changeAndIndex = th.lastChanges[changes[2].rootID] 631 require.Equal(uint64(maxHistoryLen+1), changeAndIndex.insertNumber) 632 633 // Make sure [t.history] is right. 634 require.Equal(maxHistoryLen, th.history.Len()) 635 got, ok = th.history.PopLeft() 636 require.True(ok) 637 require.Equal(uint64(maxHistoryLen), got.insertNumber) 638 require.Equal(change3.rootID, got.rootID) 639 got, ok = th.history.PopLeft() 640 require.True(ok) 641 require.Equal(uint64(maxHistoryLen+1), got.insertNumber) 642 require.Equal(change4.rootID, got.rootID) 643 got, ok = th.history.PopLeft() 644 require.True(ok) 645 require.Equal(uint64(maxHistoryLen+2), got.insertNumber) 646 require.Equal(change5.rootID, got.rootID) 647 } 648 649 func TestHistoryGetChangesToRoot(t *testing.T) { 650 maxHistoryLen := 3 651 history := newTrieHistory(maxHistoryLen) 652 653 changes := []*changeSummary{} 654 for i := 0; i < maxHistoryLen; i++ { // Fill the history 655 changes = append(changes, &changeSummary{ 656 rootID: ids.GenerateTestID(), 657 rootChange: change[maybe.Maybe[*node]]{ 658 before: maybe.Some(&node{}), 659 }, 660 nodes: map[Key]*change[*node]{ 661 ToKey([]byte{byte(i)}): { 662 before: &node{}, 663 after: &node{}, 664 }, 665 }, 666 values: map[Key]*change[maybe.Maybe[[]byte]]{ 667 ToKey([]byte{byte(i)}): { 668 before: maybe.Some([]byte{byte(i)}), 669 after: maybe.Some([]byte{byte(i + 1)}), 670 }, 671 }, 672 }) 673 history.record(changes[i]) 674 } 675 676 type test struct { 677 name string 678 rootID ids.ID 679 start maybe.Maybe[[]byte] 680 end maybe.Maybe[[]byte] 681 validateFunc func(*require.Assertions, *changeSummary) 682 expectedErr error 683 } 684 685 tests := []test{ 686 { 687 name: "unknown root ID", 688 rootID: ids.GenerateTestID(), 689 expectedErr: ErrInsufficientHistory, 690 }, 691 { 692 name: "most recent change", 693 rootID: changes[maxHistoryLen-1].rootID, 694 validateFunc: func(require *require.Assertions, got *changeSummary) { 695 expected := newChangeSummary(defaultPreallocationSize) 696 require.Equal(expected, got) 697 }, 698 }, 699 { 700 name: "second most recent change", 701 rootID: changes[maxHistoryLen-2].rootID, 702 validateFunc: func(require *require.Assertions, got *changeSummary) { 703 // Ensure this is the reverse of the most recent change 704 require.Len(got.nodes, 1) 705 require.Len(got.values, 1) 706 reversedChanges := changes[maxHistoryLen-1] 707 removedKey := ToKey([]byte{byte(maxHistoryLen - 1)}) 708 require.Equal(reversedChanges.nodes[removedKey].before, got.nodes[removedKey].after) 709 require.Equal(reversedChanges.values[removedKey].before, got.values[removedKey].after) 710 require.Equal(reversedChanges.values[removedKey].after, got.values[removedKey].before) 711 }, 712 }, 713 { 714 name: "third most recent change", 715 rootID: changes[maxHistoryLen-3].rootID, 716 validateFunc: func(require *require.Assertions, got *changeSummary) { 717 require.Len(got.nodes, 2) 718 require.Len(got.values, 2) 719 reversedChanges1 := changes[maxHistoryLen-1] 720 removedKey1 := ToKey([]byte{byte(maxHistoryLen - 1)}) 721 require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after) 722 require.Equal(reversedChanges1.values[removedKey1].before, got.values[removedKey1].after) 723 require.Equal(reversedChanges1.values[removedKey1].after, got.values[removedKey1].before) 724 reversedChanges2 := changes[maxHistoryLen-2] 725 removedKey2 := ToKey([]byte{byte(maxHistoryLen - 2)}) 726 require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after) 727 require.Equal(reversedChanges2.values[removedKey2].before, got.values[removedKey2].after) 728 require.Equal(reversedChanges2.values[removedKey2].after, got.values[removedKey2].before) 729 }, 730 }, 731 { 732 name: "third most recent change with start filter", 733 rootID: changes[maxHistoryLen-3].rootID, 734 start: maybe.Some([]byte{byte(maxHistoryLen - 1)}), // Omit values from second most recent change 735 validateFunc: func(require *require.Assertions, got *changeSummary) { 736 require.Len(got.nodes, 2) 737 require.Len(got.values, 1) 738 reversedChanges1 := changes[maxHistoryLen-1] 739 removedKey1 := ToKey([]byte{byte(maxHistoryLen - 1)}) 740 require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after) 741 require.Equal(reversedChanges1.values[removedKey1].before, got.values[removedKey1].after) 742 require.Equal(reversedChanges1.values[removedKey1].after, got.values[removedKey1].before) 743 reversedChanges2 := changes[maxHistoryLen-2] 744 removedKey2 := ToKey([]byte{byte(maxHistoryLen - 2)}) 745 require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after) 746 }, 747 }, 748 { 749 name: "third most recent change with end filter", 750 rootID: changes[maxHistoryLen-3].rootID, 751 end: maybe.Some([]byte{byte(maxHistoryLen - 2)}), // Omit values from most recent change 752 validateFunc: func(require *require.Assertions, got *changeSummary) { 753 require.Len(got.nodes, 2) 754 require.Len(got.values, 1) 755 reversedChanges1 := changes[maxHistoryLen-1] 756 removedKey1 := ToKey([]byte{byte(maxHistoryLen - 1)}) 757 require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after) 758 reversedChanges2 := changes[maxHistoryLen-2] 759 removedKey2 := ToKey([]byte{byte(maxHistoryLen - 2)}) 760 require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after) 761 require.Equal(reversedChanges2.values[removedKey2].before, got.values[removedKey2].after) 762 require.Equal(reversedChanges2.values[removedKey2].after, got.values[removedKey2].before) 763 }, 764 }, 765 } 766 767 for _, tt := range tests { 768 t.Run(tt.name, func(t *testing.T) { 769 require := require.New(t) 770 771 got, err := history.getChangesToGetToRoot(tt.rootID, tt.start, tt.end) 772 require.ErrorIs(err, tt.expectedErr) 773 if tt.expectedErr != nil { 774 return 775 } 776 tt.validateFunc(require, got) 777 }) 778 } 779 }