Skip to content

Commit c9974eb

Browse files
holimanjagdeep sidhu
authored andcommitted
cmd/geth: inspect snapshot dangling storage (ethereum#24643)
* cmd/geth: inspect snapshot dangling storage * cmd/geth: make verify-state invoke verify-dangling
1 parent e12dedc commit c9974eb

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

cmd/geth/snapshot.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"bytes"
2121
"encoding/json"
2222
"errors"
23+
"fmt"
2324
"os"
2425
"time"
2526

@@ -31,6 +32,7 @@ import (
3132
"github.com/ethereum/go-ethereum/core/state/snapshot"
3233
"github.com/ethereum/go-ethereum/core/types"
3334
"github.com/ethereum/go-ethereum/crypto"
35+
"github.com/ethereum/go-ethereum/ethdb"
3436
"github.com/ethereum/go-ethereum/log"
3537
"github.com/ethereum/go-ethereum/rlp"
3638
"github.com/ethereum/go-ethereum/trie"
@@ -102,6 +104,25 @@ geth snapshot verify-state <state-root>
102104
will traverse the whole accounts and storages set based on the specified
103105
snapshot and recalculate the root hash of state for verification.
104106
In other words, this command does the snapshot to trie conversion.
107+
`,
108+
},
109+
{
110+
Name: "check-dangling-storage",
111+
Usage: "Check that there is no 'dangling' snap storage",
112+
ArgsUsage: "<root>",
113+
Action: utils.MigrateFlags(checkDanglingStorage),
114+
Category: "MISCELLANEOUS COMMANDS",
115+
Flags: []cli.Flag{
116+
utils.DataDirFlag,
117+
utils.AncientFlag,
118+
utils.RopstenFlag,
119+
utils.SepoliaFlag,
120+
utils.RinkebyFlag,
121+
utils.GoerliFlag,
122+
},
123+
Description: `
124+
geth snapshot check-dangling-storage <state-root> traverses the snap storage
125+
data, and verifies that all snapshot storage data has a corresponding account.
105126
`,
106127
},
107128
{
@@ -242,6 +263,77 @@ func verifyState(ctx *cli.Context) error {
242263
return err
243264
}
244265
log.Info("Verified the state", "root", root)
266+
if err := checkDangling(chaindb, snaptree.Snapshot(root)); err != nil {
267+
log.Error("Dangling snap storage check failed", "root", root, "err", err)
268+
return err
269+
}
270+
return nil
271+
}
272+
273+
// checkDanglingStorage iterates the snap storage data, and verifies that all
274+
// storage also has corresponding account data.
275+
func checkDanglingStorage(ctx *cli.Context) error {
276+
stack, _ := makeConfigNode(ctx)
277+
defer stack.Close()
278+
279+
chaindb := utils.MakeChainDatabase(ctx, stack, true)
280+
headBlock := rawdb.ReadHeadBlock(chaindb)
281+
if headBlock == nil {
282+
log.Error("Failed to load head block")
283+
return errors.New("no head block")
284+
}
285+
snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false)
286+
if err != nil {
287+
log.Error("Failed to open snapshot tree", "err", err)
288+
return err
289+
}
290+
if ctx.NArg() > 1 {
291+
log.Error("Too many arguments given")
292+
return errors.New("too many arguments")
293+
}
294+
var root = headBlock.Root()
295+
if ctx.NArg() == 1 {
296+
root, err = parseRoot(ctx.Args()[0])
297+
if err != nil {
298+
log.Error("Failed to resolve state root", "err", err)
299+
return err
300+
}
301+
}
302+
return checkDangling(chaindb, snaptree.Snapshot(root))
303+
}
304+
305+
func checkDangling(chaindb ethdb.Database, snap snapshot.Snapshot) error {
306+
log.Info("Checking dangling snapshot storage")
307+
var (
308+
lastReport = time.Now()
309+
start = time.Now()
310+
lastKey []byte
311+
it = rawdb.NewKeyLengthIterator(chaindb.NewIterator(rawdb.SnapshotStoragePrefix, nil), 1+2*common.HashLength)
312+
)
313+
defer it.Release()
314+
for it.Next() {
315+
k := it.Key()
316+
accKey := k[1:33]
317+
if bytes.Equal(accKey, lastKey) {
318+
// No need to look up for every slot
319+
continue
320+
}
321+
lastKey = common.CopyBytes(accKey)
322+
if time.Since(lastReport) > time.Second*8 {
323+
log.Info("Iterating snap storage", "at", fmt.Sprintf("%#x", accKey), "elapsed", common.PrettyDuration(time.Since(start)))
324+
lastReport = time.Now()
325+
}
326+
data, err := snap.AccountRLP(common.BytesToHash(accKey))
327+
if err != nil {
328+
log.Error("Error loading snap storage data", "account", fmt.Sprintf("%#x", accKey), "err", err)
329+
return err
330+
}
331+
if len(data) == 0 {
332+
log.Error("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accKey), "storagekey", fmt.Sprintf("%#x", k))
333+
return fmt.Errorf("dangling snapshot storage account %#x", accKey)
334+
}
335+
}
336+
log.Info("Verified the snapshot storage", "root", snap.Root(), "time", common.PrettyDuration(time.Since(start)), "err", it.Error())
245337
return nil
246338
}
247339

0 commit comments

Comments
 (0)