-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a Generate Genesis State Command to Prysmctl (#11259)
* genesis tool * done with tool * delete old tool * no fatal * fix up * Update cmd/prysmctl/testnet/generate_genesis.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * radek feedback * more feedback * required * fix up * gaz * radek feedback Co-authored-by: Radosław Kapka <rkapka@wp.pl>
- Loading branch information
1 parent
e30471f
commit 20ed47a
Showing
8 changed files
with
322 additions
and
271 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
load("@prysm//tools/go:def.bzl", "go_library", "go_test") | ||
|
||
go_library( | ||
name = "go_default_library", | ||
srcs = [ | ||
"generate_genesis.go", | ||
"testnet.go", | ||
], | ||
importpath = "github.com/prysmaticlabs/prysm/v3/cmd/prysmctl/testnet", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"//config/params:go_default_library", | ||
"//io/file:go_default_library", | ||
"//proto/prysm/v1alpha1:go_default_library", | ||
"//runtime/interop:go_default_library", | ||
"@com_github_ghodss_yaml//:go_default_library", | ||
"@com_github_pkg_errors//:go_default_library", | ||
"@com_github_prysmaticlabs_fastssz//:go_default_library", | ||
"@com_github_sirupsen_logrus//:go_default_library", | ||
"@com_github_urfave_cli_v2//:go_default_library", | ||
], | ||
) | ||
|
||
go_test( | ||
name = "go_default_test", | ||
srcs = ["generate_genesis_test.go"], | ||
embed = [":go_default_library"], | ||
deps = [ | ||
"//crypto/bls:go_default_library", | ||
"//runtime/interop:go_default_library", | ||
"//testing/assert:go_default_library", | ||
"//testing/require:go_default_library", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
package testnet | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"os" | ||
"strings" | ||
|
||
"github.com/ghodss/yaml" | ||
"github.com/pkg/errors" | ||
fastssz "github.com/prysmaticlabs/fastssz" | ||
"github.com/prysmaticlabs/prysm/v3/config/params" | ||
"github.com/prysmaticlabs/prysm/v3/io/file" | ||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" | ||
"github.com/prysmaticlabs/prysm/v3/runtime/interop" | ||
"github.com/sirupsen/logrus" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
var ( | ||
generateGenesisStateFlags = struct { | ||
DepositJsonFile string | ||
ChainConfigFile string | ||
ConfigName string | ||
NumValidators uint64 | ||
GenesisTime uint64 | ||
OutputSSZ string | ||
OutputJSON string | ||
OutputYaml string | ||
}{} | ||
log = logrus.WithField("prefix", "genesis") | ||
outputSSZFlag = &cli.StringFlag{ | ||
Name: "output-ssz", | ||
Destination: &generateGenesisStateFlags.OutputSSZ, | ||
Usage: "Output filename of the SSZ marshaling of the generated genesis state", | ||
Value: "", | ||
} | ||
outputYamlFlag = &cli.StringFlag{ | ||
Name: "output-yaml", | ||
Destination: &generateGenesisStateFlags.OutputYaml, | ||
Usage: "Output filename of the YAML marshaling of the generated genesis state", | ||
Value: "", | ||
} | ||
outputJsonFlag = &cli.StringFlag{ | ||
Name: "output-json", | ||
Destination: &generateGenesisStateFlags.OutputJSON, | ||
Usage: "Output filename of the JSON marshaling of the generated genesis state", | ||
Value: "", | ||
} | ||
generateGenesisStateCmd = &cli.Command{ | ||
Name: "generate-genesis", | ||
Usage: "Generate a beacon chain genesis state", | ||
Action: cliActionGenerateGenesisState, | ||
Flags: []cli.Flag{ | ||
&cli.StringFlag{ | ||
Name: "chain-config-file", | ||
Destination: &generateGenesisStateFlags.ChainConfigFile, | ||
Usage: "The path to a YAML file with chain config values", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "deposit-json-file", | ||
Destination: &generateGenesisStateFlags.DepositJsonFile, | ||
Usage: "Path to deposit_data.json file generated by the staking-deposit-cli tool for optionally specifying validators in genesis state", | ||
}, | ||
&cli.StringFlag{ | ||
Name: "config-name", | ||
Usage: "Config kind to be used for generating the genesis state. Default: mainnet. Options include mainnet, interop, minimal, prater, ropsten, sepolia. --chain-config-file will override this flag.", | ||
Destination: &generateGenesisStateFlags.ConfigName, | ||
Value: params.MainnetName, | ||
}, | ||
&cli.Uint64Flag{ | ||
Name: "num-validators", | ||
Usage: "Number of validators to deterministically generate in the genesis state", | ||
Destination: &generateGenesisStateFlags.NumValidators, | ||
Required: true, | ||
}, | ||
&cli.Uint64Flag{ | ||
Name: "genesis-time", | ||
Destination: &generateGenesisStateFlags.GenesisTime, | ||
Usage: "Unix timestamp seconds used as the genesis time in the genesis state. If unset, defaults to now()", | ||
}, | ||
outputSSZFlag, | ||
outputYamlFlag, | ||
outputJsonFlag, | ||
}, | ||
} | ||
) | ||
|
||
// Represents a json object of hex string and uint64 values for | ||
// validators on Ethereum. This file can be generated using the official staking-deposit-cli. | ||
type depositDataJSON struct { | ||
PubKey string `json:"pubkey"` | ||
Amount uint64 `json:"amount"` | ||
WithdrawalCredentials string `json:"withdrawal_credentials"` | ||
DepositDataRoot string `json:"deposit_data_root"` | ||
Signature string `json:"signature"` | ||
} | ||
|
||
func cliActionGenerateGenesisState(cliCtx *cli.Context) error { | ||
if generateGenesisStateFlags.GenesisTime == 0 { | ||
log.Info("No genesis time specified, defaulting to now()") | ||
} | ||
outputJson := generateGenesisStateFlags.OutputJSON | ||
outputYaml := generateGenesisStateFlags.OutputYaml | ||
outputSSZ := generateGenesisStateFlags.OutputSSZ | ||
noOutputFlag := outputSSZ == "" && outputJson == "" && outputYaml == "" | ||
if noOutputFlag { | ||
return fmt.Errorf( | ||
"no %s, %s, %s flag(s) specified. At least one is required", | ||
outputJsonFlag.Name, | ||
outputYamlFlag.Name, | ||
outputSSZFlag.Name, | ||
) | ||
} | ||
if err := setGlobalParams(); err != nil { | ||
return fmt.Errorf("could not set config params: %v", err) | ||
} | ||
genesisState, err := generateGenesis(cliCtx.Context) | ||
if err != nil { | ||
return fmt.Errorf("could not generate genesis state: %v", err) | ||
} | ||
if outputJson != "" { | ||
if err := writeToOutputFile(outputJson, genesisState, json.Marshal); err != nil { | ||
return err | ||
} | ||
} | ||
if outputYaml != "" { | ||
if err := writeToOutputFile(outputJson, genesisState, yaml.Marshal); err != nil { | ||
return err | ||
} | ||
} | ||
if outputSSZ != "" { | ||
marshalFn := func(o interface{}) ([]byte, error) { | ||
marshaler, ok := o.(fastssz.Marshaler) | ||
if !ok { | ||
return nil, errors.New("not a marshaler") | ||
} | ||
return marshaler.MarshalSSZ() | ||
} | ||
if err := writeToOutputFile(outputSSZ, genesisState, marshalFn); err != nil { | ||
return err | ||
} | ||
} | ||
log.Info("Command completed") | ||
return nil | ||
} | ||
|
||
func setGlobalParams() error { | ||
chainConfigFile := generateGenesisStateFlags.ChainConfigFile | ||
if chainConfigFile != "" { | ||
log.Infof("Specified a chain config file: %s", chainConfigFile) | ||
return params.LoadChainConfigFile(chainConfigFile, nil) | ||
} | ||
cfg, err := params.ByName(generateGenesisStateFlags.ConfigName) | ||
if err != nil { | ||
return fmt.Errorf("unable to find config using name %s: %v", generateGenesisStateFlags.ConfigName, err) | ||
} | ||
return params.SetActive(cfg.Copy()) | ||
} | ||
|
||
func generateGenesis(ctx context.Context) (*ethpb.BeaconState, error) { | ||
genesisTime := generateGenesisStateFlags.GenesisTime | ||
numValidators := generateGenesisStateFlags.NumValidators | ||
depositJsonFile := generateGenesisStateFlags.DepositJsonFile | ||
if depositJsonFile != "" { | ||
expanded, err := file.ExpandPath(depositJsonFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
inputJSON, err := os.Open(expanded) // #nosec G304 | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer func() { | ||
if err := inputJSON.Close(); err != nil { | ||
log.WithError(err).Printf("Could not close file %s", depositJsonFile) | ||
} | ||
}() | ||
log.Printf("Generating genesis state from input JSON deposit data %s", depositJsonFile) | ||
return genesisStateFromJSONValidators(ctx, inputJSON, genesisTime) | ||
} | ||
if numValidators == 0 { | ||
return nil, fmt.Errorf( | ||
"expected --num-validators > 0 to have been provided", | ||
) | ||
} | ||
// If no JSON input is specified, we create the state deterministically from interop keys. | ||
genesisState, _, err := interop.GenerateGenesisState(ctx, genesisTime, numValidators) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return genesisState, err | ||
} | ||
|
||
func genesisStateFromJSONValidators(ctx context.Context, r io.Reader, genesisTime uint64) (*ethpb.BeaconState, error) { | ||
enc, err := io.ReadAll(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var depositJSON []*depositDataJSON | ||
if err := json.Unmarshal(enc, &depositJSON); err != nil { | ||
return nil, err | ||
} | ||
depositDataList := make([]*ethpb.Deposit_Data, len(depositJSON)) | ||
depositDataRoots := make([][]byte, len(depositJSON)) | ||
for i, val := range depositJSON { | ||
data, dataRootBytes, err := depositJSONToDepositData(val) | ||
if err != nil { | ||
return nil, err | ||
} | ||
depositDataList[i] = data | ||
depositDataRoots[i] = dataRootBytes | ||
} | ||
beaconState, _, err := interop.GenerateGenesisStateFromDepositData(ctx, genesisTime, depositDataList, depositDataRoots) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return beaconState, nil | ||
} | ||
|
||
func depositJSONToDepositData(input *depositDataJSON) (depositData *ethpb.Deposit_Data, dataRoot []byte, err error) { | ||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(input.PubKey, "0x")) | ||
if err != nil { | ||
return | ||
} | ||
withdrawalbytes, err := hex.DecodeString(strings.TrimPrefix(input.WithdrawalCredentials, "0x")) | ||
if err != nil { | ||
return | ||
} | ||
signatureBytes, err := hex.DecodeString(strings.TrimPrefix(input.Signature, "0x")) | ||
if err != nil { | ||
return | ||
} | ||
dataRootBytes, err := hex.DecodeString(strings.TrimPrefix(input.DepositDataRoot, "0x")) | ||
if err != nil { | ||
return | ||
} | ||
depositData = ðpb.Deposit_Data{ | ||
PublicKey: pubKeyBytes, | ||
WithdrawalCredentials: withdrawalbytes, | ||
Amount: input.Amount, | ||
Signature: signatureBytes, | ||
} | ||
dataRoot = dataRootBytes | ||
return | ||
} | ||
|
||
func writeToOutputFile( | ||
fPath string, | ||
data interface{}, | ||
marshalFn func(o interface{}) ([]byte, error), | ||
) error { | ||
encoded, err := marshalFn(data) | ||
if err != nil { | ||
return err | ||
} | ||
if err := file.WriteFile(fPath, encoded); err != nil { | ||
return err | ||
} | ||
log.Printf("Done writing genesis state to %s", fPath) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package testnet | ||
|
||
import "github.com/urfave/cli/v2" | ||
|
||
var Commands = []*cli.Command{ | ||
{ | ||
Name: "testnet", | ||
Usage: "commands for dealing with Ethereum beacon chain testnets", | ||
Subcommands: []*cli.Command{ | ||
generateGenesisStateCmd, | ||
}, | ||
}, | ||
} |
Oops, something went wrong.