diff --git a/golang/cosmos/x/swingset/keeper/grpc_query.go b/golang/cosmos/x/swingset/keeper/grpc_query.go index a15edc66ab5..4bbb7cc96fd 100644 --- a/golang/cosmos/x/swingset/keeper/grpc_query.go +++ b/golang/cosmos/x/swingset/keeper/grpc_query.go @@ -70,12 +70,18 @@ func (k Querier) ChunkedArtifactStatus(c context.Context, req *types.QueryChunke msg := k.GetPendingBundleInstall(ctx, req.ChunkedArtifactId) if msg == nil { - return nil, status.Error(codes.NotFound, "pending chunked artifact not found") + return nil, status.Errorf(codes.NotFound, + "no pending chunked artifact with id %d; it may have expired or been finalized", + req.ChunkedArtifactId, + ) } can := k.GetChunkedArtifactNode(ctx, req.ChunkedArtifactId) if can == nil { - return nil, status.Error(codes.NotFound, "pending chunked artifact node not found") + return nil, status.Errorf(codes.NotFound, + "pending chunked artifact %d exists but its tracking node is missing (possible state inconsistency)", + req.ChunkedArtifactId, + ) } return &types.QueryChunkedArtifactStatusResponse{ diff --git a/golang/cosmos/x/swingset/types/params.go b/golang/cosmos/x/swingset/types/params.go index 6c308f83758..06353cf5ea0 100644 --- a/golang/cosmos/x/swingset/types/params.go +++ b/golang/cosmos/x/swingset/types/params.go @@ -20,6 +20,8 @@ var ( ParamStoreKeyVatCleanupBudget = []byte("vat_cleanup_budget") ParamStoreKeyBundleUncompressedSizeLimitBytes = []byte("bundle_uncompressed_size_limit_bytes") ParamStoreKeyChunkSizeLimitBytes = []byte("chunk_size_limit_bytes") + ParamStoreKeyInstallationDeadlineSeconds = []byte("installation_deadline_seconds") + ParamStoreKeyInstallationDeadlineBlocks = []byte("installation_deadline_blocks") ) func NewStringBeans(key string, beans sdkmath.Uint) StringBeans { @@ -59,6 +61,8 @@ func DefaultParams() Params { VatCleanupBudget: DefaultVatCleanupBudget, BundleUncompressedSizeLimitBytes: DefaultBundleUncompressedSizeLimitBytes, ChunkSizeLimitBytes: DefaultChunkSizeLimitBytes, + InstallationDeadlineSeconds: DefaultInstallationDeadlineSeconds, // 86400 (24h) + InstallationDeadlineBlocks: DefaultInstallationDeadlineBlocks, // -1 (unlimited) } } @@ -78,6 +82,8 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(ParamStoreKeyVatCleanupBudget, &p.VatCleanupBudget, validateVatCleanupBudget), paramtypes.NewParamSetPair(ParamStoreKeyBundleUncompressedSizeLimitBytes, &p.BundleUncompressedSizeLimitBytes, validateBundleUncompressedSizeLimitBytes), paramtypes.NewParamSetPair(ParamStoreKeyChunkSizeLimitBytes, &p.ChunkSizeLimitBytes, validateChunkSizeLimitBytes), + paramtypes.NewParamSetPair(ParamStoreKeyInstallationDeadlineSeconds, &p.InstallationDeadlineSeconds, validateInstallationDeadlineSeconds), + paramtypes.NewParamSetPair(ParamStoreKeyInstallationDeadlineBlocks, &p.InstallationDeadlineBlocks, validateInstallationDeadlineBlocks), } } @@ -101,6 +107,12 @@ func (p Params) ValidateBasic() error { if err := validateVatCleanupBudget(p.VatCleanupBudget); err != nil { return err } + if err := validateInstallationDeadlineBlocks(p.InstallationDeadlineBlocks); err != nil { + return err + } + if err := validateInstallationDeadlineSeconds(p.InstallationDeadlineSeconds); err != nil { + return err + } if err := validateBundleUncompressedSizeLimitBytes(p.BundleUncompressedSizeLimitBytes); err != nil { return err } @@ -219,6 +231,34 @@ func validateChunkSizeLimitBytes(i interface{}) error { return nil } +func validateInstallationDeadlineSeconds(i interface{}) error { + value, ok := i.(int64) + if !ok { + return fmt.Errorf("installation_deadline_seconds must be int64, got %#v", i) + } + if value < -1 { + return fmt.Errorf( + "installation_deadline_seconds must be -1 (unlimited), 0 (expire immediately), or positive, got %d", + value, + ) + } + return nil +} + +func validateInstallationDeadlineBlocks(i interface{}) error { + value, ok := i.(int64) + if !ok { + return fmt.Errorf("installation_deadline_blocks must be int64, got %#v", i) + } + if value < -1 { + return fmt.Errorf( + "installation_deadline_blocks must be -1 (unlimited), 0 (expire immediately), or positive, got %d", + value, + ) + } + return nil +} + // UpdateParams appends any missing params, configuring them to their defaults, // then returning the updated params or an error. // Existing params are not diff --git a/golang/cosmos/x/swingset/types/params_test.go b/golang/cosmos/x/swingset/types/params_test.go index c346f844087..9cbddb78825 100644 --- a/golang/cosmos/x/swingset/types/params_test.go +++ b/golang/cosmos/x/swingset/types/params_test.go @@ -1,6 +1,7 @@ package types import ( + "strings" "reflect" "testing" @@ -211,3 +212,64 @@ func TestValidateParams(t *testing.T) { t.Errorf("unexpected ValidateBasic() error with empty VatCleanupBudget: %v", params.VatCleanupBudget) } } + +func TestValidateInstallationDeadlineBounds(t *testing.T) { + const minInt64 int64 = -1 << 63 + + for _, tc := range []struct { + name string + seconds int64 + blocks int64 + wantErrSubstr string + }{ + {name: "both unlimited", seconds: -1, blocks: -1}, + {name: "both immediate", seconds: 0, blocks: 0}, + {name: "both positive", seconds: 1, blocks: 1}, + { + name: "seconds below minimum", + seconds: -2, + blocks: -1, + wantErrSubstr: "installation_deadline_seconds must be -1 (unlimited), 0 (expire immediately), or positive, got -2", + }, + { + name: "blocks below minimum", + seconds: -1, + blocks: -2, + wantErrSubstr: "installation_deadline_blocks must be -1 (unlimited), 0 (expire immediately), or positive, got -2", + }, + { + name: "seconds at int64 minimum", + seconds: minInt64, + blocks: -1, + wantErrSubstr: "installation_deadline_seconds must be -1 (unlimited), 0 (expire immediately), or positive, got -9223372036854775808", + }, + { + name: "blocks at int64 minimum", + seconds: -1, + blocks: minInt64, + wantErrSubstr: "installation_deadline_blocks must be -1 (unlimited), 0 (expire immediately), or positive, got -9223372036854775808", + }, + } { + t.Run(tc.name, func(t *testing.T) { + params := DefaultParams() + params.BootstrapVatConfig = "foo" + params.FeeUnitPrice = sdk.NewCoins(sdk.NewInt64Coin("denom", 789)) + params.InstallationDeadlineSeconds = tc.seconds + params.InstallationDeadlineBlocks = tc.blocks + + err := params.ValidateBasic() + if tc.wantErrSubstr == "" { + if err != nil { + t.Fatalf("unexpected ValidateBasic() error: %v", err) + } + return + } + if err == nil { + t.Fatal("expected ValidateBasic() error, got nil") + } + if !strings.Contains(err.Error(), tc.wantErrSubstr) { + t.Fatalf("expected error containing %q, got %q", tc.wantErrSubstr, err) + } + }) + } +}