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