github.com/theQRL/go-zond@v0.1.1/core/state/snapshot/generate.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 "errors" 22 "fmt" 23 "time" 24 25 "github.com/VictoriaMetrics/fastcache" 26 "github.com/theQRL/go-zond/common" 27 "github.com/theQRL/go-zond/common/hexutil" 28 "github.com/theQRL/go-zond/core/rawdb" 29 "github.com/theQRL/go-zond/core/types" 30 "github.com/theQRL/go-zond/log" 31 "github.com/theQRL/go-zond/rlp" 32 "github.com/theQRL/go-zond/trie" 33 "github.com/theQRL/go-zond/trie/trienode" 34 "github.com/theQRL/go-zond/zonddb" 35 ) 36 37 var ( 38 // accountCheckRange is the upper limit of the number of accounts involved in 39 // each range check. This is a value estimated based on experience. If this 40 // range is too large, the failure rate of range proof will increase. Otherwise, 41 // if the range is too small, the efficiency of the state recovery will decrease. 42 accountCheckRange = 128 43 44 // storageCheckRange is the upper limit of the number of storage slots involved 45 // in each range check. This is a value estimated based on experience. If this 46 // range is too large, the failure rate of range proof will increase. Otherwise, 47 // if the range is too small, the efficiency of the state recovery will decrease. 48 storageCheckRange = 1024 49 50 // errMissingTrie is returned if the target trie is missing while the generation 51 // is running. In this case the generation is aborted and wait the new signal. 52 errMissingTrie = errors.New("missing trie") 53 ) 54 55 // generateSnapshot regenerates a brand new snapshot based on an existing state 56 // database and head block asynchronously. The snapshot is returned immediately 57 // and generation is continued in the background until done. 58 func generateSnapshot(diskdb zonddb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash) *diskLayer { 59 // Create a new disk layer with an initialized state marker at zero 60 var ( 61 stats = &generatorStats{start: time.Now()} 62 batch = diskdb.NewBatch() 63 genMarker = []byte{} // Initialized but empty! 64 ) 65 rawdb.WriteSnapshotRoot(batch, root) 66 journalProgress(batch, genMarker, stats) 67 if err := batch.Write(); err != nil { 68 log.Crit("Failed to write initialized state marker", "err", err) 69 } 70 base := &diskLayer{ 71 diskdb: diskdb, 72 triedb: triedb, 73 root: root, 74 cache: fastcache.New(cache * 1024 * 1024), 75 genMarker: genMarker, 76 genPending: make(chan struct{}), 77 genAbort: make(chan chan *generatorStats), 78 } 79 go base.generate(stats) 80 log.Debug("Start snapshot generation", "root", root) 81 return base 82 } 83 84 // journalProgress persists the generator stats into the database to resume later. 85 func journalProgress(db zonddb.KeyValueWriter, marker []byte, stats *generatorStats) { 86 // Write out the generator marker. Note it's a standalone disk layer generator 87 // which is not mixed with journal. It's ok if the generator is persisted while 88 // journal is not. 89 entry := journalGenerator{ 90 Done: marker == nil, 91 Marker: marker, 92 } 93 if stats != nil { 94 entry.Accounts = stats.accounts 95 entry.Slots = stats.slots 96 entry.Storage = uint64(stats.storage) 97 } 98 blob, err := rlp.EncodeToBytes(entry) 99 if err != nil { 100 panic(err) // Cannot happen, here to catch dev errors 101 } 102 var logstr string 103 switch { 104 case marker == nil: 105 logstr = "done" 106 case bytes.Equal(marker, []byte{}): 107 logstr = "empty" 108 case len(marker) == common.HashLength: 109 logstr = fmt.Sprintf("%#x", marker) 110 default: 111 logstr = fmt.Sprintf("%#x:%#x", marker[:common.HashLength], marker[common.HashLength:]) 112 } 113 log.Debug("Journalled generator progress", "progress", logstr) 114 rawdb.WriteSnapshotGenerator(db, blob) 115 } 116 117 // proofResult contains the output of range proving which can be used 118 // for further processing regardless if it is successful or not. 119 type proofResult struct { 120 keys [][]byte // The key set of all elements being iterated, even proving is failed 121 vals [][]byte // The val set of all elements being iterated, even proving is failed 122 diskMore bool // Set when the database has extra snapshot states since last iteration 123 trieMore bool // Set when the trie has extra snapshot states(only meaningful for successful proving) 124 proofErr error // Indicator whether the given state range is valid or not 125 tr *trie.Trie // The trie, in case the trie was resolved by the prover (may be nil) 126 } 127 128 // valid returns the indicator that range proof is successful or not. 129 func (result *proofResult) valid() bool { 130 return result.proofErr == nil 131 } 132 133 // last returns the last verified element key regardless of whether the range proof is 134 // successful or not. Nil is returned if nothing involved in the proving. 135 func (result *proofResult) last() []byte { 136 var last []byte 137 if len(result.keys) > 0 { 138 last = result.keys[len(result.keys)-1] 139 } 140 return last 141 } 142 143 // forEach iterates all the visited elements and applies the given callback on them. 144 // The iteration is aborted if the callback returns non-nil error. 145 func (result *proofResult) forEach(callback func(key []byte, val []byte) error) error { 146 for i := 0; i < len(result.keys); i++ { 147 key, val := result.keys[i], result.vals[i] 148 if err := callback(key, val); err != nil { 149 return err 150 } 151 } 152 return nil 153 } 154 155 // proveRange proves the snapshot segment with particular prefix is "valid". 156 // The iteration start point will be assigned if the iterator is restored from 157 // the last interruption. Max will be assigned in order to limit the maximum 158 // amount of data involved in each iteration. 159 // 160 // The proof result will be returned if the range proving is finished, otherwise 161 // the error will be returned to abort the entire procedure. 162 func (dl *diskLayer) proveRange(ctx *generatorContext, trieId *trie.ID, prefix []byte, kind string, origin []byte, max int, valueConvertFn func([]byte) ([]byte, error)) (*proofResult, error) { 163 var ( 164 keys [][]byte 165 vals [][]byte 166 proof = rawdb.NewMemoryDatabase() 167 diskMore = false 168 iter = ctx.iterator(kind) 169 start = time.Now() 170 min = append(prefix, origin...) 171 ) 172 for iter.Next() { 173 // Ensure the iterated item is always equal or larger than the given origin. 174 key := iter.Key() 175 if bytes.Compare(key, min) < 0 { 176 return nil, errors.New("invalid iteration position") 177 } 178 // Ensure the iterated item still fall in the specified prefix. If 179 // not which means the items in the specified area are all visited. 180 // Move the iterator a step back since we iterate one extra element 181 // out. 182 if !bytes.Equal(key[:len(prefix)], prefix) { 183 iter.Hold() 184 break 185 } 186 // Break if we've reached the max size, and signal that we're not 187 // done yet. Move the iterator a step back since we iterate one 188 // extra element out. 189 if len(keys) == max { 190 iter.Hold() 191 diskMore = true 192 break 193 } 194 keys = append(keys, common.CopyBytes(key[len(prefix):])) 195 196 if valueConvertFn == nil { 197 vals = append(vals, common.CopyBytes(iter.Value())) 198 } else { 199 val, err := valueConvertFn(iter.Value()) 200 if err != nil { 201 // Special case, the state data is corrupted (invalid slim-format account), 202 // don't abort the entire procedure directly. Instead, let the fallback 203 // generation to heal the invalid data. 204 // 205 // Here append the original value to ensure that the number of key and 206 // value are aligned. 207 vals = append(vals, common.CopyBytes(iter.Value())) 208 log.Error("Failed to convert account state data", "err", err) 209 } else { 210 vals = append(vals, val) 211 } 212 } 213 } 214 // Update metrics for database iteration and merkle proving 215 if kind == snapStorage { 216 snapStorageSnapReadCounter.Inc(time.Since(start).Nanoseconds()) 217 } else { 218 snapAccountSnapReadCounter.Inc(time.Since(start).Nanoseconds()) 219 } 220 defer func(start time.Time) { 221 if kind == snapStorage { 222 snapStorageProveCounter.Inc(time.Since(start).Nanoseconds()) 223 } else { 224 snapAccountProveCounter.Inc(time.Since(start).Nanoseconds()) 225 } 226 }(time.Now()) 227 228 // The snap state is exhausted, pass the entire key/val set for verification 229 root := trieId.Root 230 if origin == nil && !diskMore { 231 stackTr := trie.NewStackTrie(nil) 232 for i, key := range keys { 233 stackTr.Update(key, vals[i]) 234 } 235 if gotRoot := stackTr.Hash(); gotRoot != root { 236 return &proofResult{ 237 keys: keys, 238 vals: vals, 239 proofErr: fmt.Errorf("wrong root: have %#x want %#x", gotRoot, root), 240 }, nil 241 } 242 return &proofResult{keys: keys, vals: vals}, nil 243 } 244 // Snap state is chunked, generate edge proofs for verification. 245 tr, err := trie.New(trieId, dl.triedb) 246 if err != nil { 247 ctx.stats.Log("Trie missing, state snapshotting paused", dl.root, dl.genMarker) 248 return nil, errMissingTrie 249 } 250 // Firstly find out the key of last iterated element. 251 var last []byte 252 if len(keys) > 0 { 253 last = keys[len(keys)-1] 254 } 255 // Generate the Merkle proofs for the first and last element 256 if origin == nil { 257 origin = common.Hash{}.Bytes() 258 } 259 if err := tr.Prove(origin, proof); err != nil { 260 log.Debug("Failed to prove range", "kind", kind, "origin", origin, "err", err) 261 return &proofResult{ 262 keys: keys, 263 vals: vals, 264 diskMore: diskMore, 265 proofErr: err, 266 tr: tr, 267 }, nil 268 } 269 if last != nil { 270 if err := tr.Prove(last, proof); err != nil { 271 log.Debug("Failed to prove range", "kind", kind, "last", last, "err", err) 272 return &proofResult{ 273 keys: keys, 274 vals: vals, 275 diskMore: diskMore, 276 proofErr: err, 277 tr: tr, 278 }, nil 279 } 280 } 281 // Verify the snapshot segment with range prover, ensure that all flat states 282 // in this range correspond to merkle trie. 283 cont, err := trie.VerifyRangeProof(root, origin, last, keys, vals, proof) 284 return &proofResult{ 285 keys: keys, 286 vals: vals, 287 diskMore: diskMore, 288 trieMore: cont, 289 proofErr: err, 290 tr: tr}, 291 nil 292 } 293 294 // onStateCallback is a function that is called by generateRange, when processing a range of 295 // accounts or storage slots. For each element, the callback is invoked. 296 // 297 // - If 'delete' is true, then this element (and potential slots) needs to be deleted from the snapshot. 298 // - If 'write' is true, then this element needs to be updated with the 'val'. 299 // - If 'write' is false, then this element is already correct, and needs no update. 300 // The 'val' is the canonical encoding of the value (not the slim format for accounts) 301 // 302 // However, for accounts, the storage trie of the account needs to be checked. Also, 303 // dangling storages(storage exists but the corresponding account is missing) need to 304 // be cleaned up. 305 type onStateCallback func(key []byte, val []byte, write bool, delete bool) error 306 307 // generateRange generates the state segment with particular prefix. Generation can 308 // either verify the correctness of existing state through range-proof and skip 309 // generation, or iterate trie to regenerate state on demand. 310 func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefix []byte, kind string, origin []byte, max int, onState onStateCallback, valueConvertFn func([]byte) ([]byte, error)) (bool, []byte, error) { 311 // Use range prover to check the validity of the flat state in the range 312 result, err := dl.proveRange(ctx, trieId, prefix, kind, origin, max, valueConvertFn) 313 if err != nil { 314 return false, nil, err 315 } 316 last := result.last() 317 318 // Construct contextual logger 319 logCtx := []interface{}{"kind", kind, "prefix", hexutil.Encode(prefix)} 320 if len(origin) > 0 { 321 logCtx = append(logCtx, "origin", hexutil.Encode(origin)) 322 } 323 logger := log.New(logCtx...) 324 325 // The range prover says the range is correct, skip trie iteration 326 if result.valid() { 327 snapSuccessfulRangeProofMeter.Mark(1) 328 logger.Trace("Proved state range", "last", hexutil.Encode(last)) 329 330 // The verification is passed, process each state with the given 331 // callback function. If this state represents a contract, the 332 // corresponding storage check will be performed in the callback 333 if err := result.forEach(func(key []byte, val []byte) error { return onState(key, val, false, false) }); err != nil { 334 return false, nil, err 335 } 336 // Only abort the iteration when both database and trie are exhausted 337 return !result.diskMore && !result.trieMore, last, nil 338 } 339 logger.Trace("Detected outdated state range", "last", hexutil.Encode(last), "err", result.proofErr) 340 snapFailedRangeProofMeter.Mark(1) 341 342 // Special case, the entire trie is missing. In the original trie scheme, 343 // all the duplicated subtries will be filtered out (only one copy of data 344 // will be stored). While in the snapshot model, all the storage tries 345 // belong to different contracts will be kept even they are duplicated. 346 // Track it to a certain extent remove the noise data used for statistics. 347 if origin == nil && last == nil { 348 meter := snapMissallAccountMeter 349 if kind == snapStorage { 350 meter = snapMissallStorageMeter 351 } 352 meter.Mark(1) 353 } 354 // We use the snap data to build up a cache which can be used by the 355 // main account trie as a primary lookup when resolving hashes 356 var resolver trie.NodeResolver 357 if len(result.keys) > 0 { 358 mdb := rawdb.NewMemoryDatabase() 359 tdb := trie.NewDatabase(mdb, trie.HashDefaults) 360 defer tdb.Close() 361 snapTrie := trie.NewEmpty(tdb) 362 for i, key := range result.keys { 363 snapTrie.Update(key, result.vals[i]) 364 } 365 root, nodes, err := snapTrie.Commit(false) 366 if err != nil { 367 return false, nil, err 368 } 369 if nodes != nil { 370 tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) 371 tdb.Commit(root, false) 372 } 373 resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte { 374 return rawdb.ReadTrieNode(mdb, owner, path, hash, tdb.Scheme()) 375 } 376 } 377 // Construct the trie for state iteration, reuse the trie 378 // if it's already opened with some nodes resolved. 379 tr := result.tr 380 if tr == nil { 381 tr, err = trie.New(trieId, dl.triedb) 382 if err != nil { 383 ctx.stats.Log("Trie missing, state snapshotting paused", dl.root, dl.genMarker) 384 return false, nil, errMissingTrie 385 } 386 } 387 var ( 388 trieMore bool 389 kvkeys, kvvals = result.keys, result.vals 390 391 // counters 392 count = 0 // number of states delivered by iterator 393 created = 0 // states created from the trie 394 updated = 0 // states updated from the trie 395 deleted = 0 // states not in trie, but were in snapshot 396 untouched = 0 // states already correct 397 398 // timers 399 start = time.Now() 400 internal time.Duration 401 ) 402 nodeIt, err := tr.NodeIterator(origin) 403 if err != nil { 404 return false, nil, err 405 } 406 nodeIt.AddResolver(resolver) 407 iter := trie.NewIterator(nodeIt) 408 409 for iter.Next() { 410 if last != nil && bytes.Compare(iter.Key, last) > 0 { 411 trieMore = true 412 break 413 } 414 count++ 415 write := true 416 created++ 417 for len(kvkeys) > 0 { 418 if cmp := bytes.Compare(kvkeys[0], iter.Key); cmp < 0 { 419 // delete the key 420 istart := time.Now() 421 if err := onState(kvkeys[0], nil, false, true); err != nil { 422 return false, nil, err 423 } 424 kvkeys = kvkeys[1:] 425 kvvals = kvvals[1:] 426 deleted++ 427 internal += time.Since(istart) 428 continue 429 } else if cmp == 0 { 430 // the snapshot key can be overwritten 431 created-- 432 if write = !bytes.Equal(kvvals[0], iter.Value); write { 433 updated++ 434 } else { 435 untouched++ 436 } 437 kvkeys = kvkeys[1:] 438 kvvals = kvvals[1:] 439 } 440 break 441 } 442 istart := time.Now() 443 if err := onState(iter.Key, iter.Value, write, false); err != nil { 444 return false, nil, err 445 } 446 internal += time.Since(istart) 447 } 448 if iter.Err != nil { 449 return false, nil, iter.Err 450 } 451 // Delete all stale snapshot states remaining 452 istart := time.Now() 453 for _, key := range kvkeys { 454 if err := onState(key, nil, false, true); err != nil { 455 return false, nil, err 456 } 457 deleted += 1 458 } 459 internal += time.Since(istart) 460 461 // Update metrics for counting trie iteration 462 if kind == snapStorage { 463 snapStorageTrieReadCounter.Inc((time.Since(start) - internal).Nanoseconds()) 464 } else { 465 snapAccountTrieReadCounter.Inc((time.Since(start) - internal).Nanoseconds()) 466 } 467 logger.Debug("Regenerated state range", "root", trieId.Root, "last", hexutil.Encode(last), 468 "count", count, "created", created, "updated", updated, "untouched", untouched, "deleted", deleted) 469 470 // If there are either more trie items, or there are more snap items 471 // (in the next segment), then we need to keep working 472 return !trieMore && !result.diskMore, last, nil 473 } 474 475 // checkAndFlush checks if an interruption signal is received or the 476 // batch size has exceeded the allowance. 477 func (dl *diskLayer) checkAndFlush(ctx *generatorContext, current []byte) error { 478 var abort chan *generatorStats 479 select { 480 case abort = <-dl.genAbort: 481 default: 482 } 483 if ctx.batch.ValueSize() > zonddb.IdealBatchSize || abort != nil { 484 if bytes.Compare(current, dl.genMarker) < 0 { 485 log.Error("Snapshot generator went backwards", "current", fmt.Sprintf("%x", current), "genMarker", fmt.Sprintf("%x", dl.genMarker)) 486 } 487 // Flush out the batch anyway no matter it's empty or not. 488 // It's possible that all the states are recovered and the 489 // generation indeed makes progress. 490 journalProgress(ctx.batch, current, ctx.stats) 491 492 if err := ctx.batch.Write(); err != nil { 493 return err 494 } 495 ctx.batch.Reset() 496 497 dl.lock.Lock() 498 dl.genMarker = current 499 dl.lock.Unlock() 500 501 if abort != nil { 502 ctx.stats.Log("Aborting state snapshot generation", dl.root, current) 503 return newAbortErr(abort) // bubble up an error for interruption 504 } 505 // Don't hold the iterators too long, release them to let compactor works 506 ctx.reopenIterator(snapAccount) 507 ctx.reopenIterator(snapStorage) 508 } 509 if time.Since(ctx.logged) > 8*time.Second { 510 ctx.stats.Log("Generating state snapshot", dl.root, current) 511 ctx.logged = time.Now() 512 } 513 return nil 514 } 515 516 // generateStorages generates the missing storage slots of the specific contract. 517 // It's supposed to restart the generation from the given origin position. 518 func generateStorages(ctx *generatorContext, dl *diskLayer, stateRoot common.Hash, account common.Hash, storageRoot common.Hash, storeMarker []byte) error { 519 onStorage := func(key []byte, val []byte, write bool, delete bool) error { 520 defer func(start time.Time) { 521 snapStorageWriteCounter.Inc(time.Since(start).Nanoseconds()) 522 }(time.Now()) 523 524 if delete { 525 rawdb.DeleteStorageSnapshot(ctx.batch, account, common.BytesToHash(key)) 526 snapWipedStorageMeter.Mark(1) 527 return nil 528 } 529 if write { 530 rawdb.WriteStorageSnapshot(ctx.batch, account, common.BytesToHash(key), val) 531 snapGeneratedStorageMeter.Mark(1) 532 } else { 533 snapRecoveredStorageMeter.Mark(1) 534 } 535 ctx.stats.storage += common.StorageSize(1 + 2*common.HashLength + len(val)) 536 ctx.stats.slots++ 537 538 // If we've exceeded our batch allowance or termination was requested, flush to disk 539 if err := dl.checkAndFlush(ctx, append(account[:], key...)); err != nil { 540 return err 541 } 542 return nil 543 } 544 // Loop for re-generating the missing storage slots. 545 var origin = common.CopyBytes(storeMarker) 546 for { 547 id := trie.StorageTrieID(stateRoot, account, storageRoot) 548 exhausted, last, err := dl.generateRange(ctx, id, append(rawdb.SnapshotStoragePrefix, account.Bytes()...), snapStorage, origin, storageCheckRange, onStorage, nil) 549 if err != nil { 550 return err // The procedure it aborted, either by external signal or internal error. 551 } 552 // Abort the procedure if the entire contract storage is generated 553 if exhausted { 554 break 555 } 556 if origin = increaseKey(last); origin == nil { 557 break // special case, the last is 0xffffffff...fff 558 } 559 } 560 return nil 561 } 562 563 // generateAccounts generates the missing snapshot accounts as well as their 564 // storage slots in the main trie. It's supposed to restart the generation 565 // from the given origin position. 566 func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) error { 567 onAccount := func(key []byte, val []byte, write bool, delete bool) error { 568 // Make sure to clear all dangling storages before this account 569 account := common.BytesToHash(key) 570 ctx.removeStorageBefore(account) 571 572 start := time.Now() 573 if delete { 574 rawdb.DeleteAccountSnapshot(ctx.batch, account) 575 snapWipedAccountMeter.Mark(1) 576 snapAccountWriteCounter.Inc(time.Since(start).Nanoseconds()) 577 578 ctx.removeStorageAt(account) 579 return nil 580 } 581 // Retrieve the current account and flatten it into the internal format 582 var acc types.StateAccount 583 if err := rlp.DecodeBytes(val, &acc); err != nil { 584 log.Crit("Invalid account encountered during snapshot creation", "err", err) 585 } 586 // If the account is not yet in-progress, write it out 587 if accMarker == nil || !bytes.Equal(account[:], accMarker) { 588 dataLen := len(val) // Approximate size, saves us a round of RLP-encoding 589 if !write { 590 if bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) { 591 dataLen -= 32 592 } 593 if acc.Root == types.EmptyRootHash { 594 dataLen -= 32 595 } 596 snapRecoveredAccountMeter.Mark(1) 597 } else { 598 data := types.SlimAccountRLP(acc) 599 dataLen = len(data) 600 rawdb.WriteAccountSnapshot(ctx.batch, account, data) 601 snapGeneratedAccountMeter.Mark(1) 602 } 603 ctx.stats.storage += common.StorageSize(1 + common.HashLength + dataLen) 604 ctx.stats.accounts++ 605 } 606 // If the snap generation goes here after interrupted, genMarker may go backward 607 // when last genMarker is consisted of accountHash and storageHash 608 marker := account[:] 609 if accMarker != nil && bytes.Equal(marker, accMarker) && len(dl.genMarker) > common.HashLength { 610 marker = dl.genMarker[:] 611 } 612 // If we've exceeded our batch allowance or termination was requested, flush to disk 613 if err := dl.checkAndFlush(ctx, marker); err != nil { 614 return err 615 } 616 snapAccountWriteCounter.Inc(time.Since(start).Nanoseconds()) // let's count flush time as well 617 618 // If the iterated account is the contract, create a further loop to 619 // verify or regenerate the contract storage. 620 if acc.Root == types.EmptyRootHash { 621 ctx.removeStorageAt(account) 622 } else { 623 var storeMarker []byte 624 if accMarker != nil && bytes.Equal(account[:], accMarker) && len(dl.genMarker) > common.HashLength { 625 storeMarker = dl.genMarker[common.HashLength:] 626 } 627 if err := generateStorages(ctx, dl, dl.root, account, acc.Root, storeMarker); err != nil { 628 return err 629 } 630 } 631 // Some account processed, unmark the marker 632 accMarker = nil 633 return nil 634 } 635 // Always reset the initial account range as 1 whenever recover from the 636 // interruption. TODO(rjl493456442) can we remove it? 637 var accountRange = accountCheckRange 638 if len(accMarker) > 0 { 639 accountRange = 1 640 } 641 origin := common.CopyBytes(accMarker) 642 for { 643 id := trie.StateTrieID(dl.root) 644 exhausted, last, err := dl.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountRange, onAccount, types.FullAccountRLP) 645 if err != nil { 646 return err // The procedure it aborted, either by external signal or internal error. 647 } 648 origin = increaseKey(last) 649 650 // Last step, cleanup the storages after the last account. 651 // All the left storages should be treated as dangling. 652 if origin == nil || exhausted { 653 ctx.removeStorageLeft() 654 break 655 } 656 accountRange = accountCheckRange 657 } 658 return nil 659 } 660 661 // generate is a background thread that iterates over the state and storage tries, 662 // constructing the state snapshot. All the arguments are purely for statistics 663 // gathering and logging, since the method surfs the blocks as they arrive, often 664 // being restarted. 665 func (dl *diskLayer) generate(stats *generatorStats) { 666 var ( 667 accMarker []byte 668 abort chan *generatorStats 669 ) 670 if len(dl.genMarker) > 0 { // []byte{} is the start, use nil for that 671 accMarker = dl.genMarker[:common.HashLength] 672 } 673 stats.Log("Resuming state snapshot generation", dl.root, dl.genMarker) 674 675 // Initialize the global generator context. The snapshot iterators are 676 // opened at the interrupted position because the assumption is held 677 // that all the snapshot data are generated correctly before the marker. 678 // Even if the snapshot data is updated during the interruption (before 679 // or at the marker), the assumption is still held. 680 // For the account or storage slot at the interruption, they will be 681 // processed twice by the generator(they are already processed in the 682 // last run) but it's fine. 683 ctx := newGeneratorContext(stats, dl.diskdb, accMarker, dl.genMarker) 684 defer ctx.close() 685 686 if err := generateAccounts(ctx, dl, accMarker); err != nil { 687 // Extract the received interruption signal if exists 688 if aerr, ok := err.(*abortErr); ok { 689 abort = aerr.abort 690 } 691 // Aborted by internal error, wait the signal 692 if abort == nil { 693 abort = <-dl.genAbort 694 } 695 abort <- stats 696 return 697 } 698 // Snapshot fully generated, set the marker to nil. 699 // Note even there is nothing to commit, persist the 700 // generator anyway to mark the snapshot is complete. 701 journalProgress(ctx.batch, nil, stats) 702 if err := ctx.batch.Write(); err != nil { 703 log.Error("Failed to flush batch", "err", err) 704 705 abort = <-dl.genAbort 706 abort <- stats 707 return 708 } 709 ctx.batch.Reset() 710 711 log.Info("Generated state snapshot", "accounts", stats.accounts, "slots", stats.slots, 712 "storage", stats.storage, "dangling", stats.dangling, "elapsed", common.PrettyDuration(time.Since(stats.start))) 713 714 dl.lock.Lock() 715 dl.genMarker = nil 716 close(dl.genPending) 717 dl.lock.Unlock() 718 719 // Someone will be looking for us, wait it out 720 abort = <-dl.genAbort 721 abort <- nil 722 } 723 724 // increaseKey increase the input key by one bit. Return nil if the entire 725 // addition operation overflows. 726 func increaseKey(key []byte) []byte { 727 for i := len(key) - 1; i >= 0; i-- { 728 key[i]++ 729 if key[i] != 0x0 { 730 return key 731 } 732 } 733 return nil 734 } 735 736 // abortErr wraps an interruption signal received to represent the 737 // generation is aborted by external processes. 738 type abortErr struct { 739 abort chan *generatorStats 740 } 741 742 func newAbortErr(abort chan *generatorStats) error { 743 return &abortErr{abort: abort} 744 } 745 746 func (err *abortErr) Error() string { 747 return "aborted" 748 }