diff --git a/go.mod b/go.mod index 8a67865923..57f8816f1e 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( github.com/json-iterator/go v1.1.9 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/kevinms/leakybucket-go v0.0.0-20200115003610-082473db97ca + github.com/kr/pretty v0.2.0 github.com/libp2p/go-libp2p v0.9.2 github.com/libp2p/go-libp2p-blankhost v0.1.6 github.com/libp2p/go-libp2p-circuit v0.2.3 diff --git a/tools/pcli/BUILD.bazel b/tools/pcli/BUILD.bazel index d2b16e46e7..015198fd3e 100644 --- a/tools/pcli/BUILD.bazel +++ b/tools/pcli/BUILD.bazel @@ -15,6 +15,7 @@ go_library( "//beacon-chain/state/stateutil:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/version:go_default_library", + "@com_github_kr_pretty//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", @@ -44,6 +45,7 @@ go_image( "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_urfave_cli_v2//:go_default_library", + "@com_github_kr_pretty//:go_default_library", "@com_github_x_cray_logrus_prefixed_formatter//:go_default_library", "@in_gopkg_d4l3k_messagediff_v1//:go_default_library", ], diff --git a/tools/pcli/main.go b/tools/pcli/main.go index 36c2a74c19..a3bbf5a667 100644 --- a/tools/pcli/main.go +++ b/tools/pcli/main.go @@ -6,8 +6,10 @@ import ( "fmt" "io/ioutil" "os" + "regexp" "strings" + "github.com/kr/pretty" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" @@ -25,113 +27,173 @@ func main() { var blockPath string var preStatePath string var expectedPostStatePath string + var sszPath string + var sszType string customFormatter := new(prefixed.TextFormatter) customFormatter.TimestampFormat = "2006-01-02 15:04:05" customFormatter.FullTimestamp = true log.SetFormatter(customFormatter) - app := cli.App{} app.Name = "pcli" app.Usage = "A command line utility to run eth2 specific commands" app.Version = version.GetVersion() - app.Commands = []*cli.Command{{ - Name: "state-transition", - Category: "state-transition", - Usage: "Subcommand to run manual state transitions", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "block-path", - Usage: "Path to block file(ssz)", - Destination: &blockPath, + app.Commands = []*cli.Command{ + { + Name: "pretty", + Aliases: []string{"p"}, + Usage: "pretty-print SSZ data", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "ssz-path", + Usage: "Path to file(ssz)", + Required: true, + Destination: &sszPath, + }, + &cli.StringFlag{ + Name: "data-type", + Usage: "ssz file data type: " + + "block|" + + "signed_block|" + + "attestation|" + + "block_header|" + + "deposit|" + + "proposer_slashing|" + + "signed_block_header|" + + "signed_voluntary_exit|" + + "voluntary_exit|" + + "state", + Required: true, + Destination: &sszType, + }, }, - &cli.StringFlag{ - Name: "pre-state-path", - Usage: "Path to pre state file(ssz)", - Destination: &preStatePath, - }, - &cli.StringFlag{ - Name: "expected-post-state-path", - Usage: "Path to expected post state file(ssz)", - Destination: &expectedPostStatePath, + Action: func(c *cli.Context) error { + var data interface{} + switch sszType { + case "block": + data = ðpb.BeaconBlock{} + case "signed_block": + data = ðpb.SignedBeaconBlock{} + case "attestation": + data = ðpb.Attestation{} + case "block_header": + data = ðpb.BeaconBlockHeader{} + case "deposit": + data = ðpb.Deposit{} + case "proposer_slashing": + data = ðpb.ProposerSlashing{} + case "signed_block_header": + data = ðpb.SignedBeaconBlockHeader{} + case "signed_voluntary_exit": + data = ðpb.SignedVoluntaryExit{} + case "voluntary_exit": + data = ðpb.VoluntaryExit{} + case "state": + data = &pb.BeaconState{} + default: + log.Fatal("Invalid type") + } + prettyPrint(sszPath, data) + return nil }, }, - Action: func(c *cli.Context) error { - if blockPath == "" { - log.Info("Block path not provided for state transition. " + - "Please provide path") - reader := bufio.NewReader(os.Stdin) - text, err := reader.ReadString('\n') + { + Name: "state-transition", + Category: "state-transition", + Usage: "Subcommand to run manual state transitions", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "block-path", + Usage: "Path to block file(ssz)", + Destination: &blockPath, + }, + &cli.StringFlag{ + Name: "pre-state-path", + Usage: "Path to pre state file(ssz)", + Destination: &preStatePath, + }, + &cli.StringFlag{ + Name: "expected-post-state-path", + Usage: "Path to expected post state file(ssz)", + Destination: &expectedPostStatePath, + }, + }, + Action: func(c *cli.Context) error { + if blockPath == "" { + log.Info("Block path not provided for state transition. " + + "Please provide path") + reader := bufio.NewReader(os.Stdin) + text, err := reader.ReadString('\n') + if err != nil { + log.Fatal(err) + } + if text = strings.Replace(text, "\n", "", -1); text == "" { + log.Fatal("Empty block path given") + } + blockPath = text + } + block := ðpb.SignedBeaconBlock{} + if err := dataFetcher(blockPath, block); err != nil { + log.Fatal(err) + } + blkRoot, err := stateutil.BlockRoot(block.Block) if err != nil { log.Fatal(err) } - if text = strings.Replace(text, "\n", "", -1); text == "" { - log.Fatal("Empty block path given") + if preStatePath == "" { + log.Info("Pre State path not provided for state transition. " + + "Please provide path") + reader := bufio.NewReader(os.Stdin) + text, err := reader.ReadString('\n') + if err != nil { + log.Fatal(err) + } + if text = strings.Replace(text, "\n", "", -1); text == "" { + log.Fatal("Empty state path given") + } + preStatePath = text } - blockPath = text - } - block := ðpb.SignedBeaconBlock{} - if err := dataFetcher(blockPath, block); err != nil { - log.Fatal(err) - } - blkRoot, err := stateutil.BlockRoot(block.Block) - if err != nil { - log.Fatal(err) - } - if preStatePath == "" { - log.Info("Pre State path not provided for state transition. " + - "Please provide path") - reader := bufio.NewReader(os.Stdin) - text, err := reader.ReadString('\n') + preState := &pb.BeaconState{} + if err := dataFetcher(preStatePath, preState); err != nil { + log.Fatal(err) + } + stateObj, err := stateTrie.InitializeFromProto(preState) if err != nil { log.Fatal(err) } - if text = strings.Replace(text, "\n", "", -1); text == "" { - log.Fatal("Empty state path given") + preStateRoot, err := stateObj.HashTreeRoot(context.Background()) + if err != nil { + log.Fatal(err) } - preStatePath = text - } - preState := &pb.BeaconState{} - if err := dataFetcher(preStatePath, preState); err != nil { - log.Fatal(err) - } - stateObj, err := stateTrie.InitializeFromProto(preState) - if err != nil { - log.Fatal(err) - } - preStateRoot, err := stateObj.HashTreeRoot(context.Background()) - if err != nil { - log.Fatal(err) - } - log.WithFields(log.Fields{ - "blockSlot": fmt.Sprintf("%d", block.Block.Slot), - "preStateSlot": fmt.Sprintf("%d", stateObj.Slot()), - }).Infof( - "Performing state transition with a block root of %#x and pre state root of %#x", - blkRoot, - preStateRoot, - ) - postState, err := state.ExecuteStateTransition(context.Background(), stateObj, block) - if err != nil { - log.Fatal(err) - } - postRoot, err := postState.HashTreeRoot(context.Background()) - log.Infof("Finished state transition with post state root of %#x", postRoot) + log.WithFields(log.Fields{ + "blockSlot": fmt.Sprintf("%d", block.Block.Slot), + "preStateSlot": fmt.Sprintf("%d", stateObj.Slot()), + }).Infof( + "Performing state transition with a block root of %#x and pre state root of %#x", + blkRoot, + preStateRoot, + ) + postState, err := state.ExecuteStateTransition(context.Background(), stateObj, block) + if err != nil { + log.Fatal(err) + } + postRoot, err := postState.HashTreeRoot(context.Background()) + log.Infof("Finished state transition with post state root of %#x", postRoot) - // Diff the state if a post state is provided. - if expectedPostStatePath != "" { - expectedState := &pb.BeaconState{} - if err := dataFetcher(expectedPostStatePath, expectedState); err != nil { - log.Fatal(err) + // Diff the state if a post state is provided. + if expectedPostStatePath != "" { + expectedState := &pb.BeaconState{} + if err := dataFetcher(expectedPostStatePath, expectedState); err != nil { + log.Fatal(err) + } + if !ssz.DeepEqual(expectedState, postState.InnerStateUnsafe()) { + diff, _ := messagediff.PrettyDiff(expectedState, postState.InnerStateUnsafe()) + log.Errorf("Derived state differs from provided post state: %s", diff) + } } - if !ssz.DeepEqual(expectedState, postState.InnerStateUnsafe()) { - diff, _ := messagediff.PrettyDiff(expectedState, postState.InnerStateUnsafe()) - log.Errorf("Derived state differs from provided post state: %s", diff) - } - } - return nil + return nil + }, }, - }, } if err := app.Run(os.Args); err != nil { log.Error(err.Error()) @@ -147,3 +209,13 @@ func dataFetcher(fPath string, data interface{}) error { } return ssz.Unmarshal(rawFile, data) } + +func prettyPrint(sszPath string, data interface{}) { + if err := dataFetcher(sszPath, data); err != nil { + log.Fatal(err) + } + str := pretty.Sprint(data) + re := regexp.MustCompile("(?m)[\r\n]+^.*XXX_.*$") + str = re.ReplaceAllString(str, "") + fmt.Print(str) +}