diff --git a/cmd/root.go b/cmd/root.go index 46d79070..173d4591 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -253,6 +253,10 @@ func addPersistentFlags() { if err := viper.BindPFlag("allow-insecure-connections", RootCmd.PersistentFlags().Lookup("allow-insecure-connections")); err != nil { panic(err) } + RootCmd.PersistentFlags().Bool("custom-spec-support", false, "use dynamic SSZ decoding for non-mainnet presets such as Gnosis (slower; currently honoured only by 'validator exit')") + if err := viper.BindPFlag("custom-spec-support", RootCmd.PersistentFlags().Lookup("custom-spec-support")); err != nil { + panic(err) + } } // initConfig reads in config file and ENV variables if set. diff --git a/cmd/validator/exit/command.go b/cmd/validator/exit/command.go index b6a8d3ae..01fa159b 100644 --- a/cmd/validator/exit/command.go +++ b/cmd/validator/exit/command.go @@ -50,6 +50,7 @@ type command struct { timeout time.Duration connection string allowInsecureConnections bool + customSpecSupport bool // Information required to generate the operations. chainInfo *beacon.ChainInfo @@ -73,6 +74,7 @@ func newCommand(_ context.Context) (*command, error) { timeout: viper.GetDuration("timeout"), connection: viper.GetString("connection"), allowInsecureConnections: viper.GetBool("allow-insecure-connections"), + customSpecSupport: viper.GetBool("custom-spec-support"), prepareOffline: viper.GetBool("prepare-offline"), passphrases: util.GetPassphrases(), mnemonic: viper.GetString("mnemonic"), diff --git a/cmd/validator/exit/process.go b/cmd/validator/exit/process.go index c9bbc16e..e955e040 100644 --- a/cmd/validator/exit/process.go +++ b/cmd/validator/exit/process.go @@ -595,10 +595,11 @@ func (c *command) setup(ctx context.Context) error { // Connect to the consensus node. var err error c.consensusClient, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{ - Address: c.connection, - Timeout: c.timeout, - AllowInsecure: c.allowInsecureConnections, - LogFallback: !c.quiet, + Address: c.connection, + Timeout: c.timeout, + AllowInsecure: c.allowInsecureConnections, + CustomSpecSupport: c.customSpecSupport, + LogFallback: !c.quiet, }) if err != nil { return err diff --git a/util/beaconnode.go b/util/beaconnode.go index 54af7b64..01bc245d 100644 --- a/util/beaconnode.go +++ b/util/beaconnode.go @@ -37,11 +37,13 @@ var defaultBeaconNodeAddresses = []string{ // fallbackBeaconNode is used if no other connection is supplied. var fallbackBeaconNode = "http://mainnet-consensus.attestant.io/" +// ConnectOpts are the options for connecting to a beacon node. type ConnectOpts struct { - Address string - Timeout time.Duration - AllowInsecure bool - LogFallback bool + Address string + Timeout time.Duration + AllowInsecure bool + CustomSpecSupport bool + LogFallback bool } // ConnectToBeaconNode connects to a beacon node at the given address. @@ -56,12 +58,22 @@ func ConnectToBeaconNode(ctx context.Context, opts *ConnectOpts) (eth2client.Ser if opts.Address != "" { // We have an explicit address; use it. - return connectToBeaconNode(ctx, opts.Address, opts.Timeout, opts.AllowInsecure) + return connectToBeaconNode(ctx, &beaconNodeConnection{ + address: opts.Address, + timeout: opts.Timeout, + allowInsecure: opts.AllowInsecure, + customSpecSupport: opts.CustomSpecSupport, + }) } // Try the defaults. for _, address := range defaultBeaconNodeAddresses { - client, err := connectToBeaconNode(ctx, address, opts.Timeout, opts.AllowInsecure) + client, err := connectToBeaconNode(ctx, &beaconNodeConnection{ + address: address, + timeout: opts.Timeout, + allowInsecure: opts.AllowInsecure, + customSpecSupport: opts.CustomSpecSupport, + }) if err == nil { return client, nil } @@ -71,7 +83,12 @@ func ConnectToBeaconNode(ctx context.Context, opts *ConnectOpts) (eth2client.Ser if opts.LogFallback { fmt.Fprintf(os.Stderr, "No connection supplied with --connection parameter and no local beacon node found, attempting to use mainnet fallback\n") } - client, err := connectToBeaconNode(ctx, fallbackBeaconNode, opts.Timeout, true) + client, err := connectToBeaconNode(ctx, &beaconNodeConnection{ + address: fallbackBeaconNode, + timeout: opts.Timeout, + allowInsecure: true, + customSpecSupport: opts.CustomSpecSupport, + }) if err == nil { return client, nil } @@ -79,11 +96,20 @@ func ConnectToBeaconNode(ctx context.Context, opts *ConnectOpts) (eth2client.Ser return nil, errors.New("failed to connect to any beacon node") } -func connectToBeaconNode(ctx context.Context, address string, timeout time.Duration, allowInsecure bool) (eth2client.Service, error) { +// beaconNodeConnection holds the parameters for a single beacon node connection attempt. +type beaconNodeConnection struct { + address string + timeout time.Duration + allowInsecure bool + customSpecSupport bool +} + +func connectToBeaconNode(ctx context.Context, conn *beaconNodeConnection) (eth2client.Service, error) { + address := conn.address if !strings.HasPrefix(address, "http") { address = fmt.Sprintf("http://%s", address) } - if !allowInsecure { + if !conn.allowInsecure { // Ensure the connection is either secure or local. connectionURL, err := url.Parse(address) if err != nil { @@ -100,7 +126,8 @@ func connectToBeaconNode(ctx context.Context, address string, timeout time.Durat eth2Client, err := http.New(ctx, http.WithLogLevel(zerolog.Disabled), http.WithAddress(address), - http.WithTimeout(timeout), + http.WithTimeout(conn.timeout), + http.WithCustomSpecSupport(conn.customSpecSupport), ) if err != nil { return nil, errors.Wrap(err, "failed to connect to beacon node")