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 }