github.com/phillinzzz/newBsc@v1.1.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/phillinzzz/newBsc/cmd/utils" 25 "github.com/phillinzzz/newBsc/common" 26 "github.com/phillinzzz/newBsc/core/rawdb" 27 "github.com/phillinzzz/newBsc/core/state" 28 "github.com/phillinzzz/newBsc/core/state/pruner" 29 "github.com/phillinzzz/newBsc/core/state/snapshot" 30 "github.com/phillinzzz/newBsc/crypto" 31 "github.com/phillinzzz/newBsc/log" 32 "github.com/phillinzzz/newBsc/rlp" 33 "github.com/phillinzzz/newBsc/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.AncientFlag, 61 utils.RopstenFlag, 62 utils.RinkebyFlag, 63 utils.GoerliFlag, 64 utils.CacheTrieJournalFlag, 65 utils.BloomFilterSizeFlag, 66 utils.TriesInMemoryFlag, 67 }, 68 Description: ` 69 geth snapshot prune-state <state-root> 70 will prune historical state data with the help of the state snapshot. 71 All trie nodes and contract codes that do not belong to the specified 72 version state will be deleted from the database. After pruning, only 73 two version states are available: genesis and the specific one. 74 75 The default pruning target is the HEAD-127 state. 76 77 WARNING: It's necessary to delete the trie clean cache after the pruning. 78 If you specify another directory for the trie clean cache via "--cache.trie.journal" 79 during the use of Geth, please also specify it here for correct deletion. Otherwise 80 the trie clean cache with default directory will be deleted. 81 `, 82 }, 83 { 84 Name: "verify-state", 85 Usage: "Recalculate state hash based on the snapshot for verification", 86 ArgsUsage: "<root>", 87 Action: utils.MigrateFlags(verifyState), 88 Category: "MISCELLANEOUS COMMANDS", 89 Flags: []cli.Flag{ 90 utils.DataDirFlag, 91 utils.AncientFlag, 92 utils.RopstenFlag, 93 utils.RinkebyFlag, 94 utils.GoerliFlag, 95 }, 96 Description: ` 97 geth snapshot verify-state <state-root> 98 will traverse the whole accounts and storages set based on the specified 99 snapshot and recalculate the root hash of state for verification. 100 In other words, this command does the snapshot to trie conversion. 101 `, 102 }, 103 { 104 Name: "traverse-state", 105 Usage: "Traverse the state with given root hash for verification", 106 ArgsUsage: "<root>", 107 Action: utils.MigrateFlags(traverseState), 108 Category: "MISCELLANEOUS COMMANDS", 109 Flags: []cli.Flag{ 110 utils.DataDirFlag, 111 utils.AncientFlag, 112 utils.RopstenFlag, 113 utils.RinkebyFlag, 114 utils.GoerliFlag, 115 }, 116 Description: ` 117 geth snapshot traverse-state <state-root> 118 will traverse the whole state from the given state root and will abort if any 119 referenced trie node or contract code is missing. This command can be used for 120 state integrity verification. The default checking target is the HEAD state. 121 122 It's also usable without snapshot enabled. 123 `, 124 }, 125 { 126 Name: "traverse-rawstate", 127 Usage: "Traverse the state with given root hash for verification", 128 ArgsUsage: "<root>", 129 Action: utils.MigrateFlags(traverseRawState), 130 Category: "MISCELLANEOUS COMMANDS", 131 Flags: []cli.Flag{ 132 utils.DataDirFlag, 133 utils.AncientFlag, 134 utils.RopstenFlag, 135 utils.RinkebyFlag, 136 utils.GoerliFlag, 137 }, 138 Description: ` 139 geth snapshot traverse-rawstate <state-root> 140 will traverse the whole state from the given root and will abort if any referenced 141 trie node or contract code is missing. This command can be used for state integrity 142 verification. The default checking target is the HEAD state. It's basically identical 143 to traverse-state, but the check granularity is smaller. 144 145 It's also usable without snapshot enabled. 146 `, 147 }, 148 }, 149 } 150 ) 151 152 func pruneState(ctx *cli.Context) error { 153 stack, config := makeConfigNode(ctx) 154 defer stack.Close() 155 156 chaindb := utils.MakeChainDatabase(ctx, stack, false) 157 pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name), ctx.GlobalUint64(utils.TriesInMemoryFlag.Name)) 158 if err != nil { 159 log.Error("Failed to open snapshot tree", "err", err) 160 return err 161 } 162 if ctx.NArg() > 1 { 163 log.Error("Too many arguments given") 164 return errors.New("too many arguments") 165 } 166 var targetRoot common.Hash 167 if ctx.NArg() == 1 { 168 targetRoot, err = parseRoot(ctx.Args()[0]) 169 if err != nil { 170 log.Error("Failed to resolve state root", "err", err) 171 return err 172 } 173 } 174 if err = pruner.Prune(targetRoot); err != nil { 175 log.Error("Failed to prune state", "err", err) 176 return err 177 } 178 return nil 179 } 180 181 func verifyState(ctx *cli.Context) error { 182 stack, _ := makeConfigNode(ctx) 183 defer stack.Close() 184 185 chaindb := utils.MakeChainDatabase(ctx, stack, true) 186 headBlock := rawdb.ReadHeadBlock(chaindb) 187 if headBlock == nil { 188 log.Error("Failed to load head block") 189 return errors.New("no head block") 190 } 191 snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, 128, headBlock.Root(), false, false, false) 192 if err != nil { 193 log.Error("Failed to open snapshot tree", "err", err) 194 return err 195 } 196 if ctx.NArg() > 1 { 197 log.Error("Too many arguments given") 198 return errors.New("too many arguments") 199 } 200 var root = headBlock.Root() 201 if ctx.NArg() == 1 { 202 root, err = parseRoot(ctx.Args()[0]) 203 if err != nil { 204 log.Error("Failed to resolve state root", "err", err) 205 return err 206 } 207 } 208 if err := snaptree.Verify(root); err != nil { 209 log.Error("Failed to verfiy state", "root", root, "err", err) 210 return err 211 } 212 log.Info("Verified the state", "root", root) 213 return nil 214 } 215 216 // traverseState is a helper function used for pruning verification. 217 // Basically it just iterates the trie, ensure all nodes and associated 218 // contract codes are present. 219 func traverseState(ctx *cli.Context) error { 220 stack, _ := makeConfigNode(ctx) 221 defer stack.Close() 222 223 chaindb := utils.MakeChainDatabase(ctx, stack, true) 224 headBlock := rawdb.ReadHeadBlock(chaindb) 225 if headBlock == nil { 226 log.Error("Failed to load head block") 227 return errors.New("no head block") 228 } 229 if ctx.NArg() > 1 { 230 log.Error("Too many arguments given") 231 return errors.New("too many arguments") 232 } 233 var ( 234 root common.Hash 235 err error 236 ) 237 if ctx.NArg() == 1 { 238 root, err = parseRoot(ctx.Args()[0]) 239 if err != nil { 240 log.Error("Failed to resolve state root", "err", err) 241 return err 242 } 243 log.Info("Start traversing the state", "root", root) 244 } else { 245 root = headBlock.Root() 246 log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64()) 247 } 248 triedb := trie.NewDatabase(chaindb) 249 t, err := trie.NewSecure(root, triedb) 250 if err != nil { 251 log.Error("Failed to open trie", "root", root, "err", err) 252 return err 253 } 254 var ( 255 accounts int 256 slots int 257 codes int 258 lastReport time.Time 259 start = time.Now() 260 ) 261 accIter := trie.NewIterator(t.NodeIterator(nil)) 262 for accIter.Next() { 263 accounts += 1 264 var acc state.Account 265 if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil { 266 log.Error("Invalid account encountered during traversal", "err", err) 267 return err 268 } 269 if acc.Root != emptyRoot { 270 storageTrie, err := trie.NewSecure(acc.Root, triedb) 271 if err != nil { 272 log.Error("Failed to open storage trie", "root", acc.Root, "err", err) 273 return err 274 } 275 storageIter := trie.NewIterator(storageTrie.NodeIterator(nil)) 276 for storageIter.Next() { 277 slots += 1 278 } 279 if storageIter.Err != nil { 280 log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err) 281 return storageIter.Err 282 } 283 } 284 if !bytes.Equal(acc.CodeHash, emptyCode) { 285 code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash)) 286 if len(code) == 0 { 287 log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) 288 return errors.New("missing code") 289 } 290 codes += 1 291 } 292 if time.Since(lastReport) > time.Second*8 { 293 log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 294 lastReport = time.Now() 295 } 296 } 297 if accIter.Err != nil { 298 log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err) 299 return accIter.Err 300 } 301 log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start))) 302 return nil 303 } 304 305 // traverseRawState is a helper function used for pruning verification. 306 // Basically it just iterates the trie, ensure all nodes and associated 307 // contract codes are present. It's basically identical to traverseState 308 // but it will check each trie node. 309 func traverseRawState(ctx *cli.Context) error { 310 stack, _ := makeConfigNode(ctx) 311 defer stack.Close() 312 313 chaindb := utils.MakeChainDatabase(ctx, stack, true) 314 headBlock := rawdb.ReadHeadBlock(chaindb) 315 if headBlock == nil { 316 log.Error("Failed to load head block") 317 return errors.New("no head block") 318 } 319 if ctx.NArg() > 1 { 320 log.Error("Too many arguments given") 321 return errors.New("too many arguments") 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", "err", err) 331 return err 332 } 333 log.Info("Start traversing the state", "root", root) 334 } else { 335 root = headBlock.Root() 336 log.Info("Start traversing the state", "root", root, "number", headBlock.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, "err", 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", "err", 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, "err", 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, "err", 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, "err", 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 }