Skip to content

Commit

Permalink
feat: add minimum version check
Browse files Browse the repository at this point in the history
  • Loading branch information
gartnera committed Jan 2, 2025
1 parent f0c4267 commit ae0816e
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 33 deletions.
19 changes: 13 additions & 6 deletions cmd/zetaclientd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ func Start(_ *cobra.Command, _ []string) error {
Telemetry: telemetry,
}

tss, err := zetatss.Setup(ctx, tssSetupProps, logger.Std)
if err != nil {
return errors.Wrap(err, "unable to setup TSS service")
}

isObserver, err := isObserverNode(ctx, zetacoreClient)
switch {
case err != nil:
Expand All @@ -115,11 +110,23 @@ func Start(_ *cobra.Command, _ []string) error {
graceful.ShutdownNow()
})

maintenance.NewShutdownListener(zetacoreClient, logger.Std).Listen(ctx, func() {
shutdownListener := maintenance.NewShutdownListener(zetacoreClient, logger.Std)
err = shutdownListener.RunPreStartCheck(ctx)
if err != nil {
return errors.Wrap(err, "pre start check failed")
}
shutdownListener.Listen(ctx, func() {
logger.Std.Info().Msg("Shutdown listener received an action to shutdown zetaclientd.")
graceful.ShutdownNow()
})

// This will start p2p communication so it should only happen after
// preflight checks have completed
tss, err := zetatss.Setup(ctx, tssSetupProps, logger.Std)
if err != nil {
return errors.Wrap(err, "unable to setup TSS service")
}

// CreateSignerMap: This creates a map of all signers for each chain.
// Each signer is responsible for signing transactions for a particular chain
signerMap, err := orchestrator.CreateSignerMap(ctx, tss, logger)
Expand Down
5 changes: 5 additions & 0 deletions docs/openapi/openapi.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58115,6 +58115,11 @@ definitions:
description: |-
Offset from the zetacore block time to initiate signing.
Should be calculated and set based on max(zetaclient_core_block_latency).
minimum_version:
type: string
description: |-
Minimum version of zetaclient that is allowed to run. This must be either
a valid semver string (v23.0.1) or empty.
description: Flags for the top-level operation of zetaclient.
observerPendingNonces:
type: object
Expand Down
4 changes: 4 additions & 0 deletions proto/zetachain/zetacore/observer/operational.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ message OperationalFlags {
// Should be calculated and set based on max(zetaclient_core_block_latency).
google.protobuf.Duration signer_block_time_offset = 2
[ (gogoproto.stdduration) = true ];

// Minimum version of zetaclient that is allowed to run. This must be either
// a valid semver string (v23.0.1) or empty.
string minimum_version = 3;
}
8 changes: 8 additions & 0 deletions typescript/zetachain/zetacore/observer/operational_pb.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export declare class OperationalFlags extends Message<OperationalFlags> {
*/
signerBlockTimeOffset?: Duration;

/**
* Minimum version of zetaclient that is allowed to run. This must be either
* a valid semver string (v23.0.1) or empty.
*
* @generated from field: string minimum_version = 3;
*/
minimumVersion: string;

constructor(data?: PartialMessage<OperationalFlags>);

static readonly runtime: typeof proto3;
Expand Down
5 changes: 5 additions & 0 deletions x/observer/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,9 @@ var (
1140,
"signer block time offset exceeds limit",
)
ErrOperationalFlagsInvalidMinimumVersion = errorsmod.Register(
ModuleName,
1141,
"minimum version is not a valid semver string",
)
)
4 changes: 4 additions & 0 deletions x/observer/types/operational.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"time"

cosmoserrors "cosmossdk.io/errors"
"golang.org/x/mod/semver"
)

const (
Expand All @@ -23,5 +24,8 @@ func (f *OperationalFlags) Validate() error {
return cosmoserrors.Wrapf(ErrOperationalFlagsSignerBlockTimeOffsetLimit, "(%s)", signerBlockTimeOffset)
}
}
if f.MinimumVersion != "" && !semver.IsValid(f.MinimumVersion) {
return ErrOperationalFlagsInvalidMinimumVersion
}

Check warning on line 29 in x/observer/types/operational.go

View check run for this annotation

Codecov / codecov/patch

x/observer/types/operational.go#L28-L29

Added lines #L28 - L29 were not covered by tests
return nil
}
93 changes: 74 additions & 19 deletions x/observer/types/operational.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions x/observer/types/operational_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ func TestOperationalFlags_Validate(t *testing.T) {
of types.OperationalFlags
errContains string
}{
{
name: "empty is valid",
of: types.OperationalFlags{},
},
{
name: "invalid restart height",
of: types.OperationalFlags{
Expand Down Expand Up @@ -48,11 +52,18 @@ func TestOperationalFlags_Validate(t *testing.T) {
},
errContains: types.ErrOperationalFlagsSignerBlockTimeOffsetLimit.Error(),
},
{
name: "minimum version valid",
of: types.OperationalFlags{
MinimumVersion: "v1.1.1",
},
},
{
name: "all flags valid",
of: types.OperationalFlags{
RestartHeight: 1,
SignerBlockTimeOffset: ptr.Ptr(time.Second),
MinimumVersion: "v1.1.1",
},
},
}
Expand Down
67 changes: 60 additions & 7 deletions zetaclient/maintenance/shutdown_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package maintenance

import (
"context"
"fmt"
"strings"
"time"

"cosmossdk.io/errors"
"github.com/rs/zerolog"
"golang.org/x/mod/semver"

"github.com/zeta-chain/node/pkg/bg"
"github.com/zeta-chain/node/pkg/constant"
"github.com/zeta-chain/node/pkg/retry"
observertypes "github.com/zeta-chain/node/x/observer/types"
"github.com/zeta-chain/node/zetaclient/chains/interfaces"
Expand All @@ -22,17 +26,32 @@ type ShutdownListener struct {
logger zerolog.Logger

lastRestartHeightMissed int64
getVersion func() string
}

// NewShutdownListener creates a new ShutdownListener.
func NewShutdownListener(client interfaces.ZetacoreClient, logger zerolog.Logger) *ShutdownListener {
log := logger.With().Str("module", "shutdown_listener").Logger()
return &ShutdownListener{
client: client,
logger: log,
client: client,
logger: log,
getVersion: getVersionDefault,
}
}

// RunPreStartCheck runs any checks that must run before fully starting zetaclient.
// Specifically this should be run before any TSS P2P is started.
func (o *ShutdownListener) RunPreStartCheck(ctx context.Context) error {
operationalFlags, err := o.getOperationalFlagsWithRetry(ctx)
if err != nil {
return errors.Wrap(err, "unable to get initial operational flags")
}

Check warning on line 48 in zetaclient/maintenance/shutdown_listener.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/maintenance/shutdown_listener.go#L47-L48

Added lines #L47 - L48 were not covered by tests
if err := o.checkMinimumVersion(operationalFlags); err != nil {
return err
}
return nil
}

func (o *ShutdownListener) Listen(ctx context.Context, action func()) {
var (
withLogger = bg.WithLogger(o.logger)
Expand All @@ -43,12 +62,9 @@ func (o *ShutdownListener) Listen(ctx context.Context, action func()) {
}

func (o *ShutdownListener) waitForUpdate(ctx context.Context) error {
operationalFlags, err := retry.DoTypedWithBackoffAndRetry(
func() (observertypes.OperationalFlags, error) { return o.client.GetOperationalFlags(ctx) },
retry.DefaultConstantBackoff(),
)
operationalFlags, err := o.getOperationalFlagsWithRetry(ctx)
if err != nil {
return errors.Wrap(err, "unable to get initial operational flags")
return errors.Wrap(err, "get initial operational flags")

Check warning on line 67 in zetaclient/maintenance/shutdown_listener.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/maintenance/shutdown_listener.go#L67

Added line #L67 was not covered by tests
}
if o.handleNewFlags(ctx, operationalFlags) {
return nil
Expand All @@ -74,8 +90,19 @@ func (o *ShutdownListener) waitForUpdate(ctx context.Context) error {
}
}

func (o *ShutdownListener) getOperationalFlagsWithRetry(ctx context.Context) (observertypes.OperationalFlags, error) {
return retry.DoTypedWithBackoffAndRetry(
func() (observertypes.OperationalFlags, error) { return o.client.GetOperationalFlags(ctx) },
retry.DefaultConstantBackoff(),
)
}

// handleNewFlags processes the flags and returns true if a shutdown should be signaled
func (o *ShutdownListener) handleNewFlags(ctx context.Context, f observertypes.OperationalFlags) bool {
if err := o.checkMinimumVersion(f); err != nil {
o.logger.Error().Err(err).Msg("minimum version check")
return true
}
if f.RestartHeight < 1 {
return false
}
Expand Down Expand Up @@ -123,3 +150,29 @@ func (o *ShutdownListener) handleNewFlags(ctx context.Context, f observertypes.O
}
}
}

func (o *ShutdownListener) checkMinimumVersion(f observertypes.OperationalFlags) error {
if f.MinimumVersion != "" {
// we typically store the version without the required v prefix
currentVersion := ensurePrefix(o.getVersion(), "v")
if semver.Compare(currentVersion, f.MinimumVersion) == -1 {
return fmt.Errorf(
"current version (%s) is less than minimum version (%s)",
currentVersion,
f.MinimumVersion,
)
}
}
return nil
}

func getVersionDefault() string {
return constant.Version

Check warning on line 170 in zetaclient/maintenance/shutdown_listener.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/maintenance/shutdown_listener.go#L169-L170

Added lines #L169 - L170 were not covered by tests
}

func ensurePrefix(s, prefix string) string {
if !strings.HasPrefix(s, prefix) {
return prefix + s
}
return s

Check warning on line 177 in zetaclient/maintenance/shutdown_listener.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/maintenance/shutdown_listener.go#L177

Added line #L177 was not covered by tests
}
Loading

0 comments on commit ae0816e

Please sign in to comment.