github.com/haliliceylan/bsc@v1.1.10-0.20220501224556-eb78d644ebcb/cmd/geth/snapshot.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "os" 24 "path/filepath" 25 "time" 26 27 "github.com/prometheus/tsdb/fileutil" 28 cli "gopkg.in/urfave/cli.v1" 29 30 "github.com/ethereum/go-ethereum/cmd/utils" 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/core/rawdb" 33 "github.com/ethereum/go-ethereum/core/state" 34 "github.com/ethereum/go-ethereum/core/state/pruner" 35 "github.com/ethereum/go-ethereum/core/state/snapshot" 36 "github.com/ethereum/go-ethereum/crypto" 37 "github.com/ethereum/go-ethereum/ethdb" 38 "github.com/ethereum/go-ethereum/log" 39 "github.com/ethereum/go-ethereum/node" 40 "github.com/ethereum/go-ethereum/rlp" 41 "github.com/ethereum/go-ethereum/trie" 42 ) 43 44 var ( 45 // emptyRoot is the known root hash of an empty trie. 46 emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") 47 48 // emptyCode is the known hash of the empty EVM bytecode. 49 emptyCode = crypto.Keccak256(nil) 50 ) 51 52 var ( 53 snapshotCommand = cli.Command{ 54 Name: "snapshot", 55 Usage: "A set of commands based on the snapshot", 56 Category: "MISCELLANEOUS COMMANDS", 57 Description: "", 58 Subcommands: []cli.Command{ 59 { 60 Name: "prune-state", 61 Usage: "Prune stale ethereum state data based on the snapshot", 62 ArgsUsage: "<root>", 63 Action: utils.MigrateFlags(pruneState), 64 Category: "MISCELLANEOUS COMMANDS", 65 Flags: []cli.Flag{ 66 utils.DataDirFlag, 67 utils.AncientFlag, 68 utils.RopstenFlag, 69 utils.RinkebyFlag, 70 utils.GoerliFlag, 71 utils.CacheTrieJournalFlag, 72 utils.BloomFilterSizeFlag, 73 utils.TriesInMemoryFlag, 74 }, 75 Description: ` 76 geth snapshot prune-state <state-root> 77 will prune historical state data with the help of the state snapshot. 78 All trie nodes and contract codes that do not belong to the specified 79 version state will be deleted from the database. After pruning, only 80 two version states are available: genesis and the specific one. 81 82 The default pruning target is the HEAD-127 state. 83 84 WARNING: It's necessary to delete the trie clean cache after the pruning. 85 If you specify another directory for the trie clean cache via "--cache.trie.journal" 86 during the use of Geth, please also specify it here for correct deletion. Otherwise 87 the trie clean cache with default directory will be deleted. 88 `, 89 }, 90 { 91 Name: "prune-block", 92 Usage: "Prune block data offline", 93 Action: utils.MigrateFlags(pruneBlock), 94 Category: "MISCELLANEOUS COMMANDS", 95 Flags: []cli.Flag{ 96 utils.DataDirFlag, 97 utils.AncientFlag, 98 utils.BlockAmountReserved, 99 utils.TriesInMemoryFlag, 100 utils.CheckSnapshotWithMPT, 101 }, 102 Description: ` 103 geth offline prune-block for block data in ancientdb. 104 The amount of blocks expected for remaining after prune can be specified via block-amount-reserved in this command, 105 will prune and only remain the specified amount of old block data in ancientdb. 106 the brief workflow is to backup the the number of this specified amount blocks backward in original ancientdb 107 into new ancient_backup, then delete the original ancientdb dir and rename the ancient_backup to original one for replacement, 108 finally assemble the statedb and new ancientDb together. 109 The purpose of doing it is because the block data will be moved into the ancient store when it 110 becomes old enough(exceed the Threshold 90000), the disk usage will be very large over time, and is occupied mainly by ancientDb, 111 so it's very necessary to do block data prune, this feature will handle it. 112 `, 113 }, 114 { 115 Name: "verify-state", 116 Usage: "Recalculate state hash based on the snapshot for verification", 117 ArgsUsage: "<root>", 118 Action: utils.MigrateFlags(verifyState), 119 Category: "MISCELLANEOUS COMMANDS", 120 Flags: []cli.Flag{ 121 utils.DataDirFlag, 122 utils.AncientFlag, 123 utils.RopstenFlag, 124 utils.RinkebyFlag, 125 utils.GoerliFlag, 126 }, 127 Description: ` 128 geth snapshot verify-state <state-root> 129 will traverse the whole accounts and storages set based on the specified 130 snapshot and recalculate the root hash of state for verification. 131 In other words, this command does the snapshot to trie conversion. 132 `, 133 }, 134 { 135 Name: "traverse-state", 136 Usage: "Traverse the state with given root hash for verification", 137 ArgsUsage: "<root>", 138 Action: utils.MigrateFlags(traverseState), 139 Category: "MISCELLANEOUS COMMANDS", 140 Flags: []cli.Flag{ 141 utils.DataDirFlag, 142 utils.AncientFlag, 143 utils.RopstenFlag, 144 utils.RinkebyFlag, 145 utils.GoerliFlag, 146 }, 147 Description: ` 148 geth snapshot traverse-state <state-root> 149 will traverse the whole state from the given state root and will abort if any 150 referenced trie node or contract code is missing. This command can be used for 151 state integrity verification. The default checking target is the HEAD state. 152 153 It's also usable without snapshot enabled. 154 `, 155 }, 156 { 157 Name: "traverse-rawstate", 158 Usage: "Traverse the state with given root hash for verification", 159 ArgsUsage: "<root>", 160 Action: utils.MigrateFlags(traverseRawState), 161 Category: "MISCELLANEOUS COMMANDS", 162 Flags: []cli.Flag{ 163 utils.DataDirFlag, 164 utils.AncientFlag, 165 utils.RopstenFlag, 166 utils.RinkebyFlag, 167 utils.GoerliFlag, 168 }, 169 Description: ` 170 geth snapshot traverse-rawstate <state-root> 171 will traverse the whole state from the given root and will abort if any referenced 172 trie node or contract code is missing. This command can be used for state integrity 173 verification. The default checking target is the HEAD state. It's basically identical 174 to traverse-state, but the check granularity is smaller. 175 176 It's also usable without snapshot enabled. 177 `, 178 }, 179 }, 180 } 181 ) 182 183 func accessDb(ctx *cli.Context, stack *node.Node) (ethdb.Database, error) { 184 //The layer of tries trees that keep in memory. 185 TriesInMemory := int(ctx.GlobalUint64(utils.TriesInMemoryFlag.Name)) 186 chaindb := utils.MakeChainDatabase(ctx, stack, false, true) 187 defer chaindb.Close() 188 189 if !ctx.GlobalBool(utils.CheckSnapshotWithMPT.Name) { 190 return chaindb, nil 191 } 192 headBlock := rawdb.ReadHeadBlock(chaindb) 193 if headBlock == nil { 194 return nil, errors.New("failed to load head block") 195 } 196 headHeader := headBlock.Header() 197 //Make sure the MPT and snapshot matches before pruning, otherwise the node can not start. 198 snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, TriesInMemory, headBlock.Root(), false, false, false) 199 if err != nil { 200 log.Error("snaptree error", "err", err) 201 return nil, err // The relevant snapshot(s) might not exist 202 } 203 204 // Use the HEAD-(n-1) as the target root. The reason for picking it is: 205 // - in most of the normal cases, the related state is available 206 // - the probability of this layer being reorg is very low 207 208 // Retrieve all snapshot layers from the current HEAD. 209 // In theory there are n difflayers + 1 disk layer present, 210 // so n diff layers are expected to be returned. 211 layers := snaptree.Snapshots(headHeader.Root, TriesInMemory, true) 212 if len(layers) != TriesInMemory { 213 // Reject if the accumulated diff layers are less than n. It 214 // means in most of normal cases, there is no associated state 215 // with bottom-most diff layer. 216 log.Error("snapshot layers != TriesInMemory", "err", err) 217 return nil, fmt.Errorf("snapshot not old enough yet: need %d more blocks", TriesInMemory-len(layers)) 218 } 219 // Use the bottom-most diff layer as the target 220 targetRoot := layers[len(layers)-1].Root() 221 222 // Ensure the root is really present. The weak assumption 223 // is the presence of root can indicate the presence of the 224 // entire trie. 225 if blob := rawdb.ReadTrieNode(chaindb, targetRoot); len(blob) == 0 { 226 // The special case is for clique based networks(rinkeby, goerli 227 // and some other private networks), it's possible that two 228 // consecutive blocks will have same root. In this case snapshot 229 // difflayer won't be created. So HEAD-(n-1) may not paired with 230 // head-(n-1) layer. Instead the paired layer is higher than the 231 // bottom-most diff layer. Try to find the bottom-most snapshot 232 // layer with state available. 233 // 234 // Note HEAD is ignored. Usually there is the associated 235 // state available, but we don't want to use the topmost state 236 // as the pruning target. 237 var found bool 238 for i := len(layers) - 2; i >= 1; i-- { 239 if blob := rawdb.ReadTrieNode(chaindb, layers[i].Root()); len(blob) != 0 { 240 targetRoot = layers[i].Root() 241 found = true 242 log.Info("Selecting middle-layer as the pruning target", "root", targetRoot, "depth", i) 243 break 244 } 245 } 246 if !found { 247 if blob := rawdb.ReadTrieNode(chaindb, snaptree.DiskRoot()); len(blob) != 0 { 248 targetRoot = snaptree.DiskRoot() 249 found = true 250 log.Info("Selecting disk-layer as the pruning target", "root", targetRoot) 251 } 252 } 253 if !found { 254 if len(layers) > 0 { 255 log.Error("no snapshot paired state") 256 return nil, errors.New("no snapshot paired state") 257 } 258 return nil, fmt.Errorf("associated state[%x] is not present", targetRoot) 259 } 260 } else { 261 if len(layers) > 0 { 262 log.Info("Selecting bottom-most difflayer as the pruning target", "root", targetRoot, "height", headHeader.Number.Uint64()-uint64(len(layers)-1)) 263 } else { 264 log.Info("Selecting user-specified state as the pruning target", "root", targetRoot) 265 } 266 } 267 return chaindb, nil 268 } 269 270 func pruneBlock(ctx *cli.Context) error { 271 stack, config := makeConfigNode(ctx) 272 defer stack.Close() 273 blockAmountReserved := ctx.GlobalUint64(utils.BlockAmountReserved.Name) 274 chaindb, err := accessDb(ctx, stack) 275 if err != nil { 276 return err 277 } 278 var newAncientPath string 279 oldAncientPath := ctx.GlobalString(utils.AncientFlag.Name) 280 if !filepath.IsAbs(oldAncientPath) { 281 // force absolute paths, which often fail due to the splicing of relative paths 282 return errors.New("datadir.ancient not abs path") 283 } 284 285 path, _ := filepath.Split(oldAncientPath) 286 if path == "" { 287 return errors.New("prune failed, did not specify the AncientPath") 288 } 289 newAncientPath = filepath.Join(path, "ancient_back") 290 291 blockpruner := pruner.NewBlockPruner(chaindb, stack, oldAncientPath, newAncientPath, blockAmountReserved) 292 293 lock, exist, err := fileutil.Flock(filepath.Join(oldAncientPath, "PRUNEFLOCK")) 294 if err != nil { 295 log.Error("file lock error", "err", err) 296 return err 297 } 298 if exist { 299 defer lock.Release() 300 log.Info("file lock existed, waiting for prune recovery and continue", "err", err) 301 if err := blockpruner.RecoverInterruption("chaindata", config.Eth.DatabaseCache, utils.MakeDatabaseHandles(), "", false); err != nil { 302 log.Error("Pruning failed", "err", err) 303 return err 304 } 305 log.Info("Block prune successfully") 306 return nil 307 } 308 309 if _, err := os.Stat(newAncientPath); err == nil { 310 // No file lock found for old ancientDB but new ancientDB exsisted, indicating the geth was interrupted 311 // after old ancientDB removal, this happened after backup successfully, so just rename the new ancientDB 312 if err := blockpruner.AncientDbReplacer(); err != nil { 313 log.Error("Failed to rename new ancient directory") 314 return err 315 } 316 log.Info("Block prune successfully") 317 return nil 318 } 319 name := "chaindata" 320 if err := blockpruner.BlockPruneBackUp(name, config.Eth.DatabaseCache, utils.MakeDatabaseHandles(), "", false, false); err != nil { 321 log.Error("Failed to back up block", "err", err) 322 return err 323 } 324 325 log.Info("backup block successfully") 326 327 //After backing up successfully, rename the new ancientdb name to the original one, and delete the old ancientdb 328 if err := blockpruner.AncientDbReplacer(); err != nil { 329 return err 330 } 331 332 lock.Release() 333 log.Info("Block prune successfully") 334 return nil 335 } 336 337 func pruneState(ctx *cli.Context) error { 338 stack, config := makeConfigNode(ctx) 339 defer stack.Close() 340 341 chaindb := utils.MakeChainDatabase(ctx, stack, false, false) 342 pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name), ctx.GlobalUint64(utils.TriesInMemoryFlag.Name)) 343 if err != nil { 344 log.Error("Failed to open snapshot tree", "err", err) 345 return err 346 } 347 if ctx.NArg() > 1 { 348 log.Error("Too many arguments given") 349 return errors.New("too many arguments") 350 } 351 var targetRoot common.Hash 352 if ctx.NArg() == 1 { 353 targetRoot, err = parseRoot(ctx.Args()[0]) 354 if err != nil { 355 log.Error("Failed to resolve state root", "err", err) 356 return err 357 } 358 } 359 if err = pruner.Prune(targetRoot); err != nil { 360 log.Error("Failed to prune state", "err", err) 361 return err 362 } 363 return nil 364 } 365 366 func verifyState(ctx *cli.Context) error { 367 stack, _ := makeConfigNode(ctx) 368 defer stack.Close() 369 370 chaindb := utils.MakeChainDatabase(ctx, stack, true, false) 371 headBlock := rawdb.ReadHeadBlock(chaindb) 372 if headBlock == nil { 373 log.Error("Failed to load head block") 374 return errors.New("no head block") 375 } 376 snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, 128, headBlock.Root(), false, false, false) 377 if err != nil { 378 log.Error("Failed to open snapshot tree", "err", err) 379 return err 380 } 381 if ctx.NArg() > 1 { 382 log.Error("Too many arguments given") 383 return errors.New("too many arguments") 384 } 385 var root = headBlock.Root() 386 if ctx.NArg() == 1 { 387 root, err = parseRoot(ctx.Args()[0]) 388 if err != nil { 389 log.Error("Failed to resolve state root", "err", err) 390 return err 391 } 392 } 393 if err := snaptree.Verify(root); err != nil { 394 log.Error("Failed to verfiy state", "root", root, "err", err) 395 return err 396 } 397 log.Info("Verified the state", "root", root) 398 return nil 399 } 400 401 // traverseState is a helper function used for pruning verification. 402 // Basically it just iterates the trie, ensure all nodes and associated 403 // contract codes are present. 404 func traverseState(ctx *cli.Context) error { 405 stack, _ := makeConfigNode(ctx) 406 defer stack.Close() 407 408 chaindb := utils.MakeChainDatabase(ctx, stack, true, false) 409 headBlock := rawdb.ReadHeadBlock(chaindb) 410 if headBlock == nil { 411 log.Error("Failed to load head block") 412 return errors.New("no head block") 413 } 414 if ctx.NArg() > 1 { 415 log.Error("Too many arguments given") 416 return errors.New("too many arguments") 417 } 418 var ( 419 root common.Hash 420 err error 421 ) 422 if ctx.NArg() == 1 { 423 root, err = parseRoot(ctx.Args()[0]) 424 if err != nil { 425 log.Error("Failed to resolve state root", "err", err) 426 return err 427 } 428 log.Info("Start traversing the state", "root", root) 429 } else { 430 root = headBlock.Root() 431 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 432 } 433 triedb := trie.NewDatabase(chaindb) 434 t, err := trie.NewSecure(root, triedb) 435 if err != nil { 436 log.Error("Failed to open trie", "root", root, "err", err) 437 return err 438 } 439 var ( 440 accounts int 441 slots int 442 codes int 443 lastReport time.Time 444 start = time.Now() 445 ) 446 accIter := trie.NewIterator(t.NodeIterator(nil)) 447 for accIter.Next() { 448 accounts += 1 449 var acc state.Account 450 if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil { 451 log.Error("Invalid account encountered during traversal", "err", err) 452 return err 453 } 454 if acc.Root != emptyRoot { 455 storageTrie, err := trie.NewSecure(acc.Root, triedb) 456 if err != nil { 457 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 458 return err 459 } 460 storageIter := trie.NewIterator(storageTrie.NodeIterator(nil)) 461 for storageIter.Next() { 462 slots += 1 463 } 464 if storageIter.Err != nil { 465 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err) 466 return storageIter.Err 467 } 468 } 469 if !bytes.Equal(acc.CodeHash, emptyCode) { 470 code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) 471 if len(code) == 0 { 472 log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) 473 return errors.New("missing code") 474 } 475 codes += 1 476 } 477 if time.Since(lastReport) > time.Second*8 { 478 log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 479 lastReport = time.Now() 480 } 481 } 482 if accIter.Err != nil { 483 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err) 484 return accIter.Err 485 } 486 log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 487 return nil 488 } 489 490 // traverseRawState is a helper function used for pruning verification. 491 // Basically it just iterates the trie, ensure all nodes and associated 492 // contract codes are present. It's basically identical to traverseState 493 // but it will check each trie node. 494 func traverseRawState(ctx *cli.Context) error { 495 stack, _ := makeConfigNode(ctx) 496 defer stack.Close() 497 498 chaindb := utils.MakeChainDatabase(ctx, stack, true, false) 499 headBlock := rawdb.ReadHeadBlock(chaindb) 500 if headBlock == nil { 501 log.Error("Failed to load head block") 502 return errors.New("no head block") 503 } 504 if ctx.NArg() > 1 { 505 log.Error("Too many arguments given") 506 return errors.New("too many arguments") 507 } 508 var ( 509 root common.Hash 510 err error 511 ) 512 if ctx.NArg() == 1 { 513 root, err = parseRoot(ctx.Args()[0]) 514 if err != nil { 515 log.Error("Failed to resolve state root", "err", err) 516 return err 517 } 518 log.Info("Start traversing the state", "root", root) 519 } else { 520 root = headBlock.Root() 521 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 522 } 523 triedb := trie.NewDatabase(chaindb) 524 t, err := trie.NewSecure(root, triedb) 525 if err != nil { 526 log.Error("Failed to open trie", "root", root, "err", err) 527 return err 528 } 529 var ( 530 nodes int 531 accounts int 532 slots int 533 codes int 534 lastReport time.Time 535 start = time.Now() 536 ) 537 accIter := t.NodeIterator(nil) 538 for accIter.Next(true) { 539 nodes += 1 540 node := accIter.Hash() 541 542 if node != (common.Hash{}) { 543 // Check the present for non-empty hash node(embedded node doesn't 544 // have their own hash). 545 blob := rawdb.ReadTrieNode(chaindb, node) 546 if len(blob) == 0 { 547 log.Error("Missing trie node(account)", "hash", node) 548 return errors.New("missing account") 549 } 550 } 551 // If it's a leaf node, yes we are touching an account, 552 // dig into the storage trie further. 553 if accIter.Leaf() { 554 accounts += 1 555 var acc state.Account 556 if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { 557 log.Error("Invalid account encountered during traversal", "err", err) 558 return errors.New("invalid account") 559 } 560 if acc.Root != emptyRoot { 561 storageTrie, err := trie.NewSecure(acc.Root, triedb) 562 if err != nil { 563 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 564 return errors.New("missing storage trie") 565 } 566 storageIter := storageTrie.NodeIterator(nil) 567 for storageIter.Next(true) { 568 nodes += 1 569 node := storageIter.Hash() 570 571 // Check the present for non-empty hash node(embedded node doesn't 572 // have their own hash). 573 if node != (common.Hash{}) { 574 blob := rawdb.ReadTrieNode(chaindb, node) 575 if len(blob) == 0 { 576 log.Error("Missing trie node(storage)", "hash", node) 577 return errors.New("missing storage") 578 } 579 } 580 // Bump the counter if it's leaf node. 581 if storageIter.Leaf() { 582 slots += 1 583 } 584 } 585 if storageIter.Error() != nil { 586 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error()) 587 return storageIter.Error() 588 } 589 } 590 if !bytes.Equal(acc.CodeHash, emptyCode) { 591 code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) 592 if len(code) == 0 { 593 log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) 594 return errors.New("missing code") 595 } 596 codes += 1 597 } 598 if time.Since(lastReport) > time.Second*8 { 599 log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 600 lastReport = time.Now() 601 } 602 } 603 } 604 if accIter.Error() != nil { 605 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error()) 606 return accIter.Error() 607 } 608 log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 609 return nil 610 } 611 612 func parseRoot(input string) (common.Hash, error) { 613 var h common.Hash 614 if err := h.UnmarshalText([]byte(input)); err != nil { 615 return h, err 616 } 617 return h, nil 618 }