github.com/dim4egster/coreth@v0.10.2/core/state/snapshot/snapshot.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 implements a journalled, dynamic state dump. 28 package snapshot 29 30 import ( 31 "bytes" 32 "errors" 33 "fmt" 34 "sync" 35 "sync/atomic" 36 "time" 37 38 "github.com/VictoriaMetrics/fastcache" 39 "github.com/dim4egster/coreth/core/rawdb" 40 "github.com/dim4egster/coreth/ethdb" 41 "github.com/dim4egster/coreth/metrics" 42 "github.com/dim4egster/coreth/trie" 43 "github.com/ethereum/go-ethereum/common" 44 "github.com/ethereum/go-ethereum/log" 45 ) 46 47 const ( 48 // skipGenThreshold is the minimum time that must have elapsed since the 49 // creation of the previous disk layer to start snapshot generation on a new 50 // disk layer. 51 // 52 // If disk layers are being discarded at a frequency greater than this threshold, 53 // starting snapshot generation is not worth it (will be aborted before meaningful 54 // work can be done). 55 skipGenThreshold = 500 * time.Millisecond 56 ) 57 58 var ( 59 snapshotCleanAccountHitMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/hit", nil) 60 snapshotCleanAccountMissMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/miss", nil) 61 snapshotCleanAccountInexMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/inex", nil) 62 snapshotCleanAccountReadMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/read", nil) 63 snapshotCleanAccountWriteMeter = metrics.NewRegisteredMeter("state/snapshot/clean/account/write", nil) 64 65 snapshotCleanStorageHitMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/hit", nil) 66 snapshotCleanStorageMissMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/miss", nil) 67 snapshotCleanStorageInexMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/inex", nil) 68 snapshotCleanStorageReadMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/read", nil) 69 snapshotCleanStorageWriteMeter = metrics.NewRegisteredMeter("state/snapshot/clean/storage/write", nil) 70 71 snapshotDirtyAccountHitMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/hit", nil) 72 snapshotDirtyAccountMissMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/miss", nil) 73 snapshotDirtyAccountInexMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/inex", nil) 74 snapshotDirtyAccountReadMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/read", nil) 75 snapshotDirtyAccountWriteMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/account/write", nil) 76 77 snapshotDirtyStorageHitMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/hit", nil) 78 snapshotDirtyStorageMissMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/miss", nil) 79 snapshotDirtyStorageInexMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/inex", nil) 80 snapshotDirtyStorageReadMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/read", nil) 81 snapshotDirtyStorageWriteMeter = metrics.NewRegisteredMeter("state/snapshot/dirty/storage/write", nil) 82 83 snapshotDirtyAccountHitDepthHist = metrics.NewRegisteredHistogram("state/snapshot/dirty/account/hit/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) 84 snapshotDirtyStorageHitDepthHist = metrics.NewRegisteredHistogram("state/snapshot/dirty/storage/hit/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) 85 86 snapshotFlushAccountItemMeter = metrics.NewRegisteredMeter("state/snapshot/flush/account/item", nil) 87 snapshotFlushAccountSizeMeter = metrics.NewRegisteredMeter("state/snapshot/flush/account/size", nil) 88 snapshotFlushStorageItemMeter = metrics.NewRegisteredMeter("state/snapshot/flush/storage/item", nil) 89 snapshotFlushStorageSizeMeter = metrics.NewRegisteredMeter("state/snapshot/flush/storage/size", nil) 90 91 snapshotBloomIndexTimer = metrics.NewRegisteredResettingTimer("state/snapshot/bloom/index", nil) 92 snapshotBloomErrorGauge = metrics.NewRegisteredGaugeFloat64("state/snapshot/bloom/error", nil) 93 94 snapshotBloomAccountTrueHitMeter = metrics.NewRegisteredMeter("state/snapshot/bloom/account/truehit", nil) 95 snapshotBloomAccountFalseHitMeter = metrics.NewRegisteredMeter("state/snapshot/bloom/account/falsehit", nil) 96 snapshotBloomAccountMissMeter = metrics.NewRegisteredMeter("state/snapshot/bloom/account/miss", nil) 97 98 snapshotBloomStorageTrueHitMeter = metrics.NewRegisteredMeter("state/snapshot/bloom/storage/truehit", nil) 99 snapshotBloomStorageFalseHitMeter = metrics.NewRegisteredMeter("state/snapshot/bloom/storage/falsehit", nil) 100 snapshotBloomStorageMissMeter = metrics.NewRegisteredMeter("state/snapshot/bloom/storage/miss", nil) 101 102 // ErrSnapshotStale is returned from data accessors if the underlying snapshot 103 // layer had been invalidated due to the chain progressing forward far enough 104 // to not maintain the layer's original state. 105 ErrSnapshotStale = errors.New("snapshot stale") 106 107 // ErrStaleParentLayer is returned when Flatten attempts to flatten a diff layer into 108 // a stale parent. 109 ErrStaleParentLayer = errors.New("parent disk layer is stale") 110 111 // ErrNotCoveredYet is returned from data accessors if the underlying snapshot 112 // is being generated currently and the requested data item is not yet in the 113 // range of accounts covered. 114 ErrNotCoveredYet = errors.New("not covered yet") 115 116 // ErrNotConstructed is returned if the callers want to iterate the snapshot 117 // while the generation is not finished yet. 118 ErrNotConstructed = errors.New("snapshot is not constructed") 119 ) 120 121 // Snapshot represents the functionality supported by a snapshot storage layer. 122 type Snapshot interface { 123 // Root returns the root hash for which this snapshot was made. 124 Root() common.Hash 125 126 // Account directly retrieves the account associated with a particular hash in 127 // the snapshot slim data format. 128 Account(hash common.Hash) (*Account, error) 129 130 // AccountRLP directly retrieves the account RLP associated with a particular 131 // hash in the snapshot slim data format. 132 AccountRLP(hash common.Hash) ([]byte, error) 133 134 // Storage directly retrieves the storage data associated with a particular hash, 135 // within a particular account. 136 Storage(accountHash, storageHash common.Hash) ([]byte, error) 137 138 // AccountIterator creates an account iterator over the account trie given by the provided root hash. 139 AccountIterator(seek common.Hash) AccountIterator 140 141 // StorageIterator creates a storage iterator over the storage trie given by the provided root hash. 142 StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) 143 } 144 145 // snapshot is the internal version of the snapshot data layer that supports some 146 // additional methods compared to the public API. 147 type snapshot interface { 148 Snapshot 149 150 BlockHash() common.Hash 151 152 // Parent returns the subsequent layer of a snapshot, or nil if the base was 153 // reached. 154 // 155 // Note, the method is an internal helper to avoid type switching between the 156 // disk and diff layers. There is no locking involved. 157 Parent() snapshot 158 159 // Update creates a new layer on top of the existing snapshot diff tree with 160 // the specified data items. 161 // 162 // Note, the maps are retained by the method to avoid copying everything. 163 Update(blockHash, blockRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer 164 165 // Stale return whether this layer has become stale (was flattened across) or 166 // if it's still live. 167 Stale() bool 168 } 169 170 // Tree is an Ethereum state snapshot tree. It consists of one persistent base 171 // layer backed by a key-value store, on top of which arbitrarily many in-memory 172 // diff layers are topped. The memory diffs can form a tree with branching, but 173 // the disk layer is singleton and common to all. If a reorg goes deeper than the 174 // disk layer, everything needs to be deleted. 175 // 176 // The goal of a state snapshot is twofold: to allow direct access to account and 177 // storage data to avoid expensive multi-level trie lookups; and to allow sorted, 178 // cheap iteration of the account/storage tries for sync aid. 179 type Tree struct { 180 diskdb ethdb.KeyValueStore // Persistent database to store the snapshot 181 triedb *trie.Database // In-memory cache to access the trie through 182 cache int // Megabytes permitted to use for read caches 183 // Collection of all known layers 184 // blockHash -> snapshot 185 blockLayers map[common.Hash]snapshot 186 // stateRoot -> blockHash -> snapshot 187 // Update creates a new block layer with a parent taken from the blockHash -> snapshot map 188 // we can support grabbing a read only Snapshot by getting any one from the state root based map 189 stateLayers map[common.Hash]map[common.Hash]snapshot 190 verified bool // Indicates if snapshot integrity has been verified 191 lock sync.RWMutex 192 193 // Test hooks 194 onFlatten func() // Hook invoked when the bottom most diff layers are flattened 195 } 196 197 // New attempts to load an already existing snapshot from a persistent key-value 198 // store (with a number of memory layers from a journal), ensuring that the head 199 // of the snapshot matches the expected one. 200 // 201 // If the snapshot is missing or the disk layer is broken, the snapshot will be 202 // reconstructed using both the existing data and the state trie. 203 // The repair happens on a background thread. 204 func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, blockHash, root common.Hash, async bool, rebuild bool, verify bool) (*Tree, error) { 205 // Create a new, empty snapshot tree 206 snap := &Tree{ 207 diskdb: diskdb, 208 triedb: triedb, 209 cache: cache, 210 blockLayers: make(map[common.Hash]snapshot), 211 stateLayers: make(map[common.Hash]map[common.Hash]snapshot), 212 verified: !verify, // if verify is false, all verification will be bypassed 213 } 214 215 // Attempt to load a previously persisted snapshot and rebuild one if failed 216 head, generated, err := loadSnapshot(diskdb, triedb, cache, blockHash, root) 217 if err != nil { 218 if rebuild { 219 log.Warn("Failed to load snapshot, regenerating", "err", err) 220 snap.Rebuild(blockHash, root) 221 if !async { 222 if err := snap.verifyIntegrity(snap.disklayer(), true); err != nil { 223 return nil, err 224 } 225 } 226 return snap, nil 227 } 228 return nil, err // Bail out the error, don't rebuild automatically. 229 } 230 231 // Existing snapshot loaded, seed all the layers 232 // It is unnecessary to grab the lock here, since it was created within this function 233 // call, but we grab it nevertheless to follow the spec for insertSnap. 234 snap.lock.Lock() 235 defer snap.lock.Unlock() 236 for head != nil { 237 snap.insertSnap(head) 238 head = head.Parent() 239 } 240 241 // Verify any synchronously generated or loaded snapshot from disk 242 if !async || generated { 243 if err := snap.verifyIntegrity(snap.disklayer(), !async && !generated); err != nil { 244 return nil, err 245 } 246 } 247 248 return snap, nil 249 } 250 251 // insertSnap inserts [snap] into the tree. 252 // Assumes the lock is held. 253 func (t *Tree) insertSnap(snap snapshot) { 254 t.blockLayers[snap.BlockHash()] = snap 255 blockSnaps, ok := t.stateLayers[snap.Root()] 256 if !ok { 257 blockSnaps = make(map[common.Hash]snapshot) 258 t.stateLayers[snap.Root()] = blockSnaps 259 } 260 blockSnaps[snap.BlockHash()] = snap 261 } 262 263 // Snapshot retrieves a snapshot belonging to the given state root, or nil if no 264 // snapshot is maintained for that state root. 265 func (t *Tree) Snapshot(stateRoot common.Hash) Snapshot { 266 return t.getSnapshot(stateRoot, false) 267 } 268 269 // getSnapshot retrieves a Snapshot by its state root. If the caller already holds the 270 // snapTree lock when callthing this function, [holdsTreeLock] should be set to true. 271 func (t *Tree) getSnapshot(stateRoot common.Hash, holdsTreeLock bool) snapshot { 272 if !holdsTreeLock { 273 t.lock.RLock() 274 defer t.lock.RUnlock() 275 } 276 277 layers := t.stateLayers[stateRoot] 278 for _, layer := range layers { 279 return layer 280 } 281 return nil 282 } 283 284 // Snapshots returns all visited layers from the topmost layer with specific 285 // root and traverses downward. The layer amount is limited by the given number. 286 // If nodisk is set, then disk layer is excluded. 287 func (t *Tree) Snapshots(blockHash common.Hash, limits int, nodisk bool) []Snapshot { 288 t.lock.RLock() 289 defer t.lock.RUnlock() 290 291 if limits == 0 { 292 return nil 293 } 294 layer, ok := t.blockLayers[blockHash] 295 if !ok { 296 return nil 297 } 298 var ret []Snapshot 299 for { 300 if _, isdisk := layer.(*diskLayer); isdisk && nodisk { 301 break 302 } 303 ret = append(ret, layer) 304 limits -= 1 305 if limits == 0 { 306 break 307 } 308 parent := layer.Parent() 309 if parent == nil { 310 break 311 } 312 layer = parent 313 } 314 return ret 315 } 316 317 // Update adds a new snapshot into the tree, if that can be linked to an existing 318 // old parent. It is disallowed to insert a disk layer (the origin of all). 319 func (t *Tree) Update(blockHash, blockRoot, parentBlockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { 320 t.lock.Lock() 321 defer t.lock.Unlock() 322 323 // Grab the parent snapshot based on the parent block hash, not the parent state root 324 parent := t.blockLayers[parentBlockHash] 325 if parent == nil { 326 return fmt.Errorf("parent [%#x] snapshot missing", parentBlockHash) 327 } 328 329 snap := t.blockLayers[blockHash] 330 if snap != nil { 331 log.Warn("Attempted to insert a snapshot layer for an existing block", 332 "blockHash", blockHash, "blockRoot", blockRoot, "parentHash", parentBlockHash, 333 "existingBlockRoot", snap.Root(), 334 ) 335 } 336 337 snap = parent.Update(blockHash, blockRoot, destructs, accounts, storage) 338 t.insertSnap(snap) 339 return nil 340 } 341 342 // verifyIntegrity performs an integrity check on the current snapshot using 343 // verify. Most importantly, verifyIntegrity ensures verify is called at 344 // most once during the entire lifetime of [Tree], returning immediately if 345 // already invoked. If [waitBuild] is true, verifyIntegrity will wait for 346 // generation of the snapshot to finish before verifying. 347 // 348 // It is assumed that the caller holds the [snapTree] lock 349 // when calling this function. 350 func (t *Tree) verifyIntegrity(base *diskLayer, waitBuild bool) error { 351 // Find the rebuild termination channel and wait until 352 // the snapshot is generated 353 if done := base.genPending; waitBuild && done != nil { 354 log.Info("Waiting for snapshot generation", "root", base.root) 355 <-done 356 } 357 358 if t.verified { 359 return nil 360 } 361 362 if base.genMarker != nil { 363 return errors.New("cannot verify integrity of an unfinished snapshot") 364 } 365 366 start := time.Now() 367 log.Info("Verifying snapshot integrity", "root", base.root) 368 if err := t.verify(base.root, true); err != nil { 369 return fmt.Errorf("unable to verify snapshot integrity: %w", err) 370 } 371 372 log.Info("Verified snapshot integrity", "root", base.root, "elapsed", time.Since(start)) 373 t.verified = true 374 return nil 375 } 376 377 // Flatten flattens the snapshot for [blockHash] into its parent. if its 378 // parent is not a disk layer, Flatten will return an error. 379 // Note: a blockHash is used instead of a state root so that the exact state 380 // transition between the two states is well defined. This is intended to 381 // prevent the following edge case 382 // A 383 // / \ 384 // B C 385 // | 386 // D 387 // In this scenario, it's possible For (A, B) and (A, C, D) to be two 388 // different paths to the resulting state. We use block hashes and parent 389 // block hashes to ensure that the exact path through which we flatten 390 // diffLayers is well defined. 391 func (t *Tree) Flatten(blockHash common.Hash) error { 392 t.lock.Lock() 393 defer t.lock.Unlock() 394 395 start := time.Now() 396 snap, ok := t.blockLayers[blockHash] 397 if !ok { 398 return fmt.Errorf("cannot flatten missing snapshot: %s", blockHash) 399 } 400 diff, ok := snap.(*diffLayer) 401 if !ok { 402 return fmt.Errorf("cannot flatten disk layer: (%s, %s)", blockHash, snap.Root()) 403 } 404 if diff.parent == nil { 405 return fmt.Errorf("cannot flatten snapshot with missing parent (%s, %s)", blockHash, diff.root) 406 } 407 if parentDiff, ok := diff.parent.(*diffLayer); ok { 408 return fmt.Errorf("cannot flatten snapshot (%s, %s) into diff layer parent (%s, %s)", blockHash, diff.root, parentDiff.blockHash, parentDiff.root) 409 } 410 parentLayer := t.blockLayers[diff.parent.BlockHash()] 411 if parentLayer == nil { 412 return fmt.Errorf("snapshot missing parent layer: %s", diff.parent.BlockHash()) 413 } 414 415 diff.lock.Lock() 416 // Invoke the hook if it's registered. Ugly hack. 417 if t.onFlatten != nil { 418 t.onFlatten() 419 } 420 base, snapshotGenerated, err := diffToDisk(diff) 421 diff.lock.Unlock() 422 if err != nil { 423 return err 424 } 425 426 // Remove parent layer 427 if err := t.discard(diff.parent.BlockHash(), true); err != nil { 428 return fmt.Errorf("failed to discard parent layer while flattening (%s, %s): %w", blockHash, diff.root, err) 429 } 430 // We created a new diskLayer [base] to replace [diff], so we need to replace 431 // it in both maps and replace all pointers to it. 432 t.blockLayers[base.blockHash] = base 433 stateSnaps := t.stateLayers[base.root] 434 // stateSnaps must already be initialized here, since we are replacing 435 // an existing snapshot instead of adding a new one. 436 stateSnaps[base.blockHash] = base 437 438 // Replace the parent pointers for any snapshot that referenced 439 // the replaced diffLayer. 440 for _, snap := range t.blockLayers { 441 if diff, ok := snap.(*diffLayer); ok { 442 if base.blockHash == diff.parent.BlockHash() { 443 diff.lock.Lock() 444 diff.parent = base 445 diff.lock.Unlock() 446 } 447 } 448 } 449 450 // TODO add tracking of children to the snapshots to reduce overhead here. 451 children := make(map[common.Hash][]common.Hash) 452 for blockHash, snap := range t.blockLayers { 453 if diff, ok := snap.(*diffLayer); ok { 454 parent := diff.parent.BlockHash() 455 children[parent] = append(children[parent], blockHash) 456 } 457 } 458 var remove func(blockHash common.Hash) 459 remove = func(blockHash common.Hash) { 460 t.discard(blockHash, false) 461 for _, child := range children[blockHash] { 462 remove(child) 463 } 464 delete(children, blockHash) 465 } 466 for blockHash, snap := range t.blockLayers { 467 if snap.Stale() { 468 remove(blockHash) 469 } 470 } 471 // If the disk layer was modified, regenerate all the cumulative blooms 472 var rebloom func(blockHash common.Hash) 473 rebloom = func(blockHash common.Hash) { 474 if diff, ok := t.blockLayers[blockHash].(*diffLayer); ok { 475 diff.rebloom(base) 476 } 477 for _, child := range children[blockHash] { 478 rebloom(child) 479 } 480 } 481 rebloom(base.blockHash) 482 log.Debug("Flattened snapshot tree", "blockHash", blockHash, "root", base.root, "size", len(t.blockLayers), "elapsed", common.PrettyDuration(time.Since(start))) 483 484 if !snapshotGenerated { 485 return nil 486 } 487 return t.verifyIntegrity(base, false) 488 } 489 490 // Length returns the number of snapshot layers that is currently being maintained. 491 func (t *Tree) NumStateLayers() int { 492 t.lock.RLock() 493 defer t.lock.RUnlock() 494 495 return len(t.stateLayers) 496 } 497 498 func (t *Tree) NumBlockLayers() int { 499 t.lock.RLock() 500 defer t.lock.RUnlock() 501 502 return len(t.blockLayers) 503 } 504 505 // Discard removes layers that we no longer need 506 func (t *Tree) Discard(blockHash common.Hash) error { 507 t.lock.Lock() 508 defer t.lock.Unlock() 509 510 return t.discard(blockHash, false) 511 } 512 513 // discard removes the snapshot associated with [blockHash] from the 514 // snapshot tree. 515 // If [force] is true, discard may delete the disk layer. This should 516 // only be called within Flatten, when a new disk layer is being created. 517 // Assumes the lock is held. 518 func (t *Tree) discard(blockHash common.Hash, force bool) error { 519 snap := t.blockLayers[blockHash] 520 if snap == nil { 521 return fmt.Errorf("cannot discard missing snapshot: %s", blockHash) 522 } 523 _, ok := snap.(*diffLayer) 524 // Never discard the disk layer 525 if !ok && !force { 526 return fmt.Errorf("cannot discard the disk layer: %s", blockHash) 527 } 528 snaps, ok := t.stateLayers[snap.Root()] 529 if !ok { 530 return fmt.Errorf("cannot discard snapshot %s missing from state: %s", blockHash, snap.Root()) 531 } 532 // Discard the block from the map. If there are no more blocks 533 // mapping to the same state remove it from [stateLayers] as well. 534 delete(snaps, blockHash) 535 if len(snaps) == 0 { 536 delete(t.stateLayers, snap.Root()) 537 } 538 delete(t.blockLayers, blockHash) 539 return nil 540 } 541 542 // AbortGeneration aborts an ongoing snapshot generation process (if it hasn't 543 // stopped already). 544 // 545 // It is not required to manually abort snapshot generation. If generation has not 546 // been manually aborted prior to invoking [diffToDisk], it will be aborted anyways. 547 // 548 // It is safe to call this method multiple times and when there is no snapshot 549 // generation currently underway. 550 func (t *Tree) AbortGeneration() { 551 t.lock.Lock() 552 defer t.lock.Unlock() 553 554 dl := t.disklayer() 555 dl.abortGeneration() 556 } 557 558 // abortGeneration sends an abort message to the generate goroutine and waits 559 // for it to shutdown before returning (if it is running). This call should not 560 // be made concurrently. 561 func (dl *diskLayer) abortGeneration() bool { 562 // Store ideal time for abort to get better estimate of load 563 // 564 // Note that we set this time regardless if abortion was skipped otherwise we 565 // will never restart generation (age will always be negative). 566 if dl.abortStarted.IsZero() { 567 dl.abortStarted = time.Now() 568 } 569 570 // If the disk layer is running a snapshot generator, abort it 571 if dl.genAbort != nil && dl.genStats == nil { 572 abort := make(chan struct{}) 573 dl.genAbort <- abort 574 <-abort 575 return true 576 } 577 578 return false 579 } 580 581 // diffToDisk merges a bottom-most diff into the persistent disk layer underneath 582 // it. The method will panic if called onto a non-bottom-most diff layer. 583 // 584 // The disk layer persistence should be operated in an atomic way. All updates should 585 // be discarded if the whole transition if not finished. 586 func diffToDisk(bottom *diffLayer) (*diskLayer, bool, error) { 587 var ( 588 base = bottom.parent.(*diskLayer) 589 batch = base.diskdb.NewBatch() 590 ) 591 592 // Attempt to abort generation (if not already aborted) 593 base.abortGeneration() 594 595 // Put the deletion in the batch writer, flush all updates in the final step. 596 rawdb.DeleteSnapshotBlockHash(batch) 597 rawdb.DeleteSnapshotRoot(batch) 598 599 // Mark the original base as stale as we're going to create a new wrapper 600 base.lock.Lock() 601 if base.stale { 602 return nil, false, ErrStaleParentLayer // we've committed into the same base from two children, boo 603 } 604 base.stale = true 605 base.lock.Unlock() 606 607 // Destroy all the destructed accounts from the database 608 for hash := range bottom.destructSet { 609 // Skip any account not covered yet by the snapshot 610 if base.genMarker != nil && bytes.Compare(hash[:], base.genMarker) > 0 { 611 continue 612 } 613 // Remove all storage slots 614 rawdb.DeleteAccountSnapshot(batch, hash) 615 base.cache.Set(hash[:], nil) 616 617 it := rawdb.IterateStorageSnapshots(base.diskdb, hash) 618 for it.Next() { 619 key := it.Key() 620 batch.Delete(key) 621 base.cache.Del(key[1:]) 622 snapshotFlushStorageItemMeter.Mark(1) 623 624 // Ensure we don't delete too much data blindly (contract can be 625 // huge). It's ok to flush, the root will go missing in case of a 626 // crash and we'll detect and regenerate the snapshot. 627 if batch.ValueSize() > ethdb.IdealBatchSize { 628 if err := batch.Write(); err != nil { 629 log.Crit("Failed to write storage deletions", "err", err) 630 } 631 batch.Reset() 632 } 633 } 634 it.Release() 635 } 636 // Push all updated accounts into the database 637 for hash, data := range bottom.accountData { 638 // Skip any account not covered yet by the snapshot 639 if base.genMarker != nil && bytes.Compare(hash[:], base.genMarker) > 0 { 640 continue 641 } 642 // Push the account to disk 643 rawdb.WriteAccountSnapshot(batch, hash, data) 644 base.cache.Set(hash[:], data) 645 snapshotCleanAccountWriteMeter.Mark(int64(len(data))) 646 647 snapshotFlushAccountItemMeter.Mark(1) 648 snapshotFlushAccountSizeMeter.Mark(int64(len(data))) 649 650 // Ensure we don't write too much data blindly. It's ok to flush, the 651 // root will go missing in case of a crash and we'll detect and regen 652 // the snapshot. 653 if batch.ValueSize() > ethdb.IdealBatchSize { 654 if err := batch.Write(); err != nil { 655 log.Crit("Failed to write storage deletions", "err", err) 656 } 657 batch.Reset() 658 } 659 } 660 // Push all the storage slots into the database 661 for accountHash, storage := range bottom.storageData { 662 // Skip any account not covered yet by the snapshot 663 if base.genMarker != nil && bytes.Compare(accountHash[:], base.genMarker) > 0 { 664 continue 665 } 666 // Generation might be mid-account, track that case too 667 midAccount := base.genMarker != nil && bytes.Equal(accountHash[:], base.genMarker[:common.HashLength]) 668 669 for storageHash, data := range storage { 670 // Skip any slot not covered yet by the snapshot 671 if midAccount && bytes.Compare(storageHash[:], base.genMarker[common.HashLength:]) > 0 { 672 continue 673 } 674 if len(data) > 0 { 675 rawdb.WriteStorageSnapshot(batch, accountHash, storageHash, data) 676 base.cache.Set(append(accountHash[:], storageHash[:]...), data) 677 snapshotCleanStorageWriteMeter.Mark(int64(len(data))) 678 } else { 679 rawdb.DeleteStorageSnapshot(batch, accountHash, storageHash) 680 base.cache.Set(append(accountHash[:], storageHash[:]...), nil) 681 } 682 snapshotFlushStorageItemMeter.Mark(1) 683 snapshotFlushStorageSizeMeter.Mark(int64(len(data))) 684 } 685 } 686 // Update the snapshot block marker and write any remainder data 687 rawdb.WriteSnapshotBlockHash(batch, bottom.blockHash) 688 rawdb.WriteSnapshotRoot(batch, bottom.root) 689 690 // Write out the generator progress marker and report 691 journalProgress(batch, base.genMarker, base.genStats) 692 693 // Flush all the updates in the single db operation. Ensure the 694 // disk layer transition is atomic. 695 if err := batch.Write(); err != nil { 696 log.Crit("Failed to write leftover snapshot", "err", err) 697 } 698 log.Debug("Journalled disk layer", "root", bottom.root, "complete", base.genMarker == nil) 699 res := &diskLayer{ 700 root: bottom.root, 701 blockHash: bottom.blockHash, 702 cache: base.cache, 703 diskdb: base.diskdb, 704 triedb: base.triedb, 705 genMarker: base.genMarker, 706 genPending: base.genPending, 707 created: time.Now(), 708 } 709 // If snapshot generation hasn't finished yet, port over all the starts and 710 // continue where the previous round left off. 711 // 712 // Note, the `base.genAbort` comparison is not used normally, it's checked 713 // to allow the tests to play with the marker without triggering this path. 714 if base.genMarker != nil && base.genAbort != nil { 715 res.genMarker = base.genMarker 716 res.genAbort = make(chan chan struct{}) 717 718 // If the diskLayer we are about to discard is not very old, we skip 719 // generation on the next layer (assuming generation will just get canceled 720 // before doing meaningful work anyways). 721 diskLayerAge := base.abortStarted.Sub(base.created) 722 if diskLayerAge < skipGenThreshold { 723 log.Debug("Skipping snapshot generation", "previous disk layer age", diskLayerAge) 724 res.genStats = base.genStats 725 } else { 726 go res.generate(base.genStats) 727 } 728 } 729 return res, base.genMarker == nil, nil 730 } 731 732 // Rebuild wipes all available snapshot data from the persistent database and 733 // discard all caches and diff layers. Afterwards, it starts a new snapshot 734 // generator with the given root hash. 735 func (t *Tree) Rebuild(blockHash, root common.Hash) { 736 t.lock.Lock() 737 defer t.lock.Unlock() 738 739 // Track whether there's a wipe currently running and keep it alive if so 740 var wiper chan struct{} 741 742 // Iterate over and mark all layers stale 743 for _, layer := range t.blockLayers { 744 switch layer := layer.(type) { 745 case *diskLayer: 746 // If the base layer is generating, abort it and save 747 if layer.genAbort != nil { 748 abort := make(chan struct{}) 749 layer.genAbort <- abort 750 <-abort 751 752 if stats := layer.genStats; stats != nil { 753 wiper = stats.wiping 754 } 755 } 756 // Layer should be inactive now, mark it as stale 757 layer.lock.Lock() 758 layer.stale = true 759 layer.lock.Unlock() 760 761 case *diffLayer: 762 // If the layer is a simple diff, simply mark as stale 763 layer.lock.Lock() 764 atomic.StoreUint32(&layer.stale, 1) 765 layer.lock.Unlock() 766 767 default: 768 panic(fmt.Sprintf("unknown layer type: %T", layer)) 769 } 770 } 771 // Start generating a new snapshot from scratch on a background thread. The 772 // generator will run a wiper first if there's not one running right now. 773 log.Info("Rebuilding state snapshot") 774 base := generateSnapshot(t.diskdb, t.triedb, t.cache, blockHash, root, wiper) 775 t.blockLayers = map[common.Hash]snapshot{ 776 blockHash: base, 777 } 778 t.stateLayers = map[common.Hash]map[common.Hash]snapshot{ 779 root: { 780 blockHash: base, 781 }, 782 } 783 } 784 785 // AccountIterator creates a new account iterator for the specified root hash and 786 // seeks to a starting account hash. When [force] is true, a new account 787 // iterator is created without acquiring the [snapTree] lock and without 788 // confirming that the snapshot on the disk layer is fully generated. 789 func (t *Tree) AccountIterator(root common.Hash, seek common.Hash, force bool) (AccountIterator, error) { 790 if !force { 791 ok, err := t.generating() 792 if err != nil { 793 return nil, err 794 } 795 if ok { 796 return nil, ErrNotConstructed 797 } 798 } 799 return newFastAccountIterator(t, root, seek, force) 800 } 801 802 // StorageIterator creates a new storage iterator for the specified root hash and 803 // account. The iterator will be move to the specific start position. When [force] 804 // is true, a new account iterator is created without acquiring the [snapTree] 805 // lock and without confirming that the snapshot on the disk layer is fully generated. 806 func (t *Tree) StorageIterator(root common.Hash, account common.Hash, seek common.Hash, force bool) (StorageIterator, error) { 807 if !force { 808 ok, err := t.generating() 809 if err != nil { 810 return nil, err 811 } 812 if ok { 813 return nil, ErrNotConstructed 814 } 815 } 816 return newFastStorageIterator(t, root, account, seek, force) 817 } 818 819 // Verify iterates the whole state(all the accounts as well as the corresponding storages) 820 // with the specific root and compares the re-computed hash with the original one. 821 func (t *Tree) Verify(root common.Hash) error { 822 return t.verify(root, false) 823 } 824 825 // verify iterates the whole state(all the accounts as well as the corresponding storages) 826 // with the specific root and compares the re-computed hash with the original one. 827 // When [force] is true, it is assumed that the caller has confirmed that the 828 // snapshot is generated and that they hold the snapTree lock. 829 func (t *Tree) verify(root common.Hash, force bool) error { 830 acctIt, err := t.AccountIterator(root, common.Hash{}, force) 831 if err != nil { 832 return err 833 } 834 defer acctIt.Release() 835 836 got, err := generateTrieRoot(nil, acctIt, common.Hash{}, stackTrieGenerate, func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) { 837 storageIt, err := t.StorageIterator(root, accountHash, common.Hash{}, force) 838 if err != nil { 839 return common.Hash{}, err 840 } 841 defer storageIt.Release() 842 843 hash, err := generateTrieRoot(nil, storageIt, accountHash, stackTrieGenerate, nil, stat, false) 844 if err != nil { 845 return common.Hash{}, err 846 } 847 return hash, nil 848 }, newGenerateStats(), true) 849 850 if err != nil { 851 return err 852 } 853 if got != root { 854 return fmt.Errorf("state root hash mismatch: got %x, want %x", got, root) 855 } 856 return nil 857 } 858 859 // disklayer is an internal helper function to return the disk layer. 860 // The lock of snapTree is assumed to be held already. 861 func (t *Tree) disklayer() *diskLayer { 862 var snap snapshot 863 for _, s := range t.blockLayers { 864 snap = s 865 break 866 } 867 if snap == nil { 868 return nil 869 } 870 switch layer := snap.(type) { 871 case *diskLayer: 872 return layer 873 case *diffLayer: 874 return layer.origin 875 default: 876 panic(fmt.Sprintf("%T: undefined layer", snap)) 877 } 878 } 879 880 // diskRoot is a internal helper function to return the disk layer root. 881 // The lock of snapTree is assumed to be held already. 882 func (t *Tree) diskRoot() common.Hash { 883 disklayer := t.disklayer() 884 if disklayer == nil { 885 return common.Hash{} 886 } 887 return disklayer.Root() 888 } 889 890 // generating is an internal helper function which reports whether the snapshot 891 // is still under the construction. 892 func (t *Tree) generating() (bool, error) { 893 t.lock.Lock() 894 defer t.lock.Unlock() 895 896 layer := t.disklayer() 897 if layer == nil { 898 return false, errors.New("disk layer is missing") 899 } 900 layer.lock.RLock() 901 defer layer.lock.RUnlock() 902 return layer.genMarker != nil, nil 903 } 904 905 // diskRoot is a external helper function to return the disk layer root. 906 func (t *Tree) DiskRoot() common.Hash { 907 t.lock.Lock() 908 defer t.lock.Unlock() 909 910 return t.diskRoot() 911 } 912 913 func (t *Tree) DiskAccountIterator(seek common.Hash) AccountIterator { 914 t.lock.Lock() 915 defer t.lock.Unlock() 916 917 return t.disklayer().AccountIterator(seek) 918 } 919 920 func (t *Tree) DiskStorageIterator(account common.Hash, seek common.Hash) StorageIterator { 921 t.lock.Lock() 922 defer t.lock.Unlock() 923 924 it, _ := t.disklayer().StorageIterator(account, seek) 925 return it 926 } 927 928 // NewDiskLayer creates a diskLayer for direct access to the contents of the on-disk 929 // snapshot. Does not perform any validation. 930 func NewDiskLayer(diskdb ethdb.KeyValueStore) Snapshot { 931 return &diskLayer{ 932 diskdb: diskdb, 933 created: time.Now(), 934 935 // state sync uses iterators to access data, so this cache is not used. 936 // initializing it out of caution. 937 cache: fastcache.New(32 * 1024), 938 } 939 } 940 941 // NewTestTree creates a *Tree with a pre-populated diskLayer 942 func NewTestTree(diskdb ethdb.KeyValueStore, blockHash, root common.Hash) *Tree { 943 base := &diskLayer{ 944 diskdb: diskdb, 945 root: root, 946 blockHash: blockHash, 947 cache: fastcache.New(128 * 256), 948 created: time.Now(), 949 } 950 return &Tree{ 951 blockLayers: map[common.Hash]snapshot{ 952 blockHash: base, 953 }, 954 stateLayers: map[common.Hash]map[common.Hash]snapshot{ 955 root: { 956 blockHash: base, 957 }, 958 }, 959 } 960 }