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 = &ethpb.BeaconBlock{}
    76  				case "signed_block":
    77  					data = &ethpb.SignedBeaconBlock{}
    78  				case "attestation":
    79  					data = &ethpb.Attestation{}
    80  				case "block_header":
    81  					data = &ethpb.BeaconBlockHeader{}
    82  				case "deposit":
    83  					data = &ethpb.Deposit{}
    84  				case "deposit_message":
    85  					data = &pb.DepositMessage{}
    86  				case "proposer_slashing":
    87  					data = &ethpb.ProposerSlashing{}
    88  				case "signed_block_header":
    89  					data = &ethpb.SignedBeaconBlockHeader{}
    90  				case "signed_voluntary_exit":
    91  					data = &ethpb.SignedVoluntaryExit{}
    92  				case "voluntary_exit":
    93  					data = &ethpb.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 := &ethpb.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  }