github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/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/ethereum/go-ethereum/cmd/utils"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/core/rawdb"
    29  	"github.com/ethereum/go-ethereum/internal/flags"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-verkle"
    32  	"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.DatabaseFlags),
    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.DatabaseFlags),
    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.Commit().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)
    88  			if err != nil {
    89  				return fmt.Errorf("decode error child %x in db: %w", child.Commitment().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 errors.New("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  	defer chaindb.Close()
   119  	headBlock := rawdb.ReadHeadBlock(chaindb)
   120  	if headBlock == nil {
   121  		log.Error("Failed to load head block")
   122  		return errors.New("no head block")
   123  	}
   124  	if ctx.NArg() > 1 {
   125  		log.Error("Too many arguments given")
   126  		return errors.New("too many arguments")
   127  	}
   128  	var (
   129  		rootC common.Hash
   130  		err   error
   131  	)
   132  	if ctx.NArg() == 1 {
   133  		rootC, err = parseRoot(ctx.Args().First())
   134  		if err != nil {
   135  			log.Error("Failed to resolve state root", "error", err)
   136  			return err
   137  		}
   138  		log.Info("Rebuilding the tree", "root", rootC)
   139  	} else {
   140  		rootC = headBlock.Root()
   141  		log.Info("Rebuilding the tree", "root", rootC, "number", headBlock.NumberU64())
   142  	}
   143  
   144  	serializedRoot, err := chaindb.Get(rootC[:])
   145  	if err != nil {
   146  		return err
   147  	}
   148  	root, err := verkle.ParseNode(serializedRoot, 0)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	if err := checkChildren(root, chaindb.Get); err != nil {
   154  		log.Error("Could not rebuild the tree from the database", "err", err)
   155  		return err
   156  	}
   157  
   158  	log.Info("Tree was rebuilt from the database")
   159  	return nil
   160  }
   161  
   162  func expandVerkle(ctx *cli.Context) error {
   163  	stack, _ := makeConfigNode(ctx)
   164  	defer stack.Close()
   165  
   166  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   167  	defer chaindb.Close()
   168  	var (
   169  		rootC   common.Hash
   170  		keylist [][]byte
   171  		err     error
   172  	)
   173  	if ctx.NArg() >= 2 {
   174  		rootC, err = parseRoot(ctx.Args().First())
   175  		if err != nil {
   176  			log.Error("Failed to resolve state root", "error", err)
   177  			return err
   178  		}
   179  		keylist = make([][]byte, 0, ctx.Args().Len()-1)
   180  		args := ctx.Args().Slice()
   181  		for i := range args[1:] {
   182  			key, err := hex.DecodeString(args[i+1])
   183  			log.Info("decoded key", "arg", args[i+1], "key", key)
   184  			if err != nil {
   185  				return fmt.Errorf("error decoding key #%d: %w", i+1, err)
   186  			}
   187  			keylist = append(keylist, key)
   188  		}
   189  		log.Info("Rebuilding the tree", "root", rootC)
   190  	} else {
   191  		return fmt.Errorf("usage: %s root key1 [key 2...]", ctx.App.Name)
   192  	}
   193  
   194  	serializedRoot, err := chaindb.Get(rootC[:])
   195  	if err != nil {
   196  		return err
   197  	}
   198  	root, err := verkle.ParseNode(serializedRoot, 0)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	for i, key := range keylist {
   204  		log.Info("Reading key", "index", i, "key", keylist[0])
   205  		root.Get(key, chaindb.Get)
   206  	}
   207  
   208  	if err := os.WriteFile("dump.dot", []byte(verkle.ToDot(root)), 0600); err != nil {
   209  		log.Error("Failed to dump file", "err", err)
   210  	} else {
   211  		log.Info("Tree was dumped to file", "file", "dump.dot")
   212  	}
   213  	return nil
   214  }