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