github.com/ethereum/go-ethereum@v1.16.1/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/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/go-ethereum/ethdb/memorydb" 27 "github.com/ethereum/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, 121 map[common.Hash][]byte{ 122 accDelNoCache: nil, 123 accDelCache: nil, 124 conNukeNoCache: nil, 125 conNukeCache: nil, 126 accModNoCache: reverse(accModNoCache[:]), 127 accModCache: reverse(accModCache[:]), 128 }, map[common.Hash]map[common.Hash][]byte{ 129 conNukeNoCache: {conNukeNoCacheSlot: nil}, 130 conNukeCache: {conNukeCacheSlot: nil}, 131 conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, 132 conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, 133 conDelNoCache: {conDelNoCacheSlot: nil}, 134 conDelCache: {conDelCacheSlot: nil}, 135 }); err != nil { 136 t.Fatalf("failed to update snapshot tree: %v", err) 137 } 138 if err := snaps.Cap(diffRoot, 0); err != nil { 139 t.Fatalf("failed to flatten snapshot tree: %v", err) 140 } 141 // Retrieve all the data through the disk layer and validate it 142 base = snaps.Snapshot(diffRoot) 143 if _, ok := base.(*diskLayer); !ok { 144 t.Fatalf("update not flattened into the disk layer") 145 } 146 147 // assertAccount ensures that an account matches the given blob. 148 assertAccount := func(account common.Hash, data []byte) { 149 t.Helper() 150 blob, err := base.AccountRLP(account) 151 if err != nil { 152 t.Errorf("account access (%x) failed: %v", account, err) 153 } else if !bytes.Equal(blob, data) { 154 t.Errorf("account access (%x) mismatch: have %x, want %x", account, blob, data) 155 } 156 } 157 assertAccount(accNoModNoCache, accNoModNoCache[:]) 158 assertAccount(accNoModCache, accNoModCache[:]) 159 assertAccount(accModNoCache, reverse(accModNoCache[:])) 160 assertAccount(accModCache, reverse(accModCache[:])) 161 assertAccount(accDelNoCache, nil) 162 assertAccount(accDelCache, nil) 163 164 // assertStorage ensures that a storage slot matches the given blob. 165 assertStorage := func(account common.Hash, slot common.Hash, data []byte) { 166 t.Helper() 167 blob, err := base.Storage(account, slot) 168 if err != nil { 169 t.Errorf("storage access (%x:%x) failed: %v", account, slot, err) 170 } else if !bytes.Equal(blob, data) { 171 t.Errorf("storage access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data) 172 } 173 } 174 assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 175 assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 176 assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 177 assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 178 assertStorage(conDelNoCache, conDelNoCacheSlot, nil) 179 assertStorage(conDelCache, conDelCacheSlot, nil) 180 assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 181 assertStorage(conNukeCache, conNukeCacheSlot, nil) 182 183 // Retrieve all the data directly from the database and validate it 184 185 // assertDatabaseAccount ensures that an account from the database matches the given blob. 186 assertDatabaseAccount := func(account common.Hash, data []byte) { 187 t.Helper() 188 if blob := rawdb.ReadAccountSnapshot(db, account); !bytes.Equal(blob, data) { 189 t.Errorf("account database access (%x) mismatch: have %x, want %x", account, blob, data) 190 } 191 } 192 assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:]) 193 assertDatabaseAccount(accNoModCache, accNoModCache[:]) 194 assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:])) 195 assertDatabaseAccount(accModCache, reverse(accModCache[:])) 196 assertDatabaseAccount(accDelNoCache, nil) 197 assertDatabaseAccount(accDelCache, nil) 198 199 // assertDatabaseStorage ensures that a storage slot from the database matches the given blob. 200 assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) { 201 t.Helper() 202 if blob := rawdb.ReadStorageSnapshot(db, account, slot); !bytes.Equal(blob, data) { 203 t.Errorf("storage database access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data) 204 } 205 } 206 assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 207 assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 208 assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 209 assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 210 assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil) 211 assertDatabaseStorage(conDelCache, conDelCacheSlot, nil) 212 assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 213 assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil) 214 } 215 216 // Tests that merging something into a disk layer persists it into the database 217 // and invalidates any previously written and cached values, discarding anything 218 // after the in-progress generation marker. 219 func TestDiskPartialMerge(t *testing.T) { 220 // Iterate the test a few times to ensure we pick various internal orderings 221 // for the data slots as well as the progress marker. 222 for i := 0; i < 1024; i++ { 223 // Create some accounts in the disk layer 224 db := memorydb.New() 225 226 var ( 227 accNoModNoCache = randomHash() 228 accNoModCache = randomHash() 229 accModNoCache = randomHash() 230 accModCache = randomHash() 231 accDelNoCache = randomHash() 232 accDelCache = randomHash() 233 conNoModNoCache = randomHash() 234 conNoModNoCacheSlot = randomHash() 235 conNoModCache = randomHash() 236 conNoModCacheSlot = randomHash() 237 conModNoCache = randomHash() 238 conModNoCacheSlot = randomHash() 239 conModCache = randomHash() 240 conModCacheSlot = randomHash() 241 conDelNoCache = randomHash() 242 conDelNoCacheSlot = randomHash() 243 conDelCache = randomHash() 244 conDelCacheSlot = randomHash() 245 conNukeNoCache = randomHash() 246 conNukeNoCacheSlot = randomHash() 247 conNukeCache = randomHash() 248 conNukeCacheSlot = randomHash() 249 baseRoot = randomHash() 250 diffRoot = randomHash() 251 genMarker = append(randomHash().Bytes(), randomHash().Bytes()...) 252 ) 253 254 // insertAccount injects an account into the database if it's after the 255 // generator marker, drops the op otherwise. This is needed to seed the 256 // database with a valid starting snapshot. 257 insertAccount := func(account common.Hash, data []byte) { 258 if bytes.Compare(account[:], genMarker) <= 0 { 259 rawdb.WriteAccountSnapshot(db, account, data[:]) 260 } 261 } 262 insertAccount(accNoModNoCache, accNoModNoCache[:]) 263 insertAccount(accNoModCache, accNoModCache[:]) 264 insertAccount(accModNoCache, accModNoCache[:]) 265 insertAccount(accModCache, accModCache[:]) 266 insertAccount(accDelNoCache, accDelNoCache[:]) 267 insertAccount(accDelCache, accDelCache[:]) 268 269 // insertStorage injects a storage slot into the database if it's after 270 // the generator marker, drops the op otherwise. This is needed to seed 271 // the database with a valid starting snapshot. 272 insertStorage := func(account common.Hash, slot common.Hash, data []byte) { 273 if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 { 274 rawdb.WriteStorageSnapshot(db, account, slot, data[:]) 275 } 276 } 277 insertAccount(conNoModNoCache, conNoModNoCache[:]) 278 insertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 279 insertAccount(conNoModCache, conNoModCache[:]) 280 insertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 281 insertAccount(conModNoCache, conModNoCache[:]) 282 insertStorage(conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:]) 283 insertAccount(conModCache, conModCache[:]) 284 insertStorage(conModCache, conModCacheSlot, conModCacheSlot[:]) 285 insertAccount(conDelNoCache, conDelNoCache[:]) 286 insertStorage(conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:]) 287 insertAccount(conDelCache, conDelCache[:]) 288 insertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:]) 289 290 insertAccount(conNukeNoCache, conNukeNoCache[:]) 291 insertStorage(conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:]) 292 insertAccount(conNukeCache, conNukeCache[:]) 293 insertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:]) 294 295 rawdb.WriteSnapshotRoot(db, baseRoot) 296 297 // Create a disk layer based on the above using a random progress marker 298 // and cache in some data. 299 snaps := &Tree{ 300 layers: map[common.Hash]snapshot{ 301 baseRoot: &diskLayer{ 302 diskdb: db, 303 cache: fastcache.New(500 * 1024), 304 root: baseRoot, 305 }, 306 }, 307 } 308 snaps.layers[baseRoot].(*diskLayer).genMarker = genMarker 309 base := snaps.Snapshot(baseRoot) 310 311 // assertAccount ensures that an account matches the given blob if it's 312 // already covered by the disk snapshot, and errors out otherwise. 313 assertAccount := func(account common.Hash, data []byte) { 314 t.Helper() 315 blob, err := base.AccountRLP(account) 316 if bytes.Compare(account[:], genMarker) > 0 && err != ErrNotCoveredYet { 317 t.Fatalf("test %d: post-marker (%x) account access (%x) succeeded: %x", i, genMarker, account, blob) 318 } 319 if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) { 320 t.Fatalf("test %d: pre-marker (%x) account access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data) 321 } 322 } 323 assertAccount(accNoModCache, accNoModCache[:]) 324 assertAccount(accModCache, accModCache[:]) 325 assertAccount(accDelCache, accDelCache[:]) 326 327 // assertStorage ensures that a storage slot matches the given blob if 328 // it's already covered by the disk snapshot, and errors out otherwise. 329 assertStorage := func(account common.Hash, slot common.Hash, data []byte) { 330 t.Helper() 331 blob, err := base.Storage(account, slot) 332 if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && err != ErrNotCoveredYet { 333 t.Fatalf("test %d: post-marker (%x) storage access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob) 334 } 335 if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) { 336 t.Fatalf("test %d: pre-marker (%x) storage access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data) 337 } 338 } 339 assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 340 assertStorage(conModCache, conModCacheSlot, conModCacheSlot[:]) 341 assertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:]) 342 assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:]) 343 344 // Modify or delete some accounts, flatten everything onto disk 345 if err := snaps.Update(diffRoot, baseRoot, 346 map[common.Hash][]byte{ 347 accDelNoCache: nil, 348 accDelCache: nil, 349 conNukeNoCache: nil, 350 conNukeCache: nil, 351 accModNoCache: reverse(accModNoCache[:]), 352 accModCache: reverse(accModCache[:]), 353 }, 354 map[common.Hash]map[common.Hash][]byte{ 355 conNukeNoCache: { 356 conNukeNoCacheSlot: nil, 357 }, 358 conNukeCache: { 359 conNukeCacheSlot: nil, 360 }, 361 conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, 362 conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, 363 conDelNoCache: {conDelNoCacheSlot: nil}, 364 conDelCache: {conDelCacheSlot: nil}, 365 }); err != nil { 366 t.Fatalf("test %d: failed to update snapshot tree: %v", i, err) 367 } 368 if err := snaps.Cap(diffRoot, 0); err != nil { 369 t.Fatalf("test %d: failed to flatten snapshot tree: %v", i, err) 370 } 371 // Retrieve all the data through the disk layer and validate it 372 base = snaps.Snapshot(diffRoot) 373 if _, ok := base.(*diskLayer); !ok { 374 t.Fatalf("test %d: update not flattened into the disk layer", i) 375 } 376 assertAccount(accNoModNoCache, accNoModNoCache[:]) 377 assertAccount(accNoModCache, accNoModCache[:]) 378 assertAccount(accModNoCache, reverse(accModNoCache[:])) 379 assertAccount(accModCache, reverse(accModCache[:])) 380 assertAccount(accDelNoCache, nil) 381 assertAccount(accDelCache, nil) 382 383 assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 384 assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 385 assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 386 assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 387 assertStorage(conDelNoCache, conDelNoCacheSlot, nil) 388 assertStorage(conDelCache, conDelCacheSlot, nil) 389 assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 390 assertStorage(conNukeCache, conNukeCacheSlot, nil) 391 392 // Retrieve all the data directly from the database and validate it 393 394 // assertDatabaseAccount ensures that an account inside the database matches 395 // the given blob if it's already covered by the disk snapshot, and does not 396 // exist otherwise. 397 assertDatabaseAccount := func(account common.Hash, data []byte) { 398 t.Helper() 399 blob := rawdb.ReadAccountSnapshot(db, account) 400 if bytes.Compare(account[:], genMarker) > 0 && blob != nil { 401 t.Fatalf("test %d: post-marker (%x) account database access (%x) succeeded: %x", i, genMarker, account, blob) 402 } 403 if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) { 404 t.Fatalf("test %d: pre-marker (%x) account database access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data) 405 } 406 } 407 assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:]) 408 assertDatabaseAccount(accNoModCache, accNoModCache[:]) 409 assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:])) 410 assertDatabaseAccount(accModCache, reverse(accModCache[:])) 411 assertDatabaseAccount(accDelNoCache, nil) 412 assertDatabaseAccount(accDelCache, nil) 413 414 // assertDatabaseStorage ensures that a storage slot inside the database 415 // matches the given blob if it's already covered by the disk snapshot, 416 // and does not exist otherwise. 417 assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) { 418 t.Helper() 419 blob := rawdb.ReadStorageSnapshot(db, account, slot) 420 if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && blob != nil { 421 t.Fatalf("test %d: post-marker (%x) storage database access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob) 422 } 423 if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) { 424 t.Fatalf("test %d: pre-marker (%x) storage database access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data) 425 } 426 } 427 assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:]) 428 assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:]) 429 assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:])) 430 assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:])) 431 assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil) 432 assertDatabaseStorage(conDelCache, conDelCacheSlot, nil) 433 assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil) 434 assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil) 435 } 436 } 437 438 // Tests that when the bottom-most diff layer is merged into the disk 439 // layer whether the corresponding generator is persisted correctly. 440 func TestDiskGeneratorPersistence(t *testing.T) { 441 var ( 442 accOne = randomHash() 443 accTwo = randomHash() 444 accOneSlotOne = randomHash() 445 accOneSlotTwo = randomHash() 446 447 accThree = randomHash() 448 accThreeSlot = randomHash() 449 baseRoot = randomHash() 450 diffRoot = randomHash() 451 diffTwoRoot = randomHash() 452 genMarker = append(randomHash().Bytes(), randomHash().Bytes()...) 453 ) 454 // Testing scenario 1, the disk layer is still under the construction. 455 db := rawdb.NewMemoryDatabase() 456 457 rawdb.WriteAccountSnapshot(db, accOne, accOne[:]) 458 rawdb.WriteStorageSnapshot(db, accOne, accOneSlotOne, accOneSlotOne[:]) 459 rawdb.WriteStorageSnapshot(db, accOne, accOneSlotTwo, accOneSlotTwo[:]) 460 rawdb.WriteSnapshotRoot(db, baseRoot) 461 462 // Create a disk layer based on all above updates 463 snaps := &Tree{ 464 layers: map[common.Hash]snapshot{ 465 baseRoot: &diskLayer{ 466 diskdb: db, 467 cache: fastcache.New(500 * 1024), 468 root: baseRoot, 469 genMarker: genMarker, 470 }, 471 }, 472 } 473 // Modify or delete some accounts, flatten everything onto disk 474 if err := snaps.Update(diffRoot, baseRoot, 475 map[common.Hash][]byte{ 476 accTwo: accTwo[:], 477 }, nil, 478 ); err != nil { 479 t.Fatalf("failed to update snapshot tree: %v", err) 480 } 481 if err := snaps.Cap(diffRoot, 0); err != nil { 482 t.Fatalf("failed to flatten snapshot tree: %v", err) 483 } 484 blob := rawdb.ReadSnapshotGenerator(db) 485 var generator journalGenerator 486 if err := rlp.DecodeBytes(blob, &generator); err != nil { 487 t.Fatalf("Failed to decode snapshot generator %v", err) 488 } 489 if !bytes.Equal(generator.Marker, genMarker) { 490 t.Fatalf("Generator marker is not matched") 491 } 492 // Test scenario 2, the disk layer is fully generated 493 // Modify or delete some accounts, flatten everything onto disk 494 if err := snaps.Update(diffTwoRoot, diffRoot, 495 map[common.Hash][]byte{ 496 accThree: accThree.Bytes(), 497 }, 498 map[common.Hash]map[common.Hash][]byte{ 499 accThree: {accThreeSlot: accThreeSlot.Bytes()}, 500 }, 501 ); err != nil { 502 t.Fatalf("failed to update snapshot tree: %v", err) 503 } 504 diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer) 505 diskLayer.genMarker = nil // Construction finished 506 if err := snaps.Cap(diffTwoRoot, 0); err != nil { 507 t.Fatalf("failed to flatten snapshot tree: %v", err) 508 } 509 blob = rawdb.ReadSnapshotGenerator(db) 510 if err := rlp.DecodeBytes(blob, &generator); err != nil { 511 t.Fatalf("Failed to decode snapshot generator %v", err) 512 } 513 if len(generator.Marker) != 0 { 514 t.Fatalf("Failed to update snapshot generator") 515 } 516 } 517 518 // Tests that merging something into a disk layer persists it into the database 519 // and invalidates any previously written and cached values, discarding anything 520 // after the in-progress generation marker. 521 // 522 // This test case is a tiny specialized case of TestDiskPartialMerge, which tests 523 // some very specific cornercases that random tests won't ever trigger. 524 func TestDiskMidAccountPartialMerge(t *testing.T) { 525 // TODO(@karalabe) ? 526 } 527 528 // TestDiskSeek tests that seek-operations work on the disk layer 529 func TestDiskSeek(t *testing.T) { 530 // Create some accounts in the disk layer 531 db := rawdb.NewMemoryDatabase() 532 defer db.Close() 533 534 // Fill even keys [0,2,4...] 535 for i := 0; i < 0xff; i += 2 { 536 acc := common.Hash{byte(i)} 537 rawdb.WriteAccountSnapshot(db, acc, acc[:]) 538 } 539 // Add an 'higher' key, with incorrect (higher) prefix 540 highKey := []byte{rawdb.SnapshotAccountPrefix[0] + 1} 541 db.Put(highKey, []byte{0xff, 0xff}) 542 543 baseRoot := randomHash() 544 rawdb.WriteSnapshotRoot(db, baseRoot) 545 546 snaps := &Tree{ 547 layers: map[common.Hash]snapshot{ 548 baseRoot: &diskLayer{ 549 diskdb: db, 550 cache: fastcache.New(500 * 1024), 551 root: baseRoot, 552 }, 553 }, 554 } 555 // Test some different seek positions 556 type testcase struct { 557 pos byte 558 expkey byte 559 } 560 var cases = []testcase{ 561 {0xff, 0x55}, // this should exit immediately without checking key 562 {0x01, 0x02}, 563 {0xfe, 0xfe}, 564 {0xfd, 0xfe}, 565 {0x00, 0x00}, 566 } 567 for i, tc := range cases { 568 it, err := snaps.AccountIterator(baseRoot, common.Hash{tc.pos}) 569 if err != nil { 570 t.Fatalf("case %d, error: %v", i, err) 571 } 572 count := 0 573 for it.Next() { 574 k, v, err := it.Hash()[0], it.Account()[0], it.Error() 575 if err != nil { 576 t.Fatalf("test %d, item %d, error: %v", i, count, err) 577 } 578 // First item in iterator should have the expected key 579 if count == 0 && k != tc.expkey { 580 t.Fatalf("test %d, item %d, got %v exp %v", i, count, k, tc.expkey) 581 } 582 count++ 583 if v != k { 584 t.Fatalf("test %d, item %d, value wrong, got %v exp %v", i, count, v, k) 585 } 586 } 587 } 588 }