github.com/juliankolbe/go-ethereum@v1.9.992/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 "errors" 22 "time" 23 24 "github.com/juliankolbe/go-ethereum/cmd/utils" 25 "github.com/juliankolbe/go-ethereum/common" 26 "github.com/juliankolbe/go-ethereum/core/rawdb" 27 "github.com/juliankolbe/go-ethereum/core/state" 28 "github.com/juliankolbe/go-ethereum/core/state/pruner" 29 "github.com/juliankolbe/go-ethereum/core/state/snapshot" 30 "github.com/juliankolbe/go-ethereum/crypto" 31 "github.com/juliankolbe/go-ethereum/log" 32 "github.com/juliankolbe/go-ethereum/rlp" 33 "github.com/juliankolbe/go-ethereum/trie" 34 cli "gopkg.in/urfave/cli.v1" 35 ) 36 37 var ( 38 // emptyRoot is the known root hash of an empty trie. 39 emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") 40 41 // emptyCode is the known hash of the empty EVM bytecode. 42 emptyCode = crypto.Keccak256(nil) 43 ) 44 45 var ( 46 snapshotCommand = cli.Command{ 47 Name: "snapshot", 48 Usage: "A set of commands based on the snapshot", 49 Category: "MISCELLANEOUS COMMANDS", 50 Description: "", 51 Subcommands: []cli.Command{ 52 { 53 Name: "prune-state", 54 Usage: "Prune stale ethereum state data based on the snapshot", 55 ArgsUsage: "<root>", 56 Action: utils.MigrateFlags(pruneState), 57 Category: "MISCELLANEOUS COMMANDS", 58 Flags: []cli.Flag{ 59 utils.DataDirFlag, 60 utils.RopstenFlag, 61 utils.RinkebyFlag, 62 utils.GoerliFlag, 63 utils.CacheTrieJournalFlag, 64 utils.BloomFilterSizeFlag, 65 }, 66 Description: ` 67 geth snapshot prune-state <state-root> 68 will prune historical state data with the help of the state snapshot. 69 All trie nodes and contract codes that do not belong to the specified 70 version state will be deleted from the database. After pruning, only 71 two version states are available: genesis and the specific one. 72 73 The default pruning target is the HEAD-127 state. 74 75 WARNING: It's necessary to delete the trie clean cache after the pruning. 76 If you specify another directory for the trie clean cache via "--cache.trie.journal" 77 during the use of Geth, please also specify it here for correct deletion. Otherwise 78 the trie clean cache with default directory will be deleted. 79 `, 80 }, 81 { 82 Name: "verify-state", 83 Usage: "Recalculate state hash based on the snapshot for verification", 84 ArgsUsage: "<root>", 85 Action: utils.MigrateFlags(verifyState), 86 Category: "MISCELLANEOUS COMMANDS", 87 Flags: []cli.Flag{ 88 utils.DataDirFlag, 89 utils.RopstenFlag, 90 utils.RinkebyFlag, 91 utils.GoerliFlag, 92 }, 93 Description: ` 94 geth snapshot verify-state <state-root> 95 will traverse the whole accounts and storages set based on the specified 96 snapshot and recalculate the root hash of state for verification. 97 In other words, this command does the snapshot to trie conversion. 98 `, 99 }, 100 { 101 Name: "traverse-state", 102 Usage: "Traverse the state with given root hash for verification", 103 ArgsUsage: "<root>", 104 Action: utils.MigrateFlags(traverseState), 105 Category: "MISCELLANEOUS COMMANDS", 106 Flags: []cli.Flag{ 107 utils.DataDirFlag, 108 utils.RopstenFlag, 109 utils.RinkebyFlag, 110 utils.GoerliFlag, 111 }, 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 for verification", 124 ArgsUsage: "<root>", 125 Action: utils.MigrateFlags(traverseRawState), 126 Category: "MISCELLANEOUS COMMANDS", 127 Flags: []cli.Flag{ 128 utils.DataDirFlag, 129 utils.RopstenFlag, 130 utils.RinkebyFlag, 131 utils.GoerliFlag, 132 }, 133 Description: ` 134 geth snapshot traverse-rawstate <state-root> 135 will traverse the whole state from the given root and will abort if any referenced 136 trie node or contract code is missing. This command can be used for state integrity 137 verification. The default checking target is the HEAD state. It's basically identical 138 to traverse-state, but the check granularity is smaller. 139 140 It's also usable without snapshot enabled. 141 `, 142 }, 143 }, 144 } 145 ) 146 147 func pruneState(ctx *cli.Context) error { 148 stack, config := makeConfigNode(ctx) 149 defer stack.Close() 150 151 chain, chaindb := utils.MakeChain(ctx, stack, true) 152 defer chaindb.Close() 153 154 pruner, err := pruner.NewPruner(chaindb, chain.CurrentBlock().Header(), stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name)) 155 if err != nil { 156 log.Error("Failed to open snapshot tree", "error", err) 157 return err 158 } 159 if ctx.NArg() > 1 { 160 log.Error("Too many arguments given") 161 return errors.New("too many arguments") 162 } 163 var targetRoot common.Hash 164 if ctx.NArg() == 1 { 165 targetRoot, err = parseRoot(ctx.Args()[0]) 166 if err != nil { 167 log.Error("Failed to resolve state root", "error", err) 168 return err 169 } 170 } 171 if err = pruner.Prune(targetRoot); err != nil { 172 log.Error("Failed to prune state", "error", err) 173 return err 174 } 175 return nil 176 } 177 178 func verifyState(ctx *cli.Context) error { 179 stack, _ := makeConfigNode(ctx) 180 defer stack.Close() 181 182 chain, chaindb := utils.MakeChain(ctx, stack, true) 183 defer chaindb.Close() 184 185 snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, chain.CurrentBlock().Root(), false, false, false) 186 if err != nil { 187 log.Error("Failed to open snapshot tree", "error", err) 188 return err 189 } 190 if ctx.NArg() > 1 { 191 log.Error("Too many arguments given") 192 return errors.New("too many arguments") 193 } 194 var root = chain.CurrentBlock().Root() 195 if ctx.NArg() == 1 { 196 root, err = parseRoot(ctx.Args()[0]) 197 if err != nil { 198 log.Error("Failed to resolve state root", "error", err) 199 return err 200 } 201 } 202 if err := snaptree.Verify(root); err != nil { 203 log.Error("Failed to verfiy state", "error", err) 204 return err 205 } 206 log.Info("Verified the state") 207 return nil 208 } 209 210 // traverseState is a helper function used for pruning verification. 211 // Basically it just iterates the trie, ensure all nodes and associated 212 // contract codes are present. 213 func traverseState(ctx *cli.Context) error { 214 stack, _ := makeConfigNode(ctx) 215 defer stack.Close() 216 217 chain, chaindb := utils.MakeChain(ctx, stack, true) 218 defer chaindb.Close() 219 220 if ctx.NArg() > 1 { 221 log.Error("Too many arguments given") 222 return errors.New("too many arguments") 223 } 224 // Use the HEAD root as the default 225 head := chain.CurrentBlock() 226 if head == nil { 227 log.Error("Head block is missing") 228 return errors.New("head block is missing") 229 } 230 var ( 231 root common.Hash 232 err error 233 ) 234 if ctx.NArg() == 1 { 235 root, err = parseRoot(ctx.Args()[0]) 236 if err != nil { 237 log.Error("Failed to resolve state root", "error", err) 238 return err 239 } 240 log.Info("Start traversing the state", "root", root) 241 } else { 242 root = head.Root() 243 log.Info("Start traversing the state", "root", root, "number", head.NumberU64()) 244 } 245 triedb := trie.NewDatabase(chaindb) 246 t, err := trie.NewSecure(root, triedb) 247 if err != nil { 248 log.Error("Failed to open trie", "root", root, "error", err) 249 return err 250 } 251 var ( 252 accounts int 253 slots int 254 codes int 255 lastReport time.Time 256 start = time.Now() 257 ) 258 accIter := trie.NewIterator(t.NodeIterator(nil)) 259 for accIter.Next() { 260 accounts += 1 261 var acc state.Account 262 if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil { 263 log.Error("Invalid account encountered during traversal", "error", err) 264 return err 265 } 266 if acc.Root != emptyRoot { 267 storageTrie, err := trie.NewSecure(acc.Root, triedb) 268 if err != nil { 269 log.Error("Failed to open storage trie", "root", acc.Root, "error", err) 270 return err 271 } 272 storageIter := trie.NewIterator(storageTrie.NodeIterator(nil)) 273 for storageIter.Next() { 274 slots += 1 275 } 276 if storageIter.Err != nil { 277 log.Error("Failed to traverse storage trie", "root", acc.Root, "error", storageIter.Err) 278 return storageIter.Err 279 } 280 } 281 if !bytes.Equal(acc.CodeHash, emptyCode) { 282 code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) 283 if len(code) == 0 { 284 log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) 285 return errors.New("missing code") 286 } 287 codes += 1 288 } 289 if time.Since(lastReport) > time.Second*8 { 290 log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 291 lastReport = time.Now() 292 } 293 } 294 if accIter.Err != nil { 295 log.Error("Failed to traverse state trie", "root", root, "error", accIter.Err) 296 return accIter.Err 297 } 298 log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 299 return nil 300 } 301 302 // traverseRawState is a helper function used for pruning verification. 303 // Basically it just iterates the trie, ensure all nodes and associated 304 // contract codes are present. It's basically identical to traverseState 305 // but it will check each trie node. 306 func traverseRawState(ctx *cli.Context) error { 307 stack, _ := makeConfigNode(ctx) 308 defer stack.Close() 309 310 chain, chaindb := utils.MakeChain(ctx, stack, true) 311 defer chaindb.Close() 312 313 if ctx.NArg() > 1 { 314 log.Error("Too many arguments given") 315 return errors.New("too many arguments") 316 } 317 // Use the HEAD root as the default 318 head := chain.CurrentBlock() 319 if head == nil { 320 log.Error("Head block is missing") 321 return errors.New("head block is missing") 322 } 323 var ( 324 root common.Hash 325 err error 326 ) 327 if ctx.NArg() == 1 { 328 root, err = parseRoot(ctx.Args()[0]) 329 if err != nil { 330 log.Error("Failed to resolve state root", "error", err) 331 return err 332 } 333 log.Info("Start traversing the state", "root", root) 334 } else { 335 root = head.Root() 336 log.Info("Start traversing the state", "root", root, "number", head.NumberU64()) 337 } 338 triedb := trie.NewDatabase(chaindb) 339 t, err := trie.NewSecure(root, triedb) 340 if err != nil { 341 log.Error("Failed to open trie", "root", root, "error", err) 342 return err 343 } 344 var ( 345 nodes int 346 accounts int 347 slots int 348 codes int 349 lastReport time.Time 350 start = time.Now() 351 ) 352 accIter := t.NodeIterator(nil) 353 for accIter.Next(true) { 354 nodes += 1 355 node := accIter.Hash() 356 357 if node != (common.Hash{}) { 358 // Check the present for non-empty hash node(embedded node doesn't 359 // have their own hash). 360 blob := rawdb.ReadTrieNode(chaindb, node) 361 if len(blob) == 0 { 362 log.Error("Missing trie node(account)", "hash", node) 363 return errors.New("missing account") 364 } 365 } 366 // If it's a leaf node, yes we are touching an account, 367 // dig into the storage trie further. 368 if accIter.Leaf() { 369 accounts += 1 370 var acc state.Account 371 if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil { 372 log.Error("Invalid account encountered during traversal", "error", err) 373 return errors.New("invalid account") 374 } 375 if acc.Root != emptyRoot { 376 storageTrie, err := trie.NewSecure(acc.Root, triedb) 377 if err != nil { 378 log.Error("Failed to open storage trie", "root", acc.Root, "error", err) 379 return errors.New("missing storage trie") 380 } 381 storageIter := storageTrie.NodeIterator(nil) 382 for storageIter.Next(true) { 383 nodes += 1 384 node := storageIter.Hash() 385 386 // Check the present for non-empty hash node(embedded node doesn't 387 // have their own hash). 388 if node != (common.Hash{}) { 389 blob := rawdb.ReadTrieNode(chaindb, node) 390 if len(blob) == 0 { 391 log.Error("Missing trie node(storage)", "hash", node) 392 return errors.New("missing storage") 393 } 394 } 395 // Bump the counter if it's leaf node. 396 if storageIter.Leaf() { 397 slots += 1 398 } 399 } 400 if storageIter.Error() != nil { 401 log.Error("Failed to traverse storage trie", "root", acc.Root, "error", storageIter.Error()) 402 return storageIter.Error() 403 } 404 } 405 if !bytes.Equal(acc.CodeHash, emptyCode) { 406 code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) 407 if len(code) == 0 { 408 log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) 409 return errors.New("missing code") 410 } 411 codes += 1 412 } 413 if time.Since(lastReport) > time.Second*8 { 414 log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 415 lastReport = time.Now() 416 } 417 } 418 } 419 if accIter.Error() != nil { 420 log.Error("Failed to traverse state trie", "root", root, "error", accIter.Error()) 421 return accIter.Error() 422 } 423 log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 424 return nil 425 } 426 427 func parseRoot(input string) (common.Hash, error) { 428 var h common.Hash 429 if err := h.UnmarshalText([]byte(input)); err != nil { 430 return h, err 431 } 432 return h, nil 433 }