github.com/theQRL/go-zond@v0.1.1/cmd/gzond/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/theQRL/go-zond/cmd/utils" 27 "github.com/theQRL/go-zond/common" 28 "github.com/theQRL/go-zond/core/rawdb" 29 "github.com/theQRL/go-zond/core/state" 30 "github.com/theQRL/go-zond/core/state/pruner" 31 "github.com/theQRL/go-zond/core/state/snapshot" 32 "github.com/theQRL/go-zond/core/types" 33 "github.com/theQRL/go-zond/crypto" 34 "github.com/theQRL/go-zond/internal/flags" 35 "github.com/theQRL/go-zond/log" 36 "github.com/theQRL/go-zond/rlp" 37 "github.com/theQRL/go-zond/trie" 38 cli "github.com/urfave/cli/v2" 39 ) 40 41 var ( 42 snapshotCommand = &cli.Command{ 43 Name: "snapshot", 44 Usage: "A set of commands based on the snapshot", 45 Description: "", 46 Subcommands: []*cli.Command{ 47 { 48 Name: "prune-state", 49 Usage: "Prune stale ethereum state data based on the snapshot", 50 ArgsUsage: "<root>", 51 Action: pruneState, 52 Flags: flags.Merge([]cli.Flag{ 53 utils.BloomFilterSizeFlag, 54 }, utils.NetworkFlags, utils.DatabasePathFlags), 55 Description: ` 56 geth snapshot prune-state <state-root> 57 will prune historical state data with the help of the state snapshot. 58 All trie nodes and contract codes that do not belong to the specified 59 version state will be deleted from the database. After pruning, only 60 two version states are available: genesis and the specific one. 61 62 The default pruning target is the HEAD-127 state. 63 64 WARNING: it's only supported in hash mode(--state.scheme=hash)". 65 `, 66 }, 67 { 68 Name: "verify-state", 69 Usage: "Recalculate state hash based on the snapshot for verification", 70 ArgsUsage: "<root>", 71 Action: verifyState, 72 Flags: flags.Merge([]cli.Flag{ 73 utils.StateSchemeFlag, 74 }, utils.NetworkFlags, utils.DatabasePathFlags), 75 Description: ` 76 geth snapshot verify-state <state-root> 77 will traverse the whole accounts and storages set based on the specified 78 snapshot and recalculate the root hash of state for verification. 79 In other words, this command does the snapshot to trie conversion. 80 `, 81 }, 82 { 83 Name: "check-dangling-storage", 84 Usage: "Check that there is no 'dangling' snap storage", 85 ArgsUsage: "<root>", 86 Action: checkDanglingStorage, 87 Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), 88 Description: ` 89 geth snapshot check-dangling-storage <state-root> traverses the snap storage 90 data, and verifies that all snapshot storage data has a corresponding account. 91 `, 92 }, 93 { 94 Name: "inspect-account", 95 Usage: "Check all snapshot layers for the a specific account", 96 ArgsUsage: "<address | hash>", 97 Action: checkAccount, 98 Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), 99 Description: ` 100 geth snapshot inspect-account <address | hash> checks all snapshot layers and prints out 101 information about the specified address. 102 `, 103 }, 104 { 105 Name: "traverse-state", 106 Usage: "Traverse the state with given root hash and perform quick verification", 107 ArgsUsage: "<root>", 108 Action: traverseState, 109 Flags: flags.Merge([]cli.Flag{ 110 utils.StateSchemeFlag, 111 }, utils.NetworkFlags, utils.DatabasePathFlags), 112 Description: ` 113 geth snapshot traverse-state <state-root> 114 will traverse the whole state from the given state root and will abort if any 115 referenced trie node or contract code is missing. This command can be used for 116 state integrity verification. The default checking target is the HEAD state. 117 118 It's also usable without snapshot enabled. 119 `, 120 }, 121 { 122 Name: "traverse-rawstate", 123 Usage: "Traverse the state with given root hash and perform detailed verification", 124 ArgsUsage: "<root>", 125 Action: traverseRawState, 126 Flags: flags.Merge([]cli.Flag{ 127 utils.StateSchemeFlag, 128 }, 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: dumpState, 144 Flags: flags.Merge([]cli.Flag{ 145 utils.ExcludeCodeFlag, 146 utils.ExcludeStorageFlag, 147 utils.StartKeyFlag, 148 utils.DumpLimitFlag, 149 utils.StateSchemeFlag, 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 // Deprecation: this command should be deprecated once the hash-based 164 // scheme is deprecated. 165 func pruneState(ctx *cli.Context) error { 166 stack, _ := makeConfigNode(ctx) 167 defer stack.Close() 168 169 chaindb := utils.MakeChainDatabase(ctx, stack, false) 170 defer chaindb.Close() 171 172 if rawdb.ReadStateScheme(chaindb) != rawdb.HashScheme { 173 log.Crit("Offline pruning is not required for path scheme") 174 } 175 prunerconfig := pruner.Config{ 176 Datadir: stack.ResolvePath(""), 177 BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name), 178 } 179 pruner, err := pruner.NewPruner(chaindb, prunerconfig) 180 if err != nil { 181 log.Error("Failed to open snapshot tree", "err", err) 182 return err 183 } 184 if ctx.NArg() > 1 { 185 log.Error("Too many arguments given") 186 return errors.New("too many arguments") 187 } 188 var targetRoot common.Hash 189 if ctx.NArg() == 1 { 190 targetRoot, err = parseRoot(ctx.Args().First()) 191 if err != nil { 192 log.Error("Failed to resolve state root", "err", err) 193 return err 194 } 195 } 196 if err = pruner.Prune(targetRoot); err != nil { 197 log.Error("Failed to prune state", "err", err) 198 return err 199 } 200 return nil 201 } 202 203 func verifyState(ctx *cli.Context) error { 204 stack, _ := makeConfigNode(ctx) 205 defer stack.Close() 206 207 chaindb := utils.MakeChainDatabase(ctx, stack, true) 208 defer chaindb.Close() 209 210 headBlock := rawdb.ReadHeadBlock(chaindb) 211 if headBlock == nil { 212 log.Error("Failed to load head block") 213 return errors.New("no head block") 214 } 215 triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true) 216 defer triedb.Close() 217 218 snapConfig := snapshot.Config{ 219 CacheSize: 256, 220 Recovery: false, 221 NoBuild: true, 222 AsyncBuild: false, 223 } 224 snaptree, err := snapshot.New(snapConfig, chaindb, triedb, headBlock.Root()) 225 if err != nil { 226 log.Error("Failed to open snapshot tree", "err", err) 227 return err 228 } 229 if ctx.NArg() > 1 { 230 log.Error("Too many arguments given") 231 return errors.New("too many arguments") 232 } 233 var root = headBlock.Root() 234 if ctx.NArg() == 1 { 235 root, err = parseRoot(ctx.Args().First()) 236 if err != nil { 237 log.Error("Failed to resolve state root", "err", err) 238 return err 239 } 240 } 241 if err := snaptree.Verify(root); err != nil { 242 log.Error("Failed to verify state", "root", root, "err", err) 243 return err 244 } 245 log.Info("Verified the state", "root", root) 246 return snapshot.CheckDanglingStorage(chaindb) 247 } 248 249 // checkDanglingStorage iterates the snap storage data, and verifies that all 250 // storage also has corresponding account data. 251 func checkDanglingStorage(ctx *cli.Context) error { 252 stack, _ := makeConfigNode(ctx) 253 defer stack.Close() 254 255 return snapshot.CheckDanglingStorage(utils.MakeChainDatabase(ctx, stack, true)) 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 defer chaindb.Close() 267 268 triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true) 269 defer triedb.Close() 270 271 headBlock := rawdb.ReadHeadBlock(chaindb) 272 if headBlock == nil { 273 log.Error("Failed to load head block") 274 return errors.New("no head block") 275 } 276 if ctx.NArg() > 1 { 277 log.Error("Too many arguments given") 278 return errors.New("too many arguments") 279 } 280 var ( 281 root common.Hash 282 err error 283 ) 284 if ctx.NArg() == 1 { 285 root, err = parseRoot(ctx.Args().First()) 286 if err != nil { 287 log.Error("Failed to resolve state root", "err", err) 288 return err 289 } 290 log.Info("Start traversing the state", "root", root) 291 } else { 292 root = headBlock.Root() 293 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 294 } 295 t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb) 296 if err != nil { 297 log.Error("Failed to open trie", "root", root, "err", err) 298 return err 299 } 300 var ( 301 accounts int 302 slots int 303 codes int 304 lastReport time.Time 305 start = time.Now() 306 ) 307 acctIt, err := t.NodeIterator(nil) 308 if err != nil { 309 log.Error("Failed to open iterator", "root", root, "err", err) 310 return err 311 } 312 accIter := trie.NewIterator(acctIt) 313 for accIter.Next() { 314 accounts += 1 315 var acc types.StateAccount 316 if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil { 317 log.Error("Invalid account encountered during traversal", "err", err) 318 return err 319 } 320 if acc.Root != types.EmptyRootHash { 321 id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root) 322 storageTrie, err := trie.NewStateTrie(id, triedb) 323 if err != nil { 324 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 325 return err 326 } 327 storageIt, err := storageTrie.NodeIterator(nil) 328 if err != nil { 329 log.Error("Failed to open storage iterator", "root", acc.Root, "err", err) 330 return err 331 } 332 storageIter := trie.NewIterator(storageIt) 333 for storageIter.Next() { 334 slots += 1 335 } 336 if storageIter.Err != nil { 337 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err) 338 return storageIter.Err 339 } 340 } 341 if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) { 342 if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) { 343 log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) 344 return errors.New("missing code") 345 } 346 codes += 1 347 } 348 if time.Since(lastReport) > time.Second*8 { 349 log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 350 lastReport = time.Now() 351 } 352 } 353 if accIter.Err != nil { 354 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err) 355 return accIter.Err 356 } 357 log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 358 return nil 359 } 360 361 // traverseRawState is a helper function used for pruning verification. 362 // Basically it just iterates the trie, ensure all nodes and associated 363 // contract codes are present. It's basically identical to traverseState 364 // but it will check each trie node. 365 func traverseRawState(ctx *cli.Context) error { 366 stack, _ := makeConfigNode(ctx) 367 defer stack.Close() 368 369 chaindb := utils.MakeChainDatabase(ctx, stack, true) 370 defer chaindb.Close() 371 372 triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true) 373 defer triedb.Close() 374 375 headBlock := rawdb.ReadHeadBlock(chaindb) 376 if headBlock == nil { 377 log.Error("Failed to load head block") 378 return errors.New("no head block") 379 } 380 if ctx.NArg() > 1 { 381 log.Error("Too many arguments given") 382 return errors.New("too many arguments") 383 } 384 var ( 385 root common.Hash 386 err error 387 ) 388 if ctx.NArg() == 1 { 389 root, err = parseRoot(ctx.Args().First()) 390 if err != nil { 391 log.Error("Failed to resolve state root", "err", err) 392 return err 393 } 394 log.Info("Start traversing the state", "root", root) 395 } else { 396 root = headBlock.Root() 397 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 398 } 399 t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb) 400 if err != nil { 401 log.Error("Failed to open trie", "root", root, "err", err) 402 return err 403 } 404 var ( 405 nodes int 406 accounts int 407 slots int 408 codes int 409 lastReport time.Time 410 start = time.Now() 411 hasher = crypto.NewKeccakState() 412 got = make([]byte, 32) 413 ) 414 accIter, err := t.NodeIterator(nil) 415 if err != nil { 416 log.Error("Failed to open iterator", "root", root, "err", err) 417 return err 418 } 419 reader, err := triedb.Reader(root) 420 if err != nil { 421 log.Error("State is non-existent", "root", root) 422 return nil 423 } 424 for accIter.Next(true) { 425 nodes += 1 426 node := accIter.Hash() 427 428 // Check the present for non-empty hash node(embedded node doesn't 429 // have their own hash). 430 if node != (common.Hash{}) { 431 blob, _ := reader.Node(common.Hash{}, accIter.Path(), node) 432 if len(blob) == 0 { 433 log.Error("Missing trie node(account)", "hash", node) 434 return errors.New("missing account") 435 } 436 hasher.Reset() 437 hasher.Write(blob) 438 hasher.Read(got) 439 if !bytes.Equal(got, node.Bytes()) { 440 log.Error("Invalid trie node(account)", "hash", node.Hex(), "value", blob) 441 return errors.New("invalid account node") 442 } 443 } 444 // If it's a leaf node, yes we are touching an account, 445 // dig into the storage trie further. 446 if accIter.Leaf() { 447 accounts += 1 448 var acc types.StateAccount 449 if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { 450 log.Error("Invalid account encountered during traversal", "err", err) 451 return errors.New("invalid account") 452 } 453 if acc.Root != types.EmptyRootHash { 454 id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root) 455 storageTrie, err := trie.NewStateTrie(id, triedb) 456 if err != nil { 457 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 458 return errors.New("missing storage trie") 459 } 460 storageIter, err := storageTrie.NodeIterator(nil) 461 if err != nil { 462 log.Error("Failed to open storage iterator", "root", acc.Root, "err", err) 463 return err 464 } 465 for storageIter.Next(true) { 466 nodes += 1 467 node := storageIter.Hash() 468 469 // Check the presence for non-empty hash node(embedded node doesn't 470 // have their own hash). 471 if node != (common.Hash{}) { 472 blob, _ := reader.Node(common.BytesToHash(accIter.LeafKey()), storageIter.Path(), node) 473 if len(blob) == 0 { 474 log.Error("Missing trie node(storage)", "hash", node) 475 return errors.New("missing storage") 476 } 477 hasher.Reset() 478 hasher.Write(blob) 479 hasher.Read(got) 480 if !bytes.Equal(got, node.Bytes()) { 481 log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob) 482 return errors.New("invalid storage node") 483 } 484 } 485 // Bump the counter if it's leaf node. 486 if storageIter.Leaf() { 487 slots += 1 488 } 489 } 490 if storageIter.Error() != nil { 491 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error()) 492 return storageIter.Error() 493 } 494 } 495 if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) { 496 if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) { 497 log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) 498 return errors.New("missing code") 499 } 500 codes += 1 501 } 502 if time.Since(lastReport) > time.Second*8 { 503 log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 504 lastReport = time.Now() 505 } 506 } 507 } 508 if accIter.Error() != nil { 509 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error()) 510 return accIter.Error() 511 } 512 log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 513 return nil 514 } 515 516 func parseRoot(input string) (common.Hash, error) { 517 var h common.Hash 518 if err := h.UnmarshalText([]byte(input)); err != nil { 519 return h, err 520 } 521 return h, nil 522 } 523 524 func dumpState(ctx *cli.Context) error { 525 stack, _ := makeConfigNode(ctx) 526 defer stack.Close() 527 528 conf, db, root, err := parseDumpConfig(ctx, stack) 529 if err != nil { 530 return err 531 } 532 triedb := utils.MakeTrieDatabase(ctx, db, false, true) 533 defer triedb.Close() 534 535 snapConfig := snapshot.Config{ 536 CacheSize: 256, 537 Recovery: false, 538 NoBuild: true, 539 AsyncBuild: false, 540 } 541 snaptree, err := snapshot.New(snapConfig, db, triedb, root) 542 if err != nil { 543 return err 544 } 545 accIt, err := snaptree.AccountIterator(root, common.BytesToHash(conf.Start)) 546 if err != nil { 547 return err 548 } 549 defer accIt.Release() 550 551 log.Info("Snapshot dumping started", "root", root) 552 var ( 553 start = time.Now() 554 logged = time.Now() 555 accounts uint64 556 ) 557 enc := json.NewEncoder(os.Stdout) 558 enc.Encode(struct { 559 Root common.Hash `json:"root"` 560 }{root}) 561 for accIt.Next() { 562 account, err := types.FullAccount(accIt.Account()) 563 if err != nil { 564 return err 565 } 566 da := &state.DumpAccount{ 567 Balance: account.Balance.String(), 568 Nonce: account.Nonce, 569 Root: account.Root.Bytes(), 570 CodeHash: account.CodeHash, 571 SecureKey: accIt.Hash().Bytes(), 572 } 573 if !conf.SkipCode && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { 574 da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash)) 575 } 576 if !conf.SkipStorage { 577 da.Storage = make(map[common.Hash]string) 578 579 stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{}) 580 if err != nil { 581 return err 582 } 583 for stIt.Next() { 584 da.Storage[stIt.Hash()] = common.Bytes2Hex(stIt.Slot()) 585 } 586 } 587 enc.Encode(da) 588 accounts++ 589 if time.Since(logged) > 8*time.Second { 590 log.Info("Snapshot dumping in progress", "at", accIt.Hash(), "accounts", accounts, 591 "elapsed", common.PrettyDuration(time.Since(start))) 592 logged = time.Now() 593 } 594 if conf.Max > 0 && accounts >= conf.Max { 595 break 596 } 597 } 598 log.Info("Snapshot dumping complete", "accounts", accounts, 599 "elapsed", common.PrettyDuration(time.Since(start))) 600 return nil 601 } 602 603 // checkAccount iterates the snap data layers, and looks up the given account 604 // across all layers. 605 func checkAccount(ctx *cli.Context) error { 606 if ctx.NArg() != 1 { 607 return errors.New("need <address|hash> arg") 608 } 609 var ( 610 hash common.Hash 611 addr common.Address 612 ) 613 switch arg := ctx.Args().First(); len(arg) { 614 case 40, 42: 615 addr = common.HexToAddress(arg) 616 hash = crypto.Keccak256Hash(addr.Bytes()) 617 case 64, 66: 618 hash = common.HexToHash(arg) 619 default: 620 return errors.New("malformed address or hash") 621 } 622 stack, _ := makeConfigNode(ctx) 623 defer stack.Close() 624 chaindb := utils.MakeChainDatabase(ctx, stack, true) 625 defer chaindb.Close() 626 start := time.Now() 627 log.Info("Checking difflayer journal", "address", addr, "hash", hash) 628 if err := snapshot.CheckJournalAccount(chaindb, hash); err != nil { 629 return err 630 } 631 log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start))) 632 return nil 633 }