github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/core/state/snapshot/disklayer_test.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package snapshot 18 19 import ( 20 "bytes" 21 "testing" 22 23 "github.com/VictoriaMetrics/fastcache" 24 "github.com/tirogen/go-ethereum/common" 25 "github.com/tirogen/go-ethereum/core/rawdb" 26 "github.com/tirogen/go-ethereum/ethdb/memorydb" 27 "github.com/tirogen/go-ethereum/rlp" 28 ) 29 30 // reverse reverses the contents of a byte slice. It's used to update random accs 31 // with deterministic changes. 32 func reverse(blob []byte) []byte { 33 res := make([]byte, len(blob)) 34 for i, b := range blob { 35 res[len(blob)-1-i] = b 36 } 37 return res 38 } 39 40 // Tests that merging something into a disk layer persists it into the database 41 // and invalidates any previously written and cached values. 42 func TestDiskMerge(t *testing.T) { 43 // Create some accounts in the disk layer 44 db := memorydb.New() 45 46 var ( 47 accNoModNoCache = common.Hash{0x1} 48 accNoModCache = common.Hash{0x2} 49 accModNoCache = common.Hash{0x3} 50 accModCache = common.Hash{0x4} 51 accDelNoCache = common.Hash{0x5} 52 accDelCache = common.Hash{0x6} 53 conNoModNoCache = common.Hash{0x7} 54 conNoModNoCacheSlot = common.Hash{0x70} 55 conNoModCache = common.Hash{0x8} 56 conNoModCacheSlot = common.Hash{0x80} 57 conModNoCache = common.Hash{0x9} 58 conModNoCacheSlot = common.Hash{0x90} 59 conModCache = common.Hash{0xa} 60 conModCacheSlot = common.Hash{0xa0} 61 conDelNoCache = common.Hash{0xb} 62 conDelNoCacheSlot = common.Hash{0xb0} 63 conDelCache = common.Hash{0xc} 64 conDelCacheSlot = common.Hash{0xc0} 65 conNukeNoCache = common.Hash{0xd} 66 conNukeNoCacheSlot = common.Hash{0xd0} 67 conNukeCache = common.Hash{0xe} 68 conNukeCacheSlot = common.Hash{0xe0} 69 baseRoot = randomHash() 70 diffRoot = randomHash() 71 ) 72 73 rawdb.WriteAccountSnapshot(db, accNoModNoCache, accNoModNoCache[:]) 74 rawdb.WriteAccountSnapshot(db, accNoModCache, accNoModCache[:]) 75 rawdb.WriteAccountSnapshot(db, accModNoCache, accModNoCache[:]) 76 rawdb.WriteAccountSnapshot(db, accModCache, accModCache[:]) 77 rawdb.WriteAccountSnapshot(db, accDelNoCache, accDelNoCache[:]) 78 rawdb.WriteAccountSnapshot(db, accDelCache, accDelCache[:]) 79 80 rawdb.WriteAccountSnapshot(db, conNoModNoCache, conNoModNoCache[:]) 81 rawdb.WriteStorageSnapshot(db, conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 82 rawdb.WriteAccountSnapshot(db, conNoModCache, conNoModCache[:]) 83 rawdb.WriteStorageSnapshot(db, conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 84 rawdb.WriteAccountSnapshot(db, conModNoCache, conModNoCache[:]) 85 rawdb.WriteStorageSnapshot(db, conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:]) 86 rawdb.WriteAccountSnapshot(db, conModCache, conModCache[:]) 87 rawdb.WriteStorageSnapshot(db, conModCache, conModCacheSlot, conModCacheSlot[:]) 88 rawdb.WriteAccountSnapshot(db, conDelNoCache, conDelNoCache[:]) 89 rawdb.WriteStorageSnapshot(db, conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:]) 90 rawdb.WriteAccountSnapshot(db, conDelCache, conDelCache[:]) 91 rawdb.WriteStorageSnapshot(db, conDelCache, conDelCacheSlot, conDelCacheSlot[:]) 92 93 rawdb.WriteAccountSnapshot(db, conNukeNoCache, conNukeNoCache[:]) 94 rawdb.WriteStorageSnapshot(db, conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:]) 95 rawdb.WriteAccountSnapshot(db, conNukeCache, conNukeCache[:]) 96 rawdb.WriteStorageSnapshot(db, conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:]) 97 98 rawdb.WriteSnapshotRoot(db, baseRoot) 99 100 // Create a disk layer based on the above and cache in some data 101 snaps := &Tree{ 102 layers: map[common.Hash]snapshot{ 103 baseRoot: &diskLayer{ 104 diskdb: db, 105 cache: fastcache.New(500 * 1024), 106 root: baseRoot, 107 }, 108 }, 109 } 110 base := snaps.Snapshot(baseRoot) 111 base.AccountRLP(accNoModCache) 112 base.AccountRLP(accModCache) 113 base.AccountRLP(accDelCache) 114 base.Storage(conNoModCache, conNoModCacheSlot) 115 base.Storage(conModCache, conModCacheSlot) 116 base.Storage(conDelCache, conDelCacheSlot) 117 base.Storage(conNukeCache, conNukeCacheSlot) 118 119 // Modify or delete some accounts, flatten everything onto disk 120 if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{ 121 accDelNoCache: {}, 122 accDelCache: {}, 123 conNukeNoCache: {}, 124 conNukeCache: {}, 125 }, map[common.Hash][]byte{ 126 accModNoCache: reverse(accModNoCache[:]), 127 accModCache: reverse(accModCache[:]), 128 }, map[common.Hash]map[common.Hash][]byte{ 129 conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, 130 conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, 131 conDelNoCache: {conDelNoCacheSlot: nil}, 132 conDelCache: {conDelCacheSlot: nil}, 133 }); err != nil { 134 t.Fatalf("failed to update snapshot tree: %v", err) 135 } 136 if err := snaps.Cap(diffRoot, 0); err != nil { 137 t.Fatalf("failed to flatten snapshot tree: %v", err) 138 } 139 // Retrieve all the data through the disk layer and validate it 140 base = snaps.Snapshot(diffRoot) 141 if _, ok := base.(*diskLayer); !ok { 142 t.Fatalf("update not flattend into the disk layer") 143 } 144 145 // assertAccount ensures that an account matches the given blob. 146 assertAccount := func(account common.Hash, data []byte) { 147 t.Helper() 148 blob, err := base.AccountRLP(account) 149 if err != nil { 150 t.Errorf("account access (%x) failed: %v", account, err) 151 } else if !bytes.Equal(blob, data) { 152 t.Errorf("account access (%x) mismatch: have %x, want %x", account, blob, data) 153 } 154 } 155 assertAccount(accNoModNoCache, accNoModNoCache[:]) 156 assertAccount(accNoModCache, accNoModCache[:]) 157 assertAccount(accModNoCache, reverse(accModNoCache[:])) 158 assertAccount(accModCache, reverse(accModCache[:])) 159 assertAccount(accDelNoCache, nil) 160 assertAccount(accDelCache, nil) 161 162 // assertStorage ensures that a storage slot matches the given blob. 163 assertStorage := func(account common.Hash, slot common.Hash, data []byte) { 164 t.Helper() 165 blob, err := base.Storage(account, slot) 166 if err != nil { 167 t.Errorf("storage access (%x:%x) failed: %v", account, slot, err) 168 } else if !bytes.Equal(blob, data) { 169 t.Errorf("storage access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data) 170 } 171 } 172 assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 173 assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 174 assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 175 assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 176 assertStorage(conDelNoCache, conDelNoCacheSlot, nil) 177 assertStorage(conDelCache, conDelCacheSlot, nil) 178 assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 179 assertStorage(conNukeCache, conNukeCacheSlot, nil) 180 181 // Retrieve all the data directly from the database and validate it 182 183 // assertDatabaseAccount ensures that an account from the database matches the given blob. 184 assertDatabaseAccount := func(account common.Hash, data []byte) { 185 t.Helper() 186 if blob := rawdb.ReadAccountSnapshot(db, account); !bytes.Equal(blob, data) { 187 t.Errorf("account database access (%x) mismatch: have %x, want %x", account, blob, data) 188 } 189 } 190 assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:]) 191 assertDatabaseAccount(accNoModCache, accNoModCache[:]) 192 assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:])) 193 assertDatabaseAccount(accModCache, reverse(accModCache[:])) 194 assertDatabaseAccount(accDelNoCache, nil) 195 assertDatabaseAccount(accDelCache, nil) 196 197 // assertDatabaseStorage ensures that a storage slot from the database matches the given blob. 198 assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) { 199 t.Helper() 200 if blob := rawdb.ReadStorageSnapshot(db, account, slot); !bytes.Equal(blob, data) { 201 t.Errorf("storage database access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data) 202 } 203 } 204 assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 205 assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 206 assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 207 assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 208 assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil) 209 assertDatabaseStorage(conDelCache, conDelCacheSlot, nil) 210 assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 211 assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil) 212 } 213 214 // Tests that merging something into a disk layer persists it into the database 215 // and invalidates any previously written and cached values, discarding anything 216 // after the in-progress generation marker. 217 func TestDiskPartialMerge(t *testing.T) { 218 // Iterate the test a few times to ensure we pick various internal orderings 219 // for the data slots as well as the progress marker. 220 for i := 0; i < 1024; i++ { 221 // Create some accounts in the disk layer 222 db := memorydb.New() 223 224 var ( 225 accNoModNoCache = randomHash() 226 accNoModCache = randomHash() 227 accModNoCache = randomHash() 228 accModCache = randomHash() 229 accDelNoCache = randomHash() 230 accDelCache = randomHash() 231 conNoModNoCache = randomHash() 232 conNoModNoCacheSlot = randomHash() 233 conNoModCache = randomHash() 234 conNoModCacheSlot = randomHash() 235 conModNoCache = randomHash() 236 conModNoCacheSlot = randomHash() 237 conModCache = randomHash() 238 conModCacheSlot = randomHash() 239 conDelNoCache = randomHash() 240 conDelNoCacheSlot = randomHash() 241 conDelCache = randomHash() 242 conDelCacheSlot = randomHash() 243 conNukeNoCache = randomHash() 244 conNukeNoCacheSlot = randomHash() 245 conNukeCache = randomHash() 246 conNukeCacheSlot = randomHash() 247 baseRoot = randomHash() 248 diffRoot = randomHash() 249 genMarker = append(randomHash().Bytes(), randomHash().Bytes()...) 250 ) 251 252 // insertAccount injects an account into the database if it's after the 253 // generator marker, drops the op otherwise. This is needed to seed the 254 // database with a valid starting snapshot. 255 insertAccount := func(account common.Hash, data []byte) { 256 if bytes.Compare(account[:], genMarker) <= 0 { 257 rawdb.WriteAccountSnapshot(db, account, data[:]) 258 } 259 } 260 insertAccount(accNoModNoCache, accNoModNoCache[:]) 261 insertAccount(accNoModCache, accNoModCache[:]) 262 insertAccount(accModNoCache, accModNoCache[:]) 263 insertAccount(accModCache, accModCache[:]) 264 insertAccount(accDelNoCache, accDelNoCache[:]) 265 insertAccount(accDelCache, accDelCache[:]) 266 267 // insertStorage injects a storage slot into the database if it's after 268 // the generator marker, drops the op otherwise. This is needed to seed 269 // the database with a valid starting snapshot. 270 insertStorage := func(account common.Hash, slot common.Hash, data []byte) { 271 if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 { 272 rawdb.WriteStorageSnapshot(db, account, slot, data[:]) 273 } 274 } 275 insertAccount(conNoModNoCache, conNoModNoCache[:]) 276 insertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 277 insertAccount(conNoModCache, conNoModCache[:]) 278 insertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 279 insertAccount(conModNoCache, conModNoCache[:]) 280 insertStorage(conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:]) 281 insertAccount(conModCache, conModCache[:]) 282 insertStorage(conModCache, conModCacheSlot, conModCacheSlot[:]) 283 insertAccount(conDelNoCache, conDelNoCache[:]) 284 insertStorage(conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:]) 285 insertAccount(conDelCache, conDelCache[:]) 286 insertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:]) 287 288 insertAccount(conNukeNoCache, conNukeNoCache[:]) 289 insertStorage(conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:]) 290 insertAccount(conNukeCache, conNukeCache[:]) 291 insertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:]) 292 293 rawdb.WriteSnapshotRoot(db, baseRoot) 294 295 // Create a disk layer based on the above using a random progress marker 296 // and cache in some data. 297 snaps := &Tree{ 298 layers: map[common.Hash]snapshot{ 299 baseRoot: &diskLayer{ 300 diskdb: db, 301 cache: fastcache.New(500 * 1024), 302 root: baseRoot, 303 }, 304 }, 305 } 306 snaps.layers[baseRoot].(*diskLayer).genMarker = genMarker 307 base := snaps.Snapshot(baseRoot) 308 309 // assertAccount ensures that an account matches the given blob if it's 310 // already covered by the disk snapshot, and errors out otherwise. 311 assertAccount := func(account common.Hash, data []byte) { 312 t.Helper() 313 blob, err := base.AccountRLP(account) 314 if bytes.Compare(account[:], genMarker) > 0 && err != ErrNotCoveredYet { 315 t.Fatalf("test %d: post-marker (%x) account access (%x) succeeded: %x", i, genMarker, account, blob) 316 } 317 if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) { 318 t.Fatalf("test %d: pre-marker (%x) account access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data) 319 } 320 } 321 assertAccount(accNoModCache, accNoModCache[:]) 322 assertAccount(accModCache, accModCache[:]) 323 assertAccount(accDelCache, accDelCache[:]) 324 325 // assertStorage ensures that a storage slot matches the given blob if 326 // it's already covered by the disk snapshot, and errors out otherwise. 327 assertStorage := func(account common.Hash, slot common.Hash, data []byte) { 328 t.Helper() 329 blob, err := base.Storage(account, slot) 330 if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && err != ErrNotCoveredYet { 331 t.Fatalf("test %d: post-marker (%x) storage access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob) 332 } 333 if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) { 334 t.Fatalf("test %d: pre-marker (%x) storage access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data) 335 } 336 } 337 assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 338 assertStorage(conModCache, conModCacheSlot, conModCacheSlot[:]) 339 assertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:]) 340 assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:]) 341 342 // Modify or delete some accounts, flatten everything onto disk 343 if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{ 344 accDelNoCache: {}, 345 accDelCache: {}, 346 conNukeNoCache: {}, 347 conNukeCache: {}, 348 }, map[common.Hash][]byte{ 349 accModNoCache: reverse(accModNoCache[:]), 350 accModCache: reverse(accModCache[:]), 351 }, map[common.Hash]map[common.Hash][]byte{ 352 conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, 353 conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, 354 conDelNoCache: {conDelNoCacheSlot: nil}, 355 conDelCache: {conDelCacheSlot: nil}, 356 }); err != nil { 357 t.Fatalf("test %d: failed to update snapshot tree: %v", i, err) 358 } 359 if err := snaps.Cap(diffRoot, 0); err != nil { 360 t.Fatalf("test %d: failed to flatten snapshot tree: %v", i, err) 361 } 362 // Retrieve all the data through the disk layer and validate it 363 base = snaps.Snapshot(diffRoot) 364 if _, ok := base.(*diskLayer); !ok { 365 t.Fatalf("test %d: update not flattend into the disk layer", i) 366 } 367 assertAccount(accNoModNoCache, accNoModNoCache[:]) 368 assertAccount(accNoModCache, accNoModCache[:]) 369 assertAccount(accModNoCache, reverse(accModNoCache[:])) 370 assertAccount(accModCache, reverse(accModCache[:])) 371 assertAccount(accDelNoCache, nil) 372 assertAccount(accDelCache, nil) 373 374 assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 375 assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 376 assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 377 assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 378 assertStorage(conDelNoCache, conDelNoCacheSlot, nil) 379 assertStorage(conDelCache, conDelCacheSlot, nil) 380 assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 381 assertStorage(conNukeCache, conNukeCacheSlot, nil) 382 383 // Retrieve all the data directly from the database and validate it 384 385 // assertDatabaseAccount ensures that an account inside the database matches 386 // the given blob if it's already covered by the disk snapshot, and does not 387 // exist otherwise. 388 assertDatabaseAccount := func(account common.Hash, data []byte) { 389 t.Helper() 390 blob := rawdb.ReadAccountSnapshot(db, account) 391 if bytes.Compare(account[:], genMarker) > 0 && blob != nil { 392 t.Fatalf("test %d: post-marker (%x) account database access (%x) succeeded: %x", i, genMarker, account, blob) 393 } 394 if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) { 395 t.Fatalf("test %d: pre-marker (%x) account database access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data) 396 } 397 } 398 assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:]) 399 assertDatabaseAccount(accNoModCache, accNoModCache[:]) 400 assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:])) 401 assertDatabaseAccount(accModCache, reverse(accModCache[:])) 402 assertDatabaseAccount(accDelNoCache, nil) 403 assertDatabaseAccount(accDelCache, nil) 404 405 // assertDatabaseStorage ensures that a storage slot inside the database 406 // matches the given blob if it's already covered by the disk snapshot, 407 // and does not exist otherwise. 408 assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) { 409 t.Helper() 410 blob := rawdb.ReadStorageSnapshot(db, account, slot) 411 if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && blob != nil { 412 t.Fatalf("test %d: post-marker (%x) storage database access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob) 413 } 414 if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) { 415 t.Fatalf("test %d: pre-marker (%x) storage database access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data) 416 } 417 } 418 assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 419 assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 420 assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 421 assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 422 assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil) 423 assertDatabaseStorage(conDelCache, conDelCacheSlot, nil) 424 assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 425 assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil) 426 } 427 } 428 429 // Tests that when the bottom-most diff layer is merged into the disk 430 // layer whether the corresponding generator is persisted correctly. 431 func TestDiskGeneratorPersistence(t *testing.T) { 432 var ( 433 accOne = randomHash() 434 accTwo = randomHash() 435 accOneSlotOne = randomHash() 436 accOneSlotTwo = randomHash() 437 438 accThree = randomHash() 439 accThreeSlot = randomHash() 440 baseRoot = randomHash() 441 diffRoot = randomHash() 442 diffTwoRoot = randomHash() 443 genMarker = append(randomHash().Bytes(), randomHash().Bytes()...) 444 ) 445 // Testing scenario 1, the disk layer is still under the construction. 446 db := rawdb.NewMemoryDatabase() 447 448 rawdb.WriteAccountSnapshot(db, accOne, accOne[:]) 449 rawdb.WriteStorageSnapshot(db, accOne, accOneSlotOne, accOneSlotOne[:]) 450 rawdb.WriteStorageSnapshot(db, accOne, accOneSlotTwo, accOneSlotTwo[:]) 451 rawdb.WriteSnapshotRoot(db, baseRoot) 452 453 // Create a disk layer based on all above updates 454 snaps := &Tree{ 455 layers: map[common.Hash]snapshot{ 456 baseRoot: &diskLayer{ 457 diskdb: db, 458 cache: fastcache.New(500 * 1024), 459 root: baseRoot, 460 genMarker: genMarker, 461 }, 462 }, 463 } 464 // Modify or delete some accounts, flatten everything onto disk 465 if err := snaps.Update(diffRoot, baseRoot, nil, map[common.Hash][]byte{ 466 accTwo: accTwo[:], 467 }, nil); err != nil { 468 t.Fatalf("failed to update snapshot tree: %v", err) 469 } 470 if err := snaps.Cap(diffRoot, 0); err != nil { 471 t.Fatalf("failed to flatten snapshot tree: %v", err) 472 } 473 blob := rawdb.ReadSnapshotGenerator(db) 474 var generator journalGenerator 475 if err := rlp.DecodeBytes(blob, &generator); err != nil { 476 t.Fatalf("Failed to decode snapshot generator %v", err) 477 } 478 if !bytes.Equal(generator.Marker, genMarker) { 479 t.Fatalf("Generator marker is not matched") 480 } 481 // Test scenario 2, the disk layer is fully generated 482 // Modify or delete some accounts, flatten everything onto disk 483 if err := snaps.Update(diffTwoRoot, diffRoot, nil, map[common.Hash][]byte{ 484 accThree: accThree.Bytes(), 485 }, map[common.Hash]map[common.Hash][]byte{ 486 accThree: {accThreeSlot: accThreeSlot.Bytes()}, 487 }); err != nil { 488 t.Fatalf("failed to update snapshot tree: %v", err) 489 } 490 diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer) 491 diskLayer.genMarker = nil // Construction finished 492 if err := snaps.Cap(diffTwoRoot, 0); err != nil { 493 t.Fatalf("failed to flatten snapshot tree: %v", err) 494 } 495 blob = rawdb.ReadSnapshotGenerator(db) 496 if err := rlp.DecodeBytes(blob, &generator); err != nil { 497 t.Fatalf("Failed to decode snapshot generator %v", err) 498 } 499 if len(generator.Marker) != 0 { 500 t.Fatalf("Failed to update snapshot generator") 501 } 502 } 503 504 // Tests that merging something into a disk layer persists it into the database 505 // and invalidates any previously written and cached values, discarding anything 506 // after the in-progress generation marker. 507 // 508 // This test case is a tiny specialized case of TestDiskPartialMerge, which tests 509 // some very specific cornercases that random tests won't ever trigger. 510 func TestDiskMidAccountPartialMerge(t *testing.T) { 511 // TODO(@karalabe) ? 512 } 513 514 // TestDiskSeek tests that seek-operations work on the disk layer 515 func TestDiskSeek(t *testing.T) { 516 // Create some accounts in the disk layer 517 db := rawdb.NewMemoryDatabase() 518 defer db.Close() 519 520 // Fill even keys [0,2,4...] 521 for i := 0; i < 0xff; i += 2 { 522 acc := common.Hash{byte(i)} 523 rawdb.WriteAccountSnapshot(db, acc, acc[:]) 524 } 525 // Add an 'higher' key, with incorrect (higher) prefix 526 highKey := []byte{rawdb.SnapshotAccountPrefix[0] + 1} 527 db.Put(highKey, []byte{0xff, 0xff}) 528 529 baseRoot := randomHash() 530 rawdb.WriteSnapshotRoot(db, baseRoot) 531 532 snaps := &Tree{ 533 layers: map[common.Hash]snapshot{ 534 baseRoot: &diskLayer{ 535 diskdb: db, 536 cache: fastcache.New(500 * 1024), 537 root: baseRoot, 538 }, 539 }, 540 } 541 // Test some different seek positions 542 type testcase struct { 543 pos byte 544 expkey byte 545 } 546 var cases = []testcase{ 547 {0xff, 0x55}, // this should exit immediately without checking key 548 {0x01, 0x02}, 549 {0xfe, 0xfe}, 550 {0xfd, 0xfe}, 551 {0x00, 0x00}, 552 } 553 for i, tc := range cases { 554 it, err := snaps.AccountIterator(baseRoot, common.Hash{tc.pos}) 555 if err != nil { 556 t.Fatalf("case %d, error: %v", i, err) 557 } 558 count := 0 559 for it.Next() { 560 k, v, err := it.Hash()[0], it.Account()[0], it.Error() 561 if err != nil { 562 t.Fatalf("test %d, item %d, error: %v", i, count, err) 563 } 564 // First item in iterator should have the expected key 565 if count == 0 && k != tc.expkey { 566 t.Fatalf("test %d, item %d, got %v exp %v", i, count, k, tc.expkey) 567 } 568 count++ 569 if v != k { 570 t.Fatalf("test %d, item %d, value wrong, got %v exp %v", i, count, v, k) 571 } 572 } 573 } 574 }