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