github.com/calmw/ethereum@v0.1.1/cmd/geth/verkle.go (about)

     1  // Copyright 2022 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  	"encoding/hex"
    22  	"errors"
    23  	"fmt"
    24  	"os"
    25  
    26  	"github.com/calmw/ethereum/cmd/utils"
    27  	"github.com/calmw/ethereum/common"
    28  	"github.com/calmw/ethereum/core/rawdb"
    29  	"github.com/calmw/ethereum/internal/flags"
    30  	"github.com/calmw/ethereum/log"
    31  	"github.com/gballet/go-verkle"
    32  	cli "github.com/urfave/cli/v2"
    33  )
    34  
    35  var (
    36  	zero [32]byte
    37  
    38  	verkleCommand = &cli.Command{
    39  		Name:        "verkle",
    40  		Usage:       "A set of experimental verkle tree management commands",
    41  		Description: "",
    42  		Subcommands: []*cli.Command{
    43  			{
    44  				Name:      "verify",
    45  				Usage:     "verify the conversion of a MPT into a verkle tree",
    46  				ArgsUsage: "<root>",
    47  				Action:    verifyVerkle,
    48  				Flags:     flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
    49  				Description: `
    50  geth verkle verify <state-root>
    51  This command takes a root commitment and attempts to rebuild the tree.
    52   `,
    53  			},
    54  			{
    55  				Name:      "dump",
    56  				Usage:     "Dump a verkle tree to a DOT file",
    57  				ArgsUsage: "<root> <key1> [<key 2> ...]",
    58  				Action:    expandVerkle,
    59  				Flags:     flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
    60  				Description: `
    61  geth verkle dump <state-root> <key 1> [<key 2> ...]
    62  This command will produce a dot file representing the tree, rooted at <root>.
    63  in which key1, key2, ... are expanded.
    64   `,
    65  			},
    66  		},
    67  	}
    68  )
    69  
    70  // recurse into each child to ensure they can be loaded from the db. The tree isn't rebuilt
    71  // (only its nodes are loaded) so there is no need to flush them, the garbage collector should
    72  // take care of that for us.
    73  func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error {
    74  	switch node := root.(type) {
    75  	case *verkle.InternalNode:
    76  		for i, child := range node.Children() {
    77  			childC := child.ComputeCommitment().Bytes()
    78  
    79  			childS, err := resolver(childC[:])
    80  			if bytes.Equal(childC[:], zero[:]) {
    81  				continue
    82  			}
    83  			if err != nil {
    84  				return fmt.Errorf("could not find child %x in db: %w", childC, err)
    85  			}
    86  			// depth is set to 0, the tree isn't rebuilt so it's not a problem
    87  			childN, err := verkle.ParseNode(childS, 0, childC[:])
    88  			if err != nil {
    89  				return fmt.Errorf("decode error child %x in db: %w", child.ComputeCommitment().Bytes(), err)
    90  			}
    91  			if err := checkChildren(childN, resolver); err != nil {
    92  				return fmt.Errorf("%x%w", i, err) // write the path to the erroring node
    93  			}
    94  		}
    95  	case *verkle.LeafNode:
    96  		// sanity check: ensure at least one value is non-zero
    97  
    98  		for i := 0; i < verkle.NodeWidth; i++ {
    99  			if len(node.Value(i)) != 0 {
   100  				return nil
   101  			}
   102  		}
   103  		return fmt.Errorf("Both balance and nonce are 0")
   104  	case verkle.Empty:
   105  		// nothing to do
   106  	default:
   107  		return fmt.Errorf("unsupported type encountered %v", root)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  func verifyVerkle(ctx *cli.Context) error {
   114  	stack, _ := makeConfigNode(ctx)
   115  	defer stack.Close()
   116  
   117  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   118  	headBlock := rawdb.ReadHeadBlock(chaindb)
   119  	if headBlock == nil {
   120  		log.Error("Failed to load head block")
   121  		return errors.New("no head block")
   122  	}
   123  	if ctx.NArg() > 1 {
   124  		log.Error("Too many arguments given")
   125  		return errors.New("too many arguments")
   126  	}
   127  	var (
   128  		rootC common.Hash
   129  		err   error
   130  	)
   131  	if ctx.NArg() == 1 {
   132  		rootC, err = parseRoot(ctx.Args().First())
   133  		if err != nil {
   134  			log.Error("Failed to resolve state root", "error", err)
   135  			return err
   136  		}
   137  		log.Info("Rebuilding the tree", "root", rootC)
   138  	} else {
   139  		rootC = headBlock.Root()
   140  		log.Info("Rebuilding the tree", "root", rootC, "number", headBlock.NumberU64())
   141  	}
   142  
   143  	serializedRoot, err := chaindb.Get(rootC[:])
   144  	if err != nil {
   145  		return err
   146  	}
   147  	root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	if err := checkChildren(root, chaindb.Get); err != nil {
   153  		log.Error("Could not rebuild the tree from the database", "err", err)
   154  		return err
   155  	}
   156  
   157  	log.Info("Tree was rebuilt from the database")
   158  	return nil
   159  }
   160  
   161  func expandVerkle(ctx *cli.Context) error {
   162  	stack, _ := makeConfigNode(ctx)
   163  	defer stack.Close()
   164  
   165  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   166  	var (
   167  		rootC   common.Hash
   168  		keylist [][]byte
   169  		err     error
   170  	)
   171  	if ctx.NArg() >= 2 {
   172  		rootC, err = parseRoot(ctx.Args().First())
   173  		if err != nil {
   174  			log.Error("Failed to resolve state root", "error", err)
   175  			return err
   176  		}
   177  		keylist = make([][]byte, 0, ctx.Args().Len()-1)
   178  		args := ctx.Args().Slice()
   179  		for i := range args[1:] {
   180  			key, err := hex.DecodeString(args[i+1])
   181  			log.Info("decoded key", "arg", args[i+1], "key", key)
   182  			if err != nil {
   183  				return fmt.Errorf("error decoding key #%d: %w", i+1, err)
   184  			}
   185  			keylist = append(keylist, key)
   186  		}
   187  		log.Info("Rebuilding the tree", "root", rootC)
   188  	} else {
   189  		return fmt.Errorf("usage: %s root key1 [key 2...]", ctx.App.Name)
   190  	}
   191  
   192  	serializedRoot, err := chaindb.Get(rootC[:])
   193  	if err != nil {
   194  		return err
   195  	}
   196  	root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
   197  	if err != nil {
   198  		return err
   199  	}
   200  
   201  	for i, key := range keylist {
   202  		log.Info("Reading key", "index", i, "key", keylist[0])
   203  		root.Get(key, chaindb.Get)
   204  	}
   205  
   206  	if err := os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600); err != nil {
   207  		log.Error("Failed to dump file", "err", err)
   208  	} else {
   209  		log.Info("Tree was dumped to file", "file", "dump.dot")
   210  	}
   211  	return nil
   212  }