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