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