github.com/prysmaticlabs/prysm@v1.4.4/tools/pcli/main.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "regexp" 10 "strings" 11 12 fssz "github.com/ferranbt/fastssz" 13 "github.com/kr/pretty" 14 "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 15 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 16 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 17 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 18 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 19 "github.com/prysmaticlabs/prysm/shared/sszutil" 20 "github.com/prysmaticlabs/prysm/shared/version" 21 log "github.com/sirupsen/logrus" 22 "github.com/urfave/cli/v2" 23 prefixed "github.com/x-cray/logrus-prefixed-formatter" 24 "gopkg.in/d4l3k/messagediff.v1" 25 ) 26 27 func main() { 28 var blockPath string 29 var preStatePath string 30 var expectedPostStatePath string 31 var sszPath string 32 var sszType string 33 34 customFormatter := new(prefixed.TextFormatter) 35 customFormatter.TimestampFormat = "2006-01-02 15:04:05" 36 customFormatter.FullTimestamp = true 37 log.SetFormatter(customFormatter) 38 app := cli.App{} 39 app.Name = "pcli" 40 app.Usage = "A command line utility to run Ethereum consensus specific commands" 41 app.Version = version.Version() 42 app.Commands = []*cli.Command{ 43 { 44 Name: "pretty", 45 Aliases: []string{"p"}, 46 Usage: "pretty-print SSZ data", 47 Flags: []cli.Flag{ 48 &cli.StringFlag{ 49 Name: "ssz-path", 50 Usage: "Path to file(ssz)", 51 Required: true, 52 Destination: &sszPath, 53 }, 54 &cli.StringFlag{ 55 Name: "data-type", 56 Usage: "ssz file data type: " + 57 "block|" + 58 "signed_block|" + 59 "attestation|" + 60 "block_header|" + 61 "deposit|" + 62 "proposer_slashing|" + 63 "signed_block_header|" + 64 "signed_voluntary_exit|" + 65 "voluntary_exit|" + 66 "state", 67 Required: true, 68 Destination: &sszType, 69 }, 70 }, 71 Action: func(c *cli.Context) error { 72 var data fssz.Unmarshaler 73 switch sszType { 74 case "block": 75 data = ðpb.BeaconBlock{} 76 case "signed_block": 77 data = ðpb.SignedBeaconBlock{} 78 case "attestation": 79 data = ðpb.Attestation{} 80 case "block_header": 81 data = ðpb.BeaconBlockHeader{} 82 case "deposit": 83 data = ðpb.Deposit{} 84 case "deposit_message": 85 data = &pb.DepositMessage{} 86 case "proposer_slashing": 87 data = ðpb.ProposerSlashing{} 88 case "signed_block_header": 89 data = ðpb.SignedBeaconBlockHeader{} 90 case "signed_voluntary_exit": 91 data = ðpb.SignedVoluntaryExit{} 92 case "voluntary_exit": 93 data = ðpb.VoluntaryExit{} 94 case "state": 95 data = &pb.BeaconState{} 96 default: 97 log.Fatal("Invalid type") 98 } 99 prettyPrint(sszPath, data) 100 return nil 101 }, 102 }, 103 { 104 Name: "state-transition", 105 Category: "state-transition", 106 Usage: "Subcommand to run manual state transitions", 107 Flags: []cli.Flag{ 108 &cli.StringFlag{ 109 Name: "block-path", 110 Usage: "Path to block file(ssz)", 111 Destination: &blockPath, 112 }, 113 &cli.StringFlag{ 114 Name: "pre-state-path", 115 Usage: "Path to pre state file(ssz)", 116 Destination: &preStatePath, 117 }, 118 &cli.StringFlag{ 119 Name: "expected-post-state-path", 120 Usage: "Path to expected post state file(ssz)", 121 Destination: &expectedPostStatePath, 122 }, 123 }, 124 Action: func(c *cli.Context) error { 125 if blockPath == "" { 126 log.Info("Block path not provided for state transition. " + 127 "Please provide path") 128 reader := bufio.NewReader(os.Stdin) 129 text, err := reader.ReadString('\n') 130 if err != nil { 131 log.Fatal(err) 132 } 133 if text = strings.ReplaceAll(text, "\n", ""); text == "" { 134 log.Fatal("Empty block path given") 135 } 136 blockPath = text 137 } 138 block := ðpb.SignedBeaconBlock{} 139 if err := dataFetcher(blockPath, block); err != nil { 140 log.Fatal(err) 141 } 142 blkRoot, err := block.Block.HashTreeRoot() 143 if err != nil { 144 log.Fatal(err) 145 } 146 if preStatePath == "" { 147 log.Info("Pre State path not provided for state transition. " + 148 "Please provide path") 149 reader := bufio.NewReader(os.Stdin) 150 text, err := reader.ReadString('\n') 151 if err != nil { 152 log.Fatal(err) 153 } 154 if text = strings.ReplaceAll(text, "\n", ""); text == "" { 155 log.Fatal("Empty state path given") 156 } 157 preStatePath = text 158 } 159 preState := &pb.BeaconState{} 160 if err := dataFetcher(preStatePath, preState); err != nil { 161 log.Fatal(err) 162 } 163 stateObj, err := v1.InitializeFromProto(preState) 164 if err != nil { 165 log.Fatal(err) 166 } 167 preStateRoot, err := stateObj.HashTreeRoot(context.Background()) 168 if err != nil { 169 log.Fatal(err) 170 } 171 log.WithFields(log.Fields{ 172 "blockSlot": fmt.Sprintf("%d", block.Block.Slot), 173 "preStateSlot": fmt.Sprintf("%d", stateObj.Slot()), 174 }).Infof( 175 "Performing state transition with a block root of %#x and pre state root of %#x", 176 blkRoot, 177 preStateRoot, 178 ) 179 postState, err := state.ExecuteStateTransition(context.Background(), stateObj, wrapper.WrappedPhase0SignedBeaconBlock(block)) 180 if err != nil { 181 log.Fatal(err) 182 } 183 postRoot, err := postState.HashTreeRoot(context.Background()) 184 if err != nil { 185 log.Fatal(err) 186 } 187 log.Infof("Finished state transition with post state root of %#x", postRoot) 188 189 // Diff the state if a post state is provided. 190 if expectedPostStatePath != "" { 191 expectedState := &pb.BeaconState{} 192 if err := dataFetcher(expectedPostStatePath, expectedState); err != nil { 193 log.Fatal(err) 194 } 195 if !sszutil.DeepEqual(expectedState, postState.InnerStateUnsafe()) { 196 diff, _ := messagediff.PrettyDiff(expectedState, postState.InnerStateUnsafe()) 197 log.Errorf("Derived state differs from provided post state: %s", diff) 198 } 199 } 200 return nil 201 }, 202 }, 203 } 204 if err := app.Run(os.Args); err != nil { 205 log.Error(err.Error()) 206 os.Exit(1) 207 } 208 } 209 210 // dataFetcher fetches and unmarshals data from file to provided data structure. 211 func dataFetcher(fPath string, data fssz.Unmarshaler) error { 212 rawFile, err := ioutil.ReadFile(fPath) 213 if err != nil { 214 return err 215 } 216 return data.UnmarshalSSZ(rawFile) 217 } 218 219 func prettyPrint(sszPath string, data fssz.Unmarshaler) { 220 if err := dataFetcher(sszPath, data); err != nil { 221 log.Fatal(err) 222 } 223 str := pretty.Sprint(data) 224 re := regexp.MustCompile("(?m)[\r\n]+^.*XXX_.*$") 225 str = re.ReplaceAllString(str, "") 226 fmt.Print(str) 227 }