mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 07:58:22 -05:00
Compute unrealized checkpoints with pcli (#13692)
* Compute unrealized checkpoints with pcli * gazelle * fix lint * Gazelle --------- Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
This commit is contained in:
@@ -14,6 +14,7 @@ go_library(
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//tools:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
|
||||
@@ -8,6 +8,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v5/tools/pcli",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/kr/pretty"
|
||||
"github.com/pkg/errors"
|
||||
fssz "github.com/prysmaticlabs/fastssz"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
|
||||
@@ -28,14 +29,283 @@ import (
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var blockPath string
|
||||
var preStatePath string
|
||||
var expectedPostStatePath string
|
||||
var network string
|
||||
var sszPath string
|
||||
var sszType string
|
||||
var blockPath string
|
||||
var preStatePath string
|
||||
var expectedPostStatePath string
|
||||
var network string
|
||||
var sszPath string
|
||||
var sszType string
|
||||
var prettyCommand = &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|" +
|
||||
"blinded_block|" +
|
||||
"signed_block|" +
|
||||
"attestation|" +
|
||||
"block_header|" +
|
||||
"deposit|" +
|
||||
"proposer_slashing|" +
|
||||
"signed_block_header|" +
|
||||
"signed_voluntary_exit|" +
|
||||
"voluntary_exit|" +
|
||||
"state_capella",
|
||||
Required: true,
|
||||
Destination: &sszType,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
var data fssz.Unmarshaler
|
||||
switch sszType {
|
||||
case "block":
|
||||
data = ðpb.BeaconBlock{}
|
||||
case "signed_block":
|
||||
data = ðpb.SignedBeaconBlock{}
|
||||
case "blinded_block":
|
||||
data = ðpb.BlindedBeaconBlockBellatrix{}
|
||||
case "attestation":
|
||||
data = ðpb.Attestation{}
|
||||
case "block_header":
|
||||
data = ðpb.BeaconBlockHeader{}
|
||||
case "deposit":
|
||||
data = ðpb.Deposit{}
|
||||
case "deposit_message":
|
||||
data = ðpb.DepositMessage{}
|
||||
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_capella":
|
||||
data = ðpb.BeaconStateCapella{}
|
||||
default:
|
||||
log.Fatal("Invalid type")
|
||||
}
|
||||
prettyPrint(sszPath, data)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var benchmarkHashCommand = &cli.Command{
|
||||
Name: "benchmark-hash",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "benchmark-hash 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_capella|" +
|
||||
"blinded_block_capella|" +
|
||||
"signed_block_capella|" +
|
||||
"attestation|" +
|
||||
"block_header|" +
|
||||
"deposit|" +
|
||||
"proposer_slashing|" +
|
||||
"signed_block_header|" +
|
||||
"signed_voluntary_exit|" +
|
||||
"voluntary_exit|" +
|
||||
"state_capella",
|
||||
Required: true,
|
||||
Destination: &sszType,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
benchmarkHash(sszPath, sszType)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var unrealizedCheckpointsCommand = &cli.Command{
|
||||
Name: "unrealized-checkpoints",
|
||||
Category: "state-computations",
|
||||
Usage: "Subcommand to compute manually the unrealized checkpoints",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "state-path",
|
||||
Usage: "Path to state file(ssz)",
|
||||
Destination: &preStatePath,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if preStatePath == "" {
|
||||
log.Info("State path not provided, please provide path")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if text = strings.ReplaceAll(text, "\n", ""); text == "" {
|
||||
log.Fatal("Empty state path given")
|
||||
}
|
||||
preStatePath = text
|
||||
}
|
||||
stateObj, err := detectState(preStatePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
preStateRoot, err := stateObj.HashTreeRoot(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof(
|
||||
"Computing unrealized justification for state at slot %d and root %#x",
|
||||
stateObj.Slot(),
|
||||
preStateRoot,
|
||||
)
|
||||
uj, uf, err := precompute.UnrealizedCheckpoints(stateObj)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof("Computed:\nUnrealized Justified: (Root: %#x, Epoch: %d)\nUnrealized Finalized: (Root: %#x, Epoch: %d).", uj.Root, uj.Epoch, uf.Root, uf.Epoch)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stateTransitionCommand = &cli.Command{
|
||||
Name: "state-transition",
|
||||
Category: "state-computations",
|
||||
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,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "network",
|
||||
Usage: "Network to run the state transition in",
|
||||
Destination: &network,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if network != "" {
|
||||
switch network {
|
||||
case params.PraterName:
|
||||
if err := params.SetActive(params.PraterConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case params.GoerliName:
|
||||
if err := params.SetActive(params.PraterConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case params.SepoliaName:
|
||||
if err := params.SetActive(params.SepoliaConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case params.HoleskyName:
|
||||
if err := params.SetActive(params.HoleskyConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
default:
|
||||
log.Fatalf("Unknown network provided: %s", network)
|
||||
}
|
||||
}
|
||||
|
||||
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.ReplaceAll(text, "\n", ""); text == "" {
|
||||
log.Fatal("Empty block path given")
|
||||
}
|
||||
blockPath = text
|
||||
}
|
||||
block, err := detectBlock(blockPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
blkRoot, err := block.Block().HashTreeRoot()
|
||||
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')
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if text = strings.ReplaceAll(text, "\n", ""); text == "" {
|
||||
log.Fatal("Empty state path given")
|
||||
}
|
||||
preStatePath = text
|
||||
}
|
||||
stateObj, err := detectState(preStatePath)
|
||||
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 := debugStateTransition(context.Background(), stateObj, block)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
postRoot, err := postState.HashTreeRoot(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof("Finished state transition with post state root of %#x", postRoot)
|
||||
|
||||
// Diff the state if a post state is provided.
|
||||
if expectedPostStatePath != "" {
|
||||
expectedState, err := detectState(expectedPostStatePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !equality.DeepEqual(expectedState.ToProtoUnsafe(), postState.ToProtoUnsafe()) {
|
||||
diff, _ := messagediff.PrettyDiff(expectedState.ToProtoUnsafe(), postState.ToProtoUnsafe())
|
||||
log.Errorf("Derived state differs from provided post state: %s", diff)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
customFormatter := new(prefixed.TextFormatter)
|
||||
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||
customFormatter.FullTimestamp = true
|
||||
@@ -45,227 +315,10 @@ func main() {
|
||||
app.Usage = "A command line utility to run Ethereum consensus specific commands"
|
||||
app.Version = version.Version()
|
||||
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|" +
|
||||
"blinded_block|" +
|
||||
"signed_block|" +
|
||||
"attestation|" +
|
||||
"block_header|" +
|
||||
"deposit|" +
|
||||
"proposer_slashing|" +
|
||||
"signed_block_header|" +
|
||||
"signed_voluntary_exit|" +
|
||||
"voluntary_exit|" +
|
||||
"state_capella",
|
||||
Required: true,
|
||||
Destination: &sszType,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
var data fssz.Unmarshaler
|
||||
switch sszType {
|
||||
case "block":
|
||||
data = ðpb.BeaconBlock{}
|
||||
case "signed_block":
|
||||
data = ðpb.SignedBeaconBlock{}
|
||||
case "blinded_block":
|
||||
data = ðpb.BlindedBeaconBlockBellatrix{}
|
||||
case "attestation":
|
||||
data = ðpb.Attestation{}
|
||||
case "block_header":
|
||||
data = ðpb.BeaconBlockHeader{}
|
||||
case "deposit":
|
||||
data = ðpb.Deposit{}
|
||||
case "deposit_message":
|
||||
data = ðpb.DepositMessage{}
|
||||
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_capella":
|
||||
data = ðpb.BeaconStateCapella{}
|
||||
default:
|
||||
log.Fatal("Invalid type")
|
||||
}
|
||||
prettyPrint(sszPath, data)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "benchmark-hash",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "benchmark-hash 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_capella|" +
|
||||
"blinded_block_capella|" +
|
||||
"signed_block_capella|" +
|
||||
"attestation|" +
|
||||
"block_header|" +
|
||||
"deposit|" +
|
||||
"proposer_slashing|" +
|
||||
"signed_block_header|" +
|
||||
"signed_voluntary_exit|" +
|
||||
"voluntary_exit|" +
|
||||
"state_capella",
|
||||
Required: true,
|
||||
Destination: &sszType,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
benchmarkHash(sszPath, sszType)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "network",
|
||||
Usage: "Network to run the state transition in",
|
||||
Destination: &network,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if network != "" {
|
||||
switch network {
|
||||
case params.PraterName:
|
||||
if err := params.SetActive(params.PraterConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case params.GoerliName:
|
||||
if err := params.SetActive(params.PraterConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case params.SepoliaName:
|
||||
if err := params.SetActive(params.SepoliaConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case params.HoleskyName:
|
||||
if err := params.SetActive(params.HoleskyConfig()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
default:
|
||||
log.Fatalf("Unknown network provided: %s", network)
|
||||
}
|
||||
}
|
||||
|
||||
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.ReplaceAll(text, "\n", ""); text == "" {
|
||||
log.Fatal("Empty block path given")
|
||||
}
|
||||
blockPath = text
|
||||
}
|
||||
block, err := detectBlock(blockPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
blkRoot, err := block.Block().HashTreeRoot()
|
||||
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')
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if text = strings.ReplaceAll(text, "\n", ""); text == "" {
|
||||
log.Fatal("Empty state path given")
|
||||
}
|
||||
preStatePath = text
|
||||
}
|
||||
stateObj, err := detectState(preStatePath)
|
||||
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 := debugStateTransition(context.Background(), stateObj, block)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
postRoot, err := postState.HashTreeRoot(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof("Finished state transition with post state root of %#x", postRoot)
|
||||
|
||||
// Diff the state if a post state is provided.
|
||||
if expectedPostStatePath != "" {
|
||||
expectedState, err := detectState(expectedPostStatePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !equality.DeepEqual(expectedState.ToProtoUnsafe(), postState.ToProtoUnsafe()) {
|
||||
diff, _ := messagediff.PrettyDiff(expectedState.ToProtoUnsafe(), postState.ToProtoUnsafe())
|
||||
log.Errorf("Derived state differs from provided post state: %s", diff)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
prettyCommand,
|
||||
benchmarkHashCommand,
|
||||
unrealizedCheckpointsCommand,
|
||||
stateTransitionCommand,
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Error(err.Error())
|
||||
|
||||
Reference in New Issue
Block a user