diff --git a/CHANGELOG.md b/CHANGELOG.md index cff9187..beb90d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ dev: - add "--generate-keystore" option for "account derive" - update "validator exit" command to be able to generate multiple exits - support for 12-word and 18-word mnemonics with single-word (no whitespace) passphrases + - add JSON output for "validator expectation" 1.30.0: - add "chain spec" command diff --git a/cmd/validator/expectation/command.go b/cmd/validator/expectation/command.go index ff33a8e..c8ddf67 100644 --- a/cmd/validator/expectation/command.go +++ b/cmd/validator/expectation/command.go @@ -15,9 +15,12 @@ package validatorexpectation import ( "context" + "encoding/json" + "fmt" "time" eth2client "github.com/attestantio/go-eth2-client" + "github.com/hako/durafmt" "github.com/pkg/errors" "github.com/spf13/viper" ) @@ -26,6 +29,7 @@ type command struct { quiet bool verbose bool debug bool + json bool // Beacon node connection. timeout time.Duration @@ -38,18 +42,43 @@ type command struct { // Data access. eth2Client eth2client.Service validatorsProvider eth2client.ValidatorsProvider - activeValidators int - // Output. + // Results. + res *results +} + +type results struct { + activeValidators uint64 timeBetweenProposals time.Duration timeBetweenSyncCommittees time.Duration } +type resultsJSON struct { + ActiveValidators string `json:"active_validators"` + TimeBetweenProposals string `json:"time_between_proposals"` + SecsBetweenProposals string `json:"secs_between_proposals"` + TimeBetweenSyncCommittees string `json:"time_between_sync_committees"` + SecsBetweenSyncCommittees string `json:"secs_between_sync_committees"` +} + +func (r *results) MarshalJSON() ([]byte, error) { + data := &resultsJSON{ + ActiveValidators: fmt.Sprintf("%d", r.activeValidators), + TimeBetweenProposals: durafmt.Parse(r.timeBetweenProposals).LimitFirstN(2).String(), + SecsBetweenProposals: fmt.Sprintf("%d", int64(r.timeBetweenProposals.Seconds())), + TimeBetweenSyncCommittees: durafmt.Parse(r.timeBetweenSyncCommittees).LimitFirstN(2).String(), + SecsBetweenSyncCommittees: fmt.Sprintf("%d", int64(r.timeBetweenSyncCommittees.Seconds())), + } + return json.Marshal(data) +} + func newCommand(_ context.Context) (*command, error) { c := &command{ quiet: viper.GetBool("quiet"), verbose: viper.GetBool("verbose"), debug: viper.GetBool("debug"), + json: viper.GetBool("json"), + res: &results{}, } // Timeout. diff --git a/cmd/validator/expectation/output.go b/cmd/validator/expectation/output.go index de2b67a..2636269 100644 --- a/cmd/validator/expectation/output.go +++ b/cmd/validator/expectation/output.go @@ -1,4 +1,4 @@ -// Copyright © 2021 Weald Technology Trading. +// Copyright © 2021, 2023 Weald Technology Trading. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -15,24 +15,42 @@ package validatorexpectation import ( "context" + "encoding/json" + "fmt" "strings" "github.com/hako/durafmt" ) -func (c *command) output(_ context.Context) (string, error) { +func (c *command) output(ctx context.Context) (string, error) { if c.quiet { return "", nil } + if c.json { + return c.outputJSON(ctx) + } + return c.outputTxt(ctx) +} + +func (c *command) outputJSON(_ context.Context) (string, error) { + data, err := json.Marshal(c.res) + if err != nil { + return "", err + } + + return fmt.Sprintf("%s\n", string(data)), nil +} + +func (c *command) outputTxt(_ context.Context) (string, error) { builder := strings.Builder{} builder.WriteString("Expected time between block proposals: ") - builder.WriteString(durafmt.Parse(c.timeBetweenProposals).LimitFirstN(2).String()) + builder.WriteString(durafmt.Parse(c.res.timeBetweenProposals).LimitFirstN(2).String()) builder.WriteString("\n") builder.WriteString("Expected time between sync committees: ") - builder.WriteString(durafmt.Parse(c.timeBetweenSyncCommittees).LimitFirstN(2).String()) + builder.WriteString(durafmt.Parse(c.res.timeBetweenSyncCommittees).LimitFirstN(2).String()) builder.WriteString("\n") return builder.String(), nil diff --git a/cmd/validator/expectation/process.go b/cmd/validator/expectation/process.go index 2faad08..c4df022 100644 --- a/cmd/validator/expectation/process.go +++ b/cmd/validator/expectation/process.go @@ -1,4 +1,4 @@ -// Copyright © 2021 Weald Technology Trading. +// Copyright © 2021, 2023 Weald Technology Trading. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -31,7 +31,7 @@ func (c *command) process(ctx context.Context) error { } if c.debug { - fmt.Printf("Active validators: %d\n", c.activeValidators) + fmt.Printf("Active validators: %d\n", c.res.activeValidators) } if err := c.calculateProposalChance(ctx); err != nil { @@ -59,7 +59,7 @@ func (c *command) calculateProposalChance(ctx context.Context) error { return errors.New("SECONDS_PER_SLOT of incorrect type") } - c.timeBetweenProposals = slotDuration * time.Duration(c.activeValidators) / time.Duration(c.validators) + c.res.timeBetweenProposals = slotDuration * time.Duration(c.res.activeValidators) / time.Duration(c.validators) return nil } @@ -109,12 +109,12 @@ func (c *command) calculateSyncCommitteeChance(ctx context.Context) error { return errors.New("EPOCHS_PER_SYNC_COMMITTEE_PERIOD of incorrect type") } - periodsBetweenSyncCommittees := uint64(c.activeValidators) / syncCommitteeSize + periodsBetweenSyncCommittees := c.res.activeValidators / syncCommitteeSize if c.debug { fmt.Printf("Sync committee periods between inclusion: %d\n", periodsBetweenSyncCommittees) } - c.timeBetweenSyncCommittees = slotDuration * time.Duration(slotsPerEpoch*epochsPerPeriod) * time.Duration(periodsBetweenSyncCommittees) / time.Duration(c.validators) + c.res.timeBetweenSyncCommittees = slotDuration * time.Duration(slotsPerEpoch*epochsPerPeriod) * time.Duration(periodsBetweenSyncCommittees) / time.Duration(c.validators) return nil } @@ -157,7 +157,7 @@ func (c *command) setup(ctx context.Context) error { for _, validator := range validators { if validator.Validator.ActivationEpoch <= currentEpoch && validator.Validator.ExitEpoch > currentEpoch { - c.activeValidators++ + c.res.activeValidators++ } }