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