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