github.com/bearnetworkchain/go-bearnetwork@v1.10.19-0.20220604150648-d63890c2e42b/cmd/geth/snapshot.go (about) 1 // Copyright 2021 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 "encoding/json" 22 "errors" 23 "os" 24 "time" 25 26 "github.com/bearnetworkchain/go-bearnetwork/cmd/utils" 27 "github.com/bearnetworkchain/go-bearnetwork/common" 28 "github.com/bearnetworkchain/go-bearnetwork/core/rawdb" 29 "github.com/bearnetworkchain/go-bearnetwork/core/state" 30 "github.com/bearnetworkchain/go-bearnetwork/core/state/pruner" 31 "github.com/bearnetworkchain/go-bearnetwork/core/state/snapshot" 32 "github.com/bearnetworkchain/go-bearnetwork/core/types" 33 "github.com/bearnetworkchain/go-bearnetwork/crypto" 34 "github.com/bearnetworkchain/go-bearnetwork/log" 35 "github.com/bearnetworkchain/go-bearnetwork/rlp" 36 "github.com/bearnetworkchain/go-bearnetwork/trie" 37 cli "gopkg.in/urfave/cli.v1" 38 ) 39 40 var ( 41 // emptyRoot is the known root hash of an empty trie. 42 emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") 43 44 // emptyCode is the known hash of the empty EVM bytecode. 45 emptyCode = crypto.Keccak256(nil) 46 ) 47 48 var ( 49 snapshotCommand = cli.Command{ 50 Name: "snapshot", 51 Usage: "A set of commands based on the snapshot", 52 Category: "MISCELLANEOUS COMMANDS", 53 Description: "", 54 Subcommands: []cli.Command{ 55 { 56 Name: "prune-state", 57 Usage: "Prune stale ethereum state data based on the snapshot", 58 ArgsUsage: "<root>", 59 Action: utils.MigrateFlags(pruneState), 60 Category: "MISCELLANEOUS COMMANDS", 61 Flags: utils.GroupFlags([]cli.Flag{ 62 utils.CacheTrieJournalFlag, 63 utils.BloomFilterSizeFlag, 64 }, utils.NetworkFlags, utils.DatabasePathFlags), 65 Description: ` 66 geth snapshot prune-state <state-root> 67 will prune historical state data with the help of the state snapshot. 68 All trie nodes and contract codes that do not belong to the specified 69 version state will be deleted from the database. After pruning, only 70 two version states are available: genesis and the specific one. 71 72 The default pruning target is the HEAD-127 state. 73 74 WARNING: It's necessary to delete the trie clean cache after the pruning. 75 If you specify another directory for the trie clean cache via "--cache.trie.journal" 76 during the use of Geth, please also specify it here for correct deletion. Otherwise 77 the trie clean cache with default directory will be deleted. 78 `, 79 }, 80 { 81 Name: "verify-state", 82 Usage: "Recalculate state hash based on the snapshot for verification", 83 ArgsUsage: "<root>", 84 Action: utils.MigrateFlags(verifyState), 85 Category: "MISCELLANEOUS COMMANDS", 86 Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), 87 Description: ` 88 geth snapshot verify-state <state-root> 89 will traverse the whole accounts and storages set based on the specified 90 snapshot and recalculate the root hash of state for verification. 91 In other words, this command does the snapshot to trie conversion. 92 `, 93 }, 94 { 95 Name: "check-dangling-storage", 96 Usage: "Check that there is no 'dangling' snap storage", 97 ArgsUsage: "<root>", 98 Action: utils.MigrateFlags(checkDanglingStorage), 99 Category: "MISCELLANEOUS COMMANDS", 100 Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), 101 Description: ` 102 geth snapshot check-dangling-storage <state-root> traverses the snap storage 103 data, and verifies that all snapshot storage data has a corresponding account. 104 `, 105 }, 106 { 107 Name: "traverse-state", 108 Usage: "Traverse the state with given root hash and perform quick verification", 109 ArgsUsage: "<root>", 110 Action: utils.MigrateFlags(traverseState), 111 Category: "MISCELLANEOUS COMMANDS", 112 Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), 113 Description: ` 114 geth snapshot traverse-state <state-root> 115 will traverse the whole state from the given state root and will abort if any 116 referenced trie node or contract code is missing. This command can be used for 117 state integrity verification. The default checking target is the HEAD state. 118 119 It's also usable without snapshot enabled. 120 `, 121 }, 122 { 123 Name: "traverse-rawstate", 124 Usage: "Traverse the state with given root hash and perform detailed verification", 125 ArgsUsage: "<root>", 126 Action: utils.MigrateFlags(traverseRawState), 127 Category: "MISCELLANEOUS COMMANDS", 128 Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), 129 Description: ` 130 geth snapshot traverse-rawstate <state-root> 131 will traverse the whole state from the given root and will abort if any referenced 132 trie node or contract code is missing. This command can be used for state integrity 133 verification. The default checking target is the HEAD state. It's basically identical 134 to traverse-state, but the check granularity is smaller. 135 136 It's also usable without snapshot enabled. 137 `, 138 }, 139 { 140 Name: "dump", 141 Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", 142 ArgsUsage: "[? <blockHash> | <blockNum>]", 143 Action: utils.MigrateFlags(dumpState), 144 Category: "MISCELLANEOUS COMMANDS", 145 Flags: utils.GroupFlags([]cli.Flag{ 146 utils.ExcludeCodeFlag, 147 utils.ExcludeStorageFlag, 148 utils.StartKeyFlag, 149 utils.DumpLimitFlag, 150 }, utils.NetworkFlags, utils.DatabasePathFlags), 151 Description: ` 152 This command is semantically equivalent to 'geth dump', but uses the snapshots 153 as the backend data source, making this command a lot faster. 154 155 The argument is interpreted as block number or hash. If none is provided, the latest 156 block is used. 157 `, 158 }, 159 }, 160 } 161 ) 162 163 func pruneState(ctx *cli.Context) error { 164 stack, config := makeConfigNode(ctx) 165 defer stack.Close() 166 167 chaindb := utils.MakeChainDatabase(ctx, stack, false) 168 pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name)) 169 if err != nil { 170 log.Error("Failed to open snapshot tree", "err", err) 171 return err 172 } 173 if ctx.NArg() > 1 { 174 log.Error("Too many arguments given") 175 return errors.New("too many arguments") 176 } 177 var targetRoot common.Hash 178 if ctx.NArg() == 1 { 179 targetRoot, err = parseRoot(ctx.Args()[0]) 180 if err != nil { 181 log.Error("Failed to resolve state root", "err", err) 182 return err 183 } 184 } 185 if err = pruner.Prune(targetRoot); err != nil { 186 log.Error("Failed to prune state", "err", err) 187 return err 188 } 189 return nil 190 } 191 192 func verifyState(ctx *cli.Context) error { 193 stack, _ := makeConfigNode(ctx) 194 defer stack.Close() 195 196 chaindb := utils.MakeChainDatabase(ctx, stack, true) 197 headBlock := rawdb.ReadHeadBlock(chaindb) 198 if headBlock == nil { 199 log.Error("Failed to load head block") 200 return errors.New("no head block") 201 } 202 snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false) 203 if err != nil { 204 log.Error("Failed to open snapshot tree", "err", err) 205 return err 206 } 207 if ctx.NArg() > 1 { 208 log.Error("Too many arguments given") 209 return errors.New("too many arguments") 210 } 211 var root = headBlock.Root() 212 if ctx.NArg() == 1 { 213 root, err = parseRoot(ctx.Args()[0]) 214 if err != nil { 215 log.Error("Failed to resolve state root", "err", err) 216 return err 217 } 218 } 219 if err := snaptree.Verify(root); err != nil { 220 log.Error("Failed to verify state", "root", root, "err", err) 221 return err 222 } 223 log.Info("Verified the state", "root", root) 224 return snapshot.CheckDanglingStorage(chaindb) 225 } 226 227 // checkDanglingStorage iterates the snap storage data, and verifies that all 228 // storage also has corresponding account data. 229 func checkDanglingStorage(ctx *cli.Context) error { 230 stack, _ := makeConfigNode(ctx) 231 defer stack.Close() 232 233 return snapshot.CheckDanglingStorage(utils.MakeChainDatabase(ctx, stack, true)) 234 } 235 236 // traverseState is a helper function used for pruning verification. 237 // Basically it just iterates the trie, ensure all nodes and associated 238 // contract codes are present. 239 func traverseState(ctx *cli.Context) error { 240 stack, _ := makeConfigNode(ctx) 241 defer stack.Close() 242 243 chaindb := utils.MakeChainDatabase(ctx, stack, true) 244 headBlock := rawdb.ReadHeadBlock(chaindb) 245 if headBlock == nil { 246 log.Error("Failed to load head block") 247 return errors.New("no head block") 248 } 249 if ctx.NArg() > 1 { 250 log.Error("Too many arguments given") 251 return errors.New("too many arguments") 252 } 253 var ( 254 root common.Hash 255 err error 256 ) 257 if ctx.NArg() == 1 { 258 root, err = parseRoot(ctx.Args()[0]) 259 if err != nil { 260 log.Error("Failed to resolve state root", "err", err) 261 return err 262 } 263 log.Info("Start traversing the state", "root", root) 264 } else { 265 root = headBlock.Root() 266 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 267 } 268 triedb := trie.NewDatabase(chaindb) 269 t, err := trie.NewSecure(root, triedb) 270 if err != nil { 271 log.Error("Failed to open trie", "root", root, "err", err) 272 return err 273 } 274 var ( 275 accounts int 276 slots int 277 codes int 278 lastReport time.Time 279 start = time.Now() 280 ) 281 accIter := trie.NewIterator(t.NodeIterator(nil)) 282 for accIter.Next() { 283 accounts += 1 284 var acc types.StateAccount 285 if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil { 286 log.Error("Invalid account encountered during traversal", "err", err) 287 return err 288 } 289 if acc.Root != emptyRoot { 290 storageTrie, err := trie.NewSecure(acc.Root, triedb) 291 if err != nil { 292 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 293 return err 294 } 295 storageIter := trie.NewIterator(storageTrie.NodeIterator(nil)) 296 for storageIter.Next() { 297 slots += 1 298 } 299 if storageIter.Err != nil { 300 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err) 301 return storageIter.Err 302 } 303 } 304 if !bytes.Equal(acc.CodeHash, emptyCode) { 305 if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) { 306 log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) 307 return errors.New("missing code") 308 } 309 codes += 1 310 } 311 if time.Since(lastReport) > time.Second*8 { 312 log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 313 lastReport = time.Now() 314 } 315 } 316 if accIter.Err != nil { 317 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err) 318 return accIter.Err 319 } 320 log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 321 return nil 322 } 323 324 // traverseRawState is a helper function used for pruning verification. 325 // Basically it just iterates the trie, ensure all nodes and associated 326 // contract codes are present. It's basically identical to traverseState 327 // but it will check each trie node. 328 func traverseRawState(ctx *cli.Context) error { 329 stack, _ := makeConfigNode(ctx) 330 defer stack.Close() 331 332 chaindb := utils.MakeChainDatabase(ctx, stack, true) 333 headBlock := rawdb.ReadHeadBlock(chaindb) 334 if headBlock == nil { 335 log.Error("Failed to load head block") 336 return errors.New("no head block") 337 } 338 if ctx.NArg() > 1 { 339 log.Error("Too many arguments given") 340 return errors.New("too many arguments") 341 } 342 var ( 343 root common.Hash 344 err error 345 ) 346 if ctx.NArg() == 1 { 347 root, err = parseRoot(ctx.Args()[0]) 348 if err != nil { 349 log.Error("Failed to resolve state root", "err", err) 350 return err 351 } 352 log.Info("Start traversing the state", "root", root) 353 } else { 354 root = headBlock.Root() 355 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 356 } 357 triedb := trie.NewDatabase(chaindb) 358 t, err := trie.NewSecure(root, triedb) 359 if err != nil { 360 log.Error("Failed to open trie", "root", root, "err", err) 361 return err 362 } 363 var ( 364 nodes int 365 accounts int 366 slots int 367 codes int 368 lastReport time.Time 369 start = time.Now() 370 hasher = crypto.NewKeccakState() 371 got = make([]byte, 32) 372 ) 373 accIter := t.NodeIterator(nil) 374 for accIter.Next(true) { 375 nodes += 1 376 node := accIter.Hash() 377 378 // Check the present for non-empty hash node(embedded node doesn't 379 // have their own hash). 380 if node != (common.Hash{}) { 381 blob := rawdb.ReadTrieNode(chaindb, node) 382 if len(blob) == 0 { 383 log.Error("Missing trie node(account)", "hash", node) 384 return errors.New("missing account") 385 } 386 hasher.Reset() 387 hasher.Write(blob) 388 hasher.Read(got) 389 if !bytes.Equal(got, node.Bytes()) { 390 log.Error("Invalid trie node(account)", "hash", node.Hex(), "value", blob) 391 return errors.New("invalid account node") 392 } 393 } 394 // If it's a leaf node, yes we are touching an account, 395 // dig into the storage trie further. 396 if accIter.Leaf() { 397 accounts += 1 398 var acc types.StateAccount 399 if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { 400 log.Error("Invalid account encountered during traversal", "err", err) 401 return errors.New("invalid account") 402 } 403 if acc.Root != emptyRoot { 404 storageTrie, err := trie.NewSecure(acc.Root, triedb) 405 if err != nil { 406 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 407 return errors.New("missing storage trie") 408 } 409 storageIter := storageTrie.NodeIterator(nil) 410 for storageIter.Next(true) { 411 nodes += 1 412 node := storageIter.Hash() 413 414 // Check the present for non-empty hash node(embedded node doesn't 415 // have their own hash). 416 if node != (common.Hash{}) { 417 blob := rawdb.ReadTrieNode(chaindb, node) 418 if len(blob) == 0 { 419 log.Error("Missing trie node(storage)", "hash", node) 420 return errors.New("missing storage") 421 } 422 hasher.Reset() 423 hasher.Write(blob) 424 hasher.Read(got) 425 if !bytes.Equal(got, node.Bytes()) { 426 log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob) 427 return errors.New("invalid storage node") 428 } 429 } 430 // Bump the counter if it's leaf node. 431 if storageIter.Leaf() { 432 slots += 1 433 } 434 } 435 if storageIter.Error() != nil { 436 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error()) 437 return storageIter.Error() 438 } 439 } 440 if !bytes.Equal(acc.CodeHash, emptyCode) { 441 if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) { 442 log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) 443 return errors.New("missing code") 444 } 445 codes += 1 446 } 447 if time.Since(lastReport) > time.Second*8 { 448 log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 449 lastReport = time.Now() 450 } 451 } 452 } 453 if accIter.Error() != nil { 454 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error()) 455 return accIter.Error() 456 } 457 log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 458 return nil 459 } 460 461 func parseRoot(input string) (common.Hash, error) { 462 var h common.Hash 463 if err := h.UnmarshalText([]byte(input)); err != nil { 464 return h, err 465 } 466 return h, nil 467 } 468 469 func dumpState(ctx *cli.Context) error { 470 stack, _ := makeConfigNode(ctx) 471 defer stack.Close() 472 473 conf, db, root, err := parseDumpConfig(ctx, stack) 474 if err != nil { 475 return err 476 } 477 snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, root, false, false, false) 478 if err != nil { 479 return err 480 } 481 accIt, err := snaptree.AccountIterator(root, common.BytesToHash(conf.Start)) 482 if err != nil { 483 return err 484 } 485 defer accIt.Release() 486 487 log.Info("Snapshot dumping started", "root", root) 488 var ( 489 start = time.Now() 490 logged = time.Now() 491 accounts uint64 492 ) 493 enc := json.NewEncoder(os.Stdout) 494 enc.Encode(struct { 495 Root common.Hash `json:"root"` 496 }{root}) 497 for accIt.Next() { 498 account, err := snapshot.FullAccount(accIt.Account()) 499 if err != nil { 500 return err 501 } 502 da := &state.DumpAccount{ 503 Balance: account.Balance.String(), 504 Nonce: account.Nonce, 505 Root: account.Root, 506 CodeHash: account.CodeHash, 507 SecureKey: accIt.Hash().Bytes(), 508 } 509 if !conf.SkipCode && !bytes.Equal(account.CodeHash, emptyCode) { 510 da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash)) 511 } 512 if !conf.SkipStorage { 513 da.Storage = make(map[common.Hash]string) 514 515 stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{}) 516 if err != nil { 517 return err 518 } 519 for stIt.Next() { 520 da.Storage[stIt.Hash()] = common.Bytes2Hex(stIt.Slot()) 521 } 522 } 523 enc.Encode(da) 524 accounts++ 525 if time.Since(logged) > 8*time.Second { 526 log.Info("Snapshot dumping in progress", "at", accIt.Hash(), "accounts", accounts, 527 "elapsed", common.PrettyDuration(time.Since(start))) 528 logged = time.Now() 529 } 530 if conf.Max > 0 && accounts >= conf.Max { 531 break 532 } 533 } 534 log.Info("Snapshot dumping complete", "accounts", accounts, 535 "elapsed", common.PrettyDuration(time.Since(start))) 536 return nil 537 }