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