cmd/geth: add a verkle subcommand (#25718)
* cmd/geth: add a verkle subcommand * fix copyright year * remove unused command parameters * check that the output file was successfully written to Co-authored-by: Martin Holst Swende <martin@swende.se> * cmd/geth: goimports fix Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
parent
818ff32ff5
commit
9d717167aa
@ -241,6 +241,8 @@ func init() {
|
|||||||
utils.ShowDeprecated,
|
utils.ShowDeprecated,
|
||||||
// See snapshot.go
|
// See snapshot.go
|
||||||
snapshotCommand,
|
snapshotCommand,
|
||||||
|
// See verkle.go
|
||||||
|
verkleCommand,
|
||||||
}
|
}
|
||||||
sort.Sort(cli.CommandsByName(app.Commands))
|
sort.Sort(cli.CommandsByName(app.Commands))
|
||||||
|
|
||||||
|
213
cmd/geth/verkle.go
Normal file
213
cmd/geth/verkle.go
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/gballet/go-verkle"
|
||||||
|
cli "github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
zero [32]byte
|
||||||
|
|
||||||
|
verkleCommand = &cli.Command{
|
||||||
|
Name: "verkle",
|
||||||
|
Usage: "A set of experimental verkle tree management commands",
|
||||||
|
Category: "MISCELLANEOUS COMMANDS",
|
||||||
|
Description: "",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "verify",
|
||||||
|
Usage: "verify the conversion of a MPT into a verkle tree",
|
||||||
|
ArgsUsage: "<root>",
|
||||||
|
Action: verifyVerkle,
|
||||||
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
||||||
|
Description: `
|
||||||
|
geth verkle verify <state-root>
|
||||||
|
This command takes a root commitment and attempts to rebuild the tree.
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dump",
|
||||||
|
Usage: "Dump a verkle tree to a DOT file",
|
||||||
|
ArgsUsage: "<root> <key1> [<key 2> ...]",
|
||||||
|
Action: expandVerkle,
|
||||||
|
Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
|
||||||
|
Description: `
|
||||||
|
geth verkle dump <state-root> <key 1> [<key 2> ...]
|
||||||
|
This command will produce a dot file representing the tree, rooted at <root>.
|
||||||
|
in which key1, key2, ... are expanded.
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// recurse into each child to ensure they can be loaded from the db. The tree isn't rebuilt
|
||||||
|
// (only its nodes are loaded) so there is no need to flush them, the garbage collector should
|
||||||
|
// take care of that for us.
|
||||||
|
func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error {
|
||||||
|
switch node := root.(type) {
|
||||||
|
case *verkle.InternalNode:
|
||||||
|
for i, child := range node.Children() {
|
||||||
|
childC := child.ComputeCommitment().Bytes()
|
||||||
|
|
||||||
|
childS, err := resolver(childC[:])
|
||||||
|
if bytes.Equal(childC[:], zero[:]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not find child %x in db: %w", childC, err)
|
||||||
|
}
|
||||||
|
// depth is set to 0, the tree isn't rebuilt so it's not a problem
|
||||||
|
childN, err := verkle.ParseNode(childS, 0, childC[:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("decode error child %x in db: %w", child.ComputeCommitment().Bytes(), err)
|
||||||
|
}
|
||||||
|
if err := checkChildren(childN, resolver); err != nil {
|
||||||
|
return fmt.Errorf("%x%w", i, err) // write the path to the erroring node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *verkle.LeafNode:
|
||||||
|
// sanity check: ensure at least one value is non-zero
|
||||||
|
|
||||||
|
for i := 0; i < verkle.NodeWidth; i++ {
|
||||||
|
if len(node.Value(i)) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Both balance and nonce are 0")
|
||||||
|
case verkle.Empty:
|
||||||
|
// nothing to do
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type encountered %v", root)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyVerkle(ctx *cli.Context) error {
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
|
||||||
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||||
|
if headBlock == nil {
|
||||||
|
log.Error("Failed to load head block")
|
||||||
|
return errors.New("no head block")
|
||||||
|
}
|
||||||
|
if ctx.NArg() > 1 {
|
||||||
|
log.Error("Too many arguments given")
|
||||||
|
return errors.New("too many arguments")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
rootC common.Hash
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if ctx.NArg() == 1 {
|
||||||
|
rootC, err = parseRoot(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to resolve state root", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Rebuilding the tree", "root", rootC)
|
||||||
|
} else {
|
||||||
|
rootC = headBlock.Root()
|
||||||
|
log.Info("Rebuilding the tree", "root", rootC, "number", headBlock.NumberU64())
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedRoot, err := chaindb.Get(rootC[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkChildren(root, chaindb.Get); err != nil {
|
||||||
|
log.Error("Could not rebuild the tree from the database", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Tree was rebuilt from the database")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandVerkle(ctx *cli.Context) error {
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
|
||||||
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
|
var (
|
||||||
|
rootC common.Hash
|
||||||
|
keylist [][]byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if ctx.NArg() >= 2 {
|
||||||
|
rootC, err = parseRoot(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to resolve state root", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
keylist = make([][]byte, 0, ctx.Args().Len()-1)
|
||||||
|
args := ctx.Args().Slice()
|
||||||
|
for i := range args[1:] {
|
||||||
|
key, err := hex.DecodeString(args[i+1])
|
||||||
|
log.Info("decoded key", "arg", args[i+1], "key", key)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error decoding key #%d: %w", i+1, err)
|
||||||
|
}
|
||||||
|
keylist = append(keylist, key)
|
||||||
|
}
|
||||||
|
log.Info("Rebuilding the tree", "root", rootC)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("usage: %s root key1 [key 2...]", ctx.App.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedRoot, err := chaindb.Get(rootC[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, key := range keylist {
|
||||||
|
log.Info("Reading key", "index", i, "key", keylist[0])
|
||||||
|
root.Get(key, chaindb.Get)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600); err != nil {
|
||||||
|
log.Error("Failed to dump file", "err", err)
|
||||||
|
} else {
|
||||||
|
log.Info("Tree was dumped to file", "file", "dump.dot")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
2
go.mod
2
go.mod
@ -78,10 +78,12 @@ require (
|
|||||||
github.com/aws/smithy-go v1.1.0 // indirect
|
github.com/aws/smithy-go v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
|
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
|
||||||
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
|
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect
|
||||||
|
github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.4.0 // indirect
|
github.com/go-logfmt/logfmt v0.4.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.1 // indirect
|
github.com/go-ole/go-ole v1.2.1 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||||
|
5
go.sum
5
go.sum
@ -86,6 +86,8 @@ github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7 h1:6IrxszG5G+O7zhtkWxq6+unVvnrm1fqV2Pe+T95DUzw=
|
||||||
|
github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||||
@ -134,6 +136,8 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgx
|
|||||||
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
|
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||||
|
github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732 h1:AB7YjNrzlVHsYz06zCULVV2zYCEft82P86dSmtwxKL0=
|
||||||
|
github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732/go.mod h1:o/XfIXWi4/GqbQirfRm5uTbXMG5NpqxkxblnbZ+QM9I=
|
||||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||||
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
@ -569,6 +573,7 @@ golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||||
|
Loading…
Reference in New Issue
Block a user