github.com/MetalBlockchain/subnet-evm@v0.4.9/core/state/snapshot/snapshot_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package snapshot 28 29 import ( 30 "fmt" 31 "math/big" 32 "math/rand" 33 "testing" 34 "time" 35 36 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 37 "github.com/ethereum/go-ethereum/common" 38 "github.com/ethereum/go-ethereum/rlp" 39 ) 40 41 // randomHash generates a random blob of data and returns it as a hash. 42 func randomHash() common.Hash { 43 var hash common.Hash 44 if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { 45 panic(err) 46 } 47 return hash 48 } 49 50 // randomAccount generates a random account and returns it RLP encoded. 51 func randomAccount() []byte { 52 root := randomHash() 53 a := Account{ 54 Balance: big.NewInt(rand.Int63()), 55 Nonce: rand.Uint64(), 56 Root: root[:], 57 CodeHash: emptyCode[:], 58 } 59 data, _ := rlp.EncodeToBytes(a) 60 return data 61 } 62 63 // randomAccountSet generates a set of random accounts with the given strings as 64 // the account address hashes. 65 func randomAccountSet(hashes ...string) map[common.Hash][]byte { 66 accounts := make(map[common.Hash][]byte) 67 for _, hash := range hashes { 68 accounts[common.HexToHash(hash)] = randomAccount() 69 } 70 return accounts 71 } 72 73 // randomStorageSet generates a set of random slots with the given strings as 74 // the slot addresses. 75 func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte { 76 storages := make(map[common.Hash]map[common.Hash][]byte) 77 for index, account := range accounts { 78 storages[common.HexToHash(account)] = make(map[common.Hash][]byte) 79 80 if index < len(hashes) { 81 hashes := hashes[index] 82 for _, hash := range hashes { 83 storages[common.HexToHash(account)][common.HexToHash(hash)] = randomHash().Bytes() 84 } 85 } 86 if index < len(nilStorage) { 87 nils := nilStorage[index] 88 for _, hash := range nils { 89 storages[common.HexToHash(account)][common.HexToHash(hash)] = nil 90 } 91 } 92 } 93 return storages 94 } 95 96 // Tests that if a disk layer becomes stale, no active external references will 97 // be returned with junk data. This version of the test flattens every diff layer 98 // to check internal corner case around the bottom-most memory accumulator. 99 func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) { 100 // Create an empty base layer and a snapshot tree out of it 101 snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01")) 102 // Retrieve a reference to the base and commit a diff on top 103 ref := snaps.Snapshot(common.HexToHash("0xff01")) 104 105 accounts := map[common.Hash][]byte{ 106 common.HexToHash("0xa1"): randomAccount(), 107 } 108 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0xff02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 109 t.Fatalf("failed to create a diff layer: %v", err) 110 } 111 if n := snaps.NumStateLayers(); n != 2 { 112 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 2) 113 } 114 if n := snaps.NumBlockLayers(); n != 2 { 115 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 2) 116 } 117 // Commit the diff layer onto the disk and ensure it's persisted 118 snaps.verified = true // Bypass validation of junk data 119 if err := snaps.Flatten(common.HexToHash("0x02")); err != nil { 120 t.Fatalf("failed to merge diff layer onto disk: %v", err) 121 } 122 // Since the base layer was modified, ensure that data retrieval on the external reference fail 123 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 124 t.Errorf("stale reference returned account: %v (err: %v)", acc, err) 125 } 126 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 127 t.Errorf("stale reference returned storage slot: %v (err: %v)", slot, err) 128 } 129 if n := snaps.NumStateLayers(); n != 1 { 130 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 1) 131 } 132 if n := snaps.NumBlockLayers(); n != 1 { 133 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 1) 134 } 135 } 136 137 // Tests that if a disk layer becomes stale, no active external references will 138 // be returned with junk data. This version of the test retains the bottom diff 139 // layer to check the usual mode of operation where the accumulator is retained. 140 func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) { 141 // Create an empty base layer and a snapshot tree out of it 142 snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01")) 143 // Retrieve a reference to the base and commit two diffs on top 144 ref := snaps.Snapshot(common.HexToHash("0xff01")) 145 146 accounts := map[common.Hash][]byte{ 147 common.HexToHash("0xa1"): randomAccount(), 148 } 149 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0xff02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 150 t.Fatalf("failed to create a diff layer: %v", err) 151 } 152 if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0xff03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { 153 t.Fatalf("failed to create a diff layer: %v", err) 154 } 155 if n := snaps.NumBlockLayers(); n != 3 { 156 t.Errorf("pre-cap block layer count mismatch: have %d, want %d", n, 3) 157 } 158 if n := snaps.NumStateLayers(); n != 3 { 159 t.Errorf("pre-cap state layer count mismatch: have %d, want %d", n, 3) 160 } 161 // Commit the diff layer onto the disk and ensure it's persisted 162 defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit) 163 aggregatorMemoryLimit = 0 164 165 snaps.verified = true // Bypass validation of junk data 166 if err := snaps.Flatten(common.HexToHash("0x02")); err != nil { 167 t.Fatalf("Failed to flatten diff layer onto disk: %v", err) 168 } 169 // Since the base layer was modified, ensure that data retrieval on the external reference fails 170 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 171 t.Errorf("stale reference returned account: %v (err: %v)", acc, err) 172 } 173 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 174 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 175 } 176 if n := snaps.NumBlockLayers(); n != 2 { 177 t.Errorf("pre-cap block layer count mismatch: have %d, want %d", n, 2) 178 } 179 if n := snaps.NumStateLayers(); n != 2 { 180 t.Errorf("pre-cap state layer count mismatch: have %d, want %d", n, 2) 181 } 182 } 183 184 // Tests that if a diff layer becomes stale, no active external references will 185 // be returned with junk data. This version of the test retains the bottom diff 186 // layer to check the usual mode of operation where the accumulator is retained. 187 func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) { 188 // Create an empty base layer and a snapshot tree out of it 189 snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01")) 190 // Commit three diffs on top and retrieve a reference to the bottommost 191 accounts := map[common.Hash][]byte{ 192 common.HexToHash("0xa1"): randomAccount(), 193 } 194 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0xff02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 195 t.Fatalf("failed to create a diff layer: %v", err) 196 } 197 if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0xff03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { 198 t.Fatalf("failed to create a diff layer: %v", err) 199 } 200 if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0xff04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil { 201 t.Fatalf("failed to create a diff layer: %v", err) 202 } 203 if n := snaps.NumStateLayers(); n != 4 { 204 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 4) 205 } 206 if n := snaps.NumBlockLayers(); n != 4 { 207 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 4) 208 } 209 ref := snaps.Snapshot(common.HexToHash("0xff02")) 210 211 snaps.verified = true // Bypass validation of junk data 212 if err := snaps.Flatten(common.HexToHash("0x02")); err != nil { 213 t.Fatal(err) 214 } 215 // Since the accumulator diff layer was modified, ensure that data retrieval on the external reference fails 216 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 217 t.Errorf("stale reference returned account: %v (err: %v)", acc, err) 218 } 219 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 220 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 221 } 222 if n := snaps.NumStateLayers(); n != 3 { 223 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 3) 224 } 225 if n := snaps.NumBlockLayers(); n != 3 { 226 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 3) 227 } 228 } 229 230 // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening. 231 func TestPostFlattenBasicDataAccess(t *testing.T) { 232 // setAccount is a helper to construct a random account entry and assign it to 233 // an account slot in a snapshot 234 setAccount := func(accKey string) map[common.Hash][]byte { 235 return map[common.Hash][]byte{ 236 common.HexToHash(accKey): randomAccount(), 237 } 238 } 239 // Create a starting base layer and a snapshot tree out of it 240 snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01")) 241 // The lowest difflayer 242 snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0xffa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil) 243 snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xffa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil) 244 snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xffb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil) 245 246 snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xffa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil) 247 snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xffb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil) 248 249 // checkExist verifies if an account exists in a snapshot 250 checkExist := func(layer Snapshot, key string) error { 251 if data, _ := layer.Account(common.HexToHash(key)); data == nil { 252 return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key)) 253 } 254 return nil 255 } 256 checkNotExist := func(layer Snapshot, key string) error { 257 if data, err := layer.Account(common.HexToHash(key)); err != nil { 258 return fmt.Errorf("expected %x to not error: %w", key, err) 259 } else if data != nil { 260 return fmt.Errorf("expected %x to be empty, got %v", key, data) 261 } 262 return nil 263 } 264 // shouldErr checks that an account access errors as expected 265 shouldErr := func(layer Snapshot, key string) error { 266 if data, err := layer.Account(common.HexToHash(key)); err == nil { 267 return fmt.Errorf("expected error, got data %v", data) 268 } 269 return nil 270 } 271 // Check basics for both snapshots 0xffa3 and 0xffb3 272 snap := snaps.Snapshot(common.HexToHash("0xffa3")).(*diffLayer) 273 274 // Check that the accounts that should exist are present in the snapshot 275 if err := checkExist(snap, "0xa1"); err != nil { 276 t.Error(err) 277 } 278 if err := checkExist(snap, "0xa2"); err != nil { 279 t.Error(err) 280 } 281 if err := checkExist(snap, "0xa1"); err != nil { 282 t.Error(err) 283 } 284 // Check that the accounts that should only exist in the other branch 285 // of the snapshot tree are not present in the snapshot. 286 if err := checkNotExist(snap, "0xb1"); err != nil { 287 t.Error(err) 288 } 289 if err := checkNotExist(snap, "0xb2"); err != nil { 290 t.Error(err) 291 } 292 293 snap = snaps.Snapshot(common.HexToHash("0xffb3")).(*diffLayer) 294 295 // Check that the accounts that should exist are present in the snapshot 296 if err := checkExist(snap, "0xa1"); err != nil { 297 t.Error(err) 298 } 299 if err := checkExist(snap, "0xb2"); err != nil { 300 t.Error(err) 301 } 302 if err := checkExist(snap, "0xb3"); err != nil { 303 t.Error(err) 304 } 305 // Check that the accounts that should only exist in the other branch 306 // of the snapshot tree are not present in the snapshot. 307 if err := checkNotExist(snap, "0xa2"); err != nil { 308 t.Error(err) 309 } 310 311 // Flatten a non-existent block should fail 312 if err := snaps.Flatten(common.HexToHash("0x1337")); err == nil { 313 t.Errorf("expected error, got none") 314 } 315 316 // Now, merge the a-chain layer by layer 317 snaps.verified = true // Bypass validation of junk data 318 if err := snaps.Flatten(common.HexToHash("0xa1")); err != nil { 319 t.Error(err) 320 } 321 if err := snaps.Flatten(common.HexToHash("0xa2")); err != nil { 322 t.Error(err) 323 } 324 325 // At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is 326 // the parent of b2, b2 should no longer be able to iterate into parent. 327 328 // These should still be accessible since it doesn't require iteration into the 329 // disk layer. However, it's also valid for these diffLayers to be marked as stale. 330 if err := checkExist(snap, "0xb2"); err != nil { 331 t.Error(err) 332 } 333 if err := checkExist(snap, "0xb3"); err != nil { 334 t.Error(err) 335 } 336 // But 0xa1 would need iteration into the modified parent 337 // and 0xa2 and 0xa3 should never be present since they are 338 // on the other branch of the snapshot tree. These should strictly 339 // error since they're not in the diff layers and will result in 340 // traversing into the stale disk layer. 341 if err := shouldErr(snap, "0xa1"); err != nil { 342 t.Error(err) 343 } 344 if err := shouldErr(snap, "0xa2"); err != nil { 345 t.Error(err) 346 } 347 if err := shouldErr(snap, "0xa3"); err != nil { 348 t.Error(err) 349 } 350 351 snap = snaps.Snapshot(common.HexToHash("0xffa3")).(*diffLayer) 352 // Check that the accounts that should exist are present in the snapshot 353 if err := checkExist(snap, "0xa1"); err != nil { 354 t.Error(err) 355 } 356 if err := checkExist(snap, "0xa2"); err != nil { 357 t.Error(err) 358 } 359 if err := checkExist(snap, "0xa3"); err != nil { 360 t.Error(err) 361 } 362 // Check that the accounts that should only exist in the other branch 363 // of the snapshot tree are not present in the snapshot. 364 if err := checkNotExist(snap, "0xb1"); err != nil { 365 t.Error(err) 366 } 367 if err := checkNotExist(snap, "0xb2"); err != nil { 368 t.Error(err) 369 } 370 371 diskLayer := snaps.Snapshot(common.HexToHash("0xffa2")) 372 // Check that the accounts that should exist are present in the snapshot 373 if err := checkExist(diskLayer, "0xa1"); err != nil { 374 t.Error(err) 375 } 376 if err := checkExist(diskLayer, "0xa2"); err != nil { 377 t.Error(err) 378 } 379 // 0xa3 should not be included until the diffLayer built on top of 380 // 0xffa2. 381 if err := checkNotExist(diskLayer, "0xa3"); err != nil { 382 t.Error(err) 383 } 384 // Check that the accounts that should only exist in the other branch 385 // of the snapshot tree are not present in the snapshot. 386 if err := checkNotExist(diskLayer, "0xb1"); err != nil { 387 t.Error(err) 388 } 389 if err := checkNotExist(diskLayer, "0xb2"); err != nil { 390 t.Error(err) 391 } 392 } 393 394 // TestTreeFlattenDoesNotDropPendingLayers tests that Tree.Flatten correctly 395 // retains layers built on top of the given root layer in the presence of multiple 396 // different blocks inserted with an identical state root. 397 // In this example, (B, C) and (D, E) share the identical state root, but were 398 // inserted under different blocks. 399 // A 400 // / \ 401 // B C 402 // | | 403 // D E 404 // 405 // `t.Flatten(C)` should result in: 406 // 407 // B C 408 // | | 409 // D E 410 // With the branch D, E, hanging and relying on Discard to be called to 411 // garbage collect the references. 412 func TestTreeFlattenDoesNotDropPendingLayers(t *testing.T) { 413 var ( 414 baseRoot = common.HexToHash("0xffff01") 415 baseBlockHash = common.HexToHash("0x01") 416 ) 417 snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot) 418 accounts := map[common.Hash][]byte{ 419 common.HexToHash("0xa1"): randomAccount(), 420 } 421 422 // Create N layers on top of base (N+1) total 423 parentAHash := baseBlockHash 424 parentBHash := baseBlockHash 425 totalLayers := 10 426 for i := 2; i <= totalLayers; i++ { 427 diffBlockAHash := common.Hash{0xee, 0xee, byte(i)} 428 diffBlockBHash := common.Hash{0xdd, 0xdd, byte(i)} 429 diffBlockRoot := common.Hash{0xff, 0xff, byte(i)} 430 if err := snaps.Update(diffBlockAHash, diffBlockRoot, parentAHash, nil, accounts, nil); err != nil { 431 t.Fatalf("failed to create a diff layer: %v", err) 432 } 433 if err := snaps.Update(diffBlockBHash, diffBlockRoot, parentBHash, nil, accounts, nil); err != nil { 434 t.Fatalf("failed to create a diff layer: %v", err) 435 } 436 437 parentAHash = diffBlockAHash 438 parentBHash = diffBlockBHash 439 } 440 441 if n := snaps.NumStateLayers(); n != 10 { 442 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 10) 443 } 444 if n := snaps.NumBlockLayers(); n != 19 { 445 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 19) 446 } 447 448 snaps.verified = true // Bypass validation of junk data 449 if err := snaps.Flatten(common.Hash{0xee, 0xee, byte(2)}); err != nil { 450 t.Fatalf("failed to flatten diff layer from chain A: %v", err) 451 } 452 453 if n := snaps.NumStateLayers(); n != 9 { 454 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 9) 455 } 456 if n := snaps.NumBlockLayers(); n != 18 { 457 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 18) 458 } 459 460 // Attempt to flatten from chain B should error 461 if err := snaps.Flatten(common.Hash{0xdd, 0xdd, byte(2)}); err == nil { 462 t.Errorf("expected flattening abandoned chain to error") 463 } 464 465 for i := 2; i <= 10; i++ { 466 if err := snaps.Discard(common.Hash{0xdd, 0xdd, byte(i)}); err != nil { 467 t.Errorf("attempt to discard snapshot from abandoned chain failed: %v", err) 468 } 469 } 470 471 if n := snaps.NumStateLayers(); n != 9 { 472 t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 9) 473 } 474 if n := snaps.NumBlockLayers(); n != 9 { 475 t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 18) 476 } 477 } 478 479 func TestStaleOriginLayer(t *testing.T) { 480 var ( 481 baseRoot = common.HexToHash("0xffff01") 482 baseBlockHash = common.HexToHash("0x01") 483 diffRootA = common.HexToHash("0xff02") 484 diffBlockHashA = common.HexToHash("0x02") 485 diffRootB = common.HexToHash("0xff03") 486 diffBlockHashB = common.HexToHash("0x03") 487 diffRootC = common.HexToHash("0xff04") 488 diffBlockHashC = common.HexToHash("0x04") 489 ) 490 snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot) 491 addrA := randomHash() 492 accountsA := map[common.Hash][]byte{ 493 addrA: randomAccount(), 494 } 495 addrB := randomHash() 496 accountsB := map[common.Hash][]byte{ 497 addrB: randomAccount(), 498 } 499 addrC := randomHash() 500 accountsC := map[common.Hash][]byte{ 501 addrC: randomAccount(), 502 } 503 504 // Create diff layer A containing account 0xa1 505 if err := snaps.Update(diffBlockHashA, diffRootA, baseBlockHash, nil, accountsA, nil); err != nil { 506 t.Errorf("failed to create diff layer A: %v", err) 507 } 508 // Flatten account 0xa1 to disk 509 snaps.verified = true // Bypass validation of junk data 510 if err := snaps.Flatten(diffBlockHashA); err != nil { 511 t.Errorf("failed to flatten diff block A: %v", err) 512 } 513 // Create diff layer B containing account 0xa2 514 // The bloom filter should contain only 0xa2. 515 if err := snaps.Update(diffBlockHashB, diffRootB, diffBlockHashA, nil, accountsB, nil); err != nil { 516 t.Errorf("failed to create diff layer B: %v", err) 517 } 518 // Create diff layer C containing account 0xa3 519 // The bloom filter should contain 0xa2 and 0xa3 520 if err := snaps.Update(diffBlockHashC, diffRootC, diffBlockHashB, nil, accountsC, nil); err != nil { 521 t.Errorf("failed to create diff layer C: %v", err) 522 } 523 524 if err := snaps.Flatten(diffBlockHashB); err != nil { 525 t.Errorf("failed to flattten diff block A: %v", err) 526 } 527 snap := snaps.Snapshot(diffRootC) 528 if _, err := snap.Account(addrA); err != nil { 529 t.Errorf("expected account to exist: %v", err) 530 } 531 } 532 533 func TestRebloomOnFlatten(t *testing.T) { 534 // Create diff layers, including a level with two children, then flatten 535 // the layers and assert that the bloom filters are being updated correctly. 536 // 537 // Each layer will add an account which will be in the blooms for every 538 // following difflayer. No accesses would need to touch disk. Layers C and D 539 // should have all addrs in their blooms except for each others. 540 // 541 // After flattening A into the root, the remaining blooms should no longer 542 // have addrA but should retain all the others. 543 // 544 // After flattening B into A, blooms C and D should contain only their own 545 // addrs. 546 // 547 // Initial Root (diskLayer) 548 // | 549 // A <- First diffLayer. Adds addrA 550 // | 551 // B <- Second diffLayer. Adds addrB 552 // / \ 553 // Adds addrC -> C D <- Adds addrD 554 555 var ( 556 baseRoot = common.HexToHash("0xff01") 557 baseBlockHash = common.HexToHash("0x01") 558 diffRootA = common.HexToHash("0xff02") 559 diffBlockHashA = common.HexToHash("0x02") 560 diffRootB = common.HexToHash("0xff03") 561 diffBlockHashB = common.HexToHash("0x03") 562 diffRootC = common.HexToHash("0xff04") 563 diffBlockHashC = common.HexToHash("0x04") 564 diffRootD = common.HexToHash("0xff05") 565 diffBlockHashD = common.HexToHash("0x05") 566 ) 567 568 snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot) 569 addrA := randomHash() 570 accountsA := map[common.Hash][]byte{ 571 addrA: randomAccount(), 572 } 573 addrB := randomHash() 574 accountsB := map[common.Hash][]byte{ 575 addrB: randomAccount(), 576 } 577 addrC := randomHash() 578 accountsC := map[common.Hash][]byte{ 579 addrC: randomAccount(), 580 } 581 addrD := randomHash() 582 accountsD := map[common.Hash][]byte{ 583 addrD: randomAccount(), 584 } 585 586 // Build the tree 587 if err := snaps.Update(diffBlockHashA, diffRootA, baseBlockHash, nil, accountsA, nil); err != nil { 588 t.Errorf("failed to create diff layer A: %v", err) 589 } 590 if err := snaps.Update(diffBlockHashB, diffRootB, diffBlockHashA, nil, accountsB, nil); err != nil { 591 t.Errorf("failed to create diff layer B: %v", err) 592 } 593 if err := snaps.Update(diffBlockHashC, diffRootC, diffBlockHashB, nil, accountsC, nil); err != nil { 594 t.Errorf("failed to create diff layer C: %v", err) 595 } 596 if err := snaps.Update(diffBlockHashD, diffRootD, diffBlockHashB, nil, accountsD, nil); err != nil { 597 t.Errorf("failed to create diff layer D: %v", err) 598 } 599 600 assertBlooms := func(snap Snapshot, hitsA, hitsB, hitsC, hitsD bool) { 601 dl, ok := snap.(*diffLayer) 602 if !ok { 603 t.Fatal("snapshot should be a diffLayer") 604 } 605 606 if hitsA != dl.diffed.Contains(accountBloomHasher(addrA)) { 607 t.Errorf("expected bloom filter to return %t but got %t", hitsA, !hitsA) 608 } 609 610 if hitsB != dl.diffed.Contains(accountBloomHasher(addrB)) { 611 t.Errorf("expected bloom filter to return %t but got %t", hitsB, !hitsB) 612 } 613 614 if hitsC != dl.diffed.Contains(accountBloomHasher(addrC)) { 615 t.Errorf("expected bloom filter to return %t but got %t", hitsC, !hitsC) 616 } 617 618 if hitsD != dl.diffed.Contains(accountBloomHasher(addrD)) { 619 t.Errorf("expected bloom filter to return %t but got %t", hitsD, !hitsD) 620 } 621 } 622 623 // First check that each layer's bloom has all current and ancestor addrs, 624 // but no sibling-only addrs. 625 assertBlooms(snaps.Snapshot(diffRootB), true, true, false, false) 626 assertBlooms(snaps.Snapshot(diffRootC), true, true, true, false) 627 assertBlooms(snaps.Snapshot(diffRootD), true, true, false, true) 628 629 // Flatten diffLayer A, making it a disk layer and trigger a rebloom on B, C 630 // and D. If we didn't rebloom, or didn't rebloom recursively, then blooms C 631 // and D would still think addrA was in the diff layers 632 snaps.verified = true // Bypass validation of junk data 633 if err := snaps.Flatten(diffBlockHashA); err != nil { 634 t.Errorf("failed to flattten diff block A: %v", err) 635 } 636 637 // Check that no blooms still have addrA, but they have all the others 638 assertBlooms(snaps.Snapshot(diffRootB), false, true, false, false) 639 assertBlooms(snaps.Snapshot(diffRootC), false, true, true, false) 640 assertBlooms(snaps.Snapshot(diffRootD), false, true, false, true) 641 642 // Flatten diffLayer B, making it a disk layer and trigger a rebloom on C 643 // and D. If we didn't rebloom, or didn't rebloom recursively, then blooms C 644 // and D would still think addrB was in the diff layers 645 if err := snaps.Flatten(diffBlockHashB); err != nil { 646 t.Errorf("failed to flattten diff block A: %v", err) 647 } 648 649 // Blooms C and D should now have only their own addrs 650 assertBlooms(snaps.Snapshot(diffRootC), false, false, true, false) 651 assertBlooms(snaps.Snapshot(diffRootD), false, false, false, true) 652 } 653 654 // TestReadStateDuringFlattening tests the scenario that, during the 655 // bottom diff layers are merging which tags these as stale, the read 656 // happens via a pre-created top snapshot layer which tries to access 657 // the state in these stale layers. Ensure this read can retrieve the 658 // right state back(block until the flattening is finished) instead of 659 // an unexpected error(snapshot layer is stale). 660 func TestReadStateDuringFlattening(t *testing.T) { 661 // setAccount is a helper to construct a random account entry and assign it to 662 // an account slot in a snapshot 663 setAccount := func(accKey string) map[common.Hash][]byte { 664 return map[common.Hash][]byte{ 665 common.HexToHash(accKey): randomAccount(), 666 } 667 } 668 669 var ( 670 baseRoot = common.HexToHash("0xff01") 671 baseBlockHash = common.HexToHash("0x01") 672 diffRootA = common.HexToHash("0xff02") 673 diffBlockHashA = common.HexToHash("0x02") 674 diffRootB = common.HexToHash("0xff03") 675 diffBlockHashB = common.HexToHash("0x03") 676 diffRootC = common.HexToHash("0xff04") 677 diffBlockHashC = common.HexToHash("0x04") 678 ) 679 680 snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot) 681 682 // 4 layers in total, 3 diff layers and 1 disk layers 683 snaps.Update(diffBlockHashA, diffRootA, baseBlockHash, nil, setAccount("0xa1"), nil) 684 snaps.Update(diffBlockHashB, diffRootB, diffBlockHashA, nil, setAccount("0xa2"), nil) 685 snaps.Update(diffBlockHashC, diffRootC, diffBlockHashB, nil, setAccount("0xa3"), nil) 686 687 // Obtain the topmost snapshot handler for state accessing 688 snap := snaps.Snapshot(diffRootC) 689 690 // Register the testing hook to access the state after flattening 691 var result = make(chan *Account) 692 snaps.onFlatten = func() { 693 // Spin up a thread to read the account from the pre-created 694 // snapshot handler. It's expected to be blocked. 695 go func() { 696 account, _ := snap.Account(common.HexToHash("0xa1")) 697 result <- account 698 }() 699 select { 700 case res := <-result: 701 t.Fatalf("Unexpected return %v", res) 702 case <-time.NewTimer(time.Millisecond * 300).C: 703 } 704 } 705 // Flatten the first diff block, which will mark the bottom-most layer as stale. 706 snaps.Flatten(diffBlockHashA) 707 select { 708 case account := <-result: 709 if account == nil { 710 t.Fatal("Failed to retrieve account") 711 } 712 case <-time.NewTimer(time.Millisecond * 300).C: 713 t.Fatal("Unexpected blocker") 714 } 715 }