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