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