From 2b4955c6c2f8c3bb7354eb3b22796b1768300a7b Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 8 Sep 2022 09:11:21 +0200 Subject: [PATCH 1/5] cmd/geth: add a verkle subcommand --- cmd/geth/main.go | 2 + cmd/geth/verkle.go | 227 +++++++++++++++++++++++++++++++++++++++++++++ cmd/utils/flags.go | 13 +++ go.mod | 2 + go.sum | 5 + 5 files changed, 249 insertions(+) create mode 100644 cmd/geth/verkle.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index b9e3ed31e813..70b354ae148b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -241,6 +241,8 @@ func init() { utils.ShowDeprecated, // See snapshot.go snapshotCommand, + // See verkle.go + verkleCommand, } sort.Sort(cli.CommandsByName(app.Commands)) diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go new file mode 100644 index 000000000000..99288aaf3876 --- /dev/null +++ b/cmd/geth/verkle.go @@ -0,0 +1,227 @@ +// Copyright 2020 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 . + +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: "", + Action: verifyVerkle, + Flags: flags.Merge([]cli.Flag{ + utils.VerkleConversionInsertRangeStartFlag, + utils.VerkleConversionInsertRangeSizeFlag, + }, utils.NetworkFlags, utils.DatabasePathFlags), + Description: ` +geth verkle verify +This command takes a root commitment and attempts to rebuild the tree. + `, + }, + { + Name: "dump", + Usage: "Dump a verkle tree to a DOT file", + ArgsUsage: " [ ...]", + Action: expandVerkle, + Flags: flags.Merge([]cli.Flag{ + utils.VerkleConversionInsertRangeStartFlag, + utils.VerkleConversionInsertRangeSizeFlag, + }, utils.NetworkFlags, utils.DatabasePathFlags), + Description: ` +geth verkle dump [ ...] +This command will produce a dot file representing the tree, rooted at . +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()) + } + + var ( + //start = time.Now() + rangeStart = ctx.Uint64(utils.VerkleConversionInsertRangeStartFlag.Name) + rangeEnd = rangeStart + ctx.Uint64(utils.VerkleConversionInsertRangeSizeFlag.Name) + ) + + if rangeEnd > 256 { + rangeEnd = 256 + } + + 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() > 1 { + 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) + } + + os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600) + + log.Info("Tree was dumped to file") + return nil +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fb2aa7c21587..91658095336f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -976,6 +976,19 @@ var ( Value: metrics.DefaultConfig.InfluxDBOrganization, Category: flags.MetricsCategory, } + + // Verkle-related flags + VerkleConversionInsertRangeStartFlag = &cli.Uint64Flag{ + Name: "range.start", + Usage: "Starting byte of the key insertion/verification range.", + Value: 0, + } + // Verkle-related flags + VerkleConversionInsertRangeSizeFlag = &cli.Uint64Flag{ + Name: "range.size", + Usage: "Number of prefix bytes in the key insertion/verification range.", + Value: 256, + } ) var ( diff --git a/go.mod b/go.mod index 4a769c7a2dca..72613343948e 100644 --- a/go.mod +++ b/go.mod @@ -78,10 +78,12 @@ require ( github.com/aws/smithy-go v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.1 // 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/deepmap/oapi-codegen v1.8.2 // indirect github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // 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-ole/go-ole v1.2.1 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect diff --git a/go.sum b/go.sum index 4b27867fbc79..72d9b25e2021 100644 --- a/go.sum +++ b/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.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 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/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= @@ -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/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-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.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= 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-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-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-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= From 49a7da2a69407982432c31b59162a3ac7186bb2e Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 8 Sep 2022 09:16:57 +0200 Subject: [PATCH 2/5] fix copyright year --- cmd/geth/verkle.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index 99288aaf3876..d5dce5dc1521 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -1,4 +1,4 @@ -// Copyright 2020 The go-ethereum Authors +// 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 From 7a25e1d848288facd80804a019988944321f75fd Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 8 Sep 2022 10:42:51 +0200 Subject: [PATCH 3/5] remove unused command parameters --- cmd/geth/verkle.go | 22 +++------------------- cmd/utils/flags.go | 13 ------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index d5dce5dc1521..655eb10efba0 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -46,10 +46,7 @@ var ( Usage: "verify the conversion of a MPT into a verkle tree", ArgsUsage: "", Action: verifyVerkle, - Flags: flags.Merge([]cli.Flag{ - utils.VerkleConversionInsertRangeStartFlag, - utils.VerkleConversionInsertRangeSizeFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth verkle verify This command takes a root commitment and attempts to rebuild the tree. @@ -60,10 +57,7 @@ This command takes a root commitment and attempts to rebuild the tree. Usage: "Dump a verkle tree to a DOT file", ArgsUsage: " [ ...]", Action: expandVerkle, - Flags: flags.Merge([]cli.Flag{ - utils.VerkleConversionInsertRangeStartFlag, - utils.VerkleConversionInsertRangeSizeFlag, - }, utils.NetworkFlags, utils.DatabasePathFlags), + Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth verkle dump [ ...] This command will produce a dot file representing the tree, rooted at . @@ -147,16 +141,6 @@ func verifyVerkle(ctx *cli.Context) error { log.Info("Rebuilding the tree", "root", rootC, "number", headBlock.NumberU64()) } - var ( - //start = time.Now() - rangeStart = ctx.Uint64(utils.VerkleConversionInsertRangeStartFlag.Name) - rangeEnd = rangeStart + ctx.Uint64(utils.VerkleConversionInsertRangeSizeFlag.Name) - ) - - if rangeEnd > 256 { - rangeEnd = 256 - } - serializedRoot, err := chaindb.Get(rootC[:]) if err != nil { return err @@ -185,7 +169,7 @@ func expandVerkle(ctx *cli.Context) error { keylist [][]byte err error ) - if ctx.NArg() > 1 { + if ctx.NArg() >= 2 { rootC, err = parseRoot(ctx.Args().First()) if err != nil { log.Error("Failed to resolve state root", "error", err) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 91658095336f..fb2aa7c21587 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -976,19 +976,6 @@ var ( Value: metrics.DefaultConfig.InfluxDBOrganization, Category: flags.MetricsCategory, } - - // Verkle-related flags - VerkleConversionInsertRangeStartFlag = &cli.Uint64Flag{ - Name: "range.start", - Usage: "Starting byte of the key insertion/verification range.", - Value: 0, - } - // Verkle-related flags - VerkleConversionInsertRangeSizeFlag = &cli.Uint64Flag{ - Name: "range.size", - Usage: "Number of prefix bytes in the key insertion/verification range.", - Value: 256, - } ) var ( From 64c8bccd51570a5023753725042ba845896bf8fb Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 9 Sep 2022 18:39:05 +0200 Subject: [PATCH 4/5] check that the output file was successfully written to Co-authored-by: Martin Holst Swende --- cmd/geth/verkle.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index 655eb10efba0..9bc7eadb5687 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -204,8 +204,10 @@ func expandVerkle(ctx *cli.Context) error { root.Get(key, chaindb.Get) } - os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600) - - log.Info("Tree was dumped to file") + 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 } From ea2a962d17382209136796446b6a9aaace15b75f Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 13 Sep 2022 18:13:13 +0200 Subject: [PATCH 5/5] cmd/geth: goimports fix --- cmd/geth/verkle.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index 9bc7eadb5687..f85ec37ea924 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -204,9 +204,9 @@ func expandVerkle(ctx *cli.Context) error { root.Get(key, chaindb.Get) } - if err := os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600); err != nil{ + if err := os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600); err != nil { log.Error("Failed to dump file", "err", err) - } else{ + } else { log.Info("Tree was dumped to file", "file", "dump.dot") } return nil