Skip to content

Commit

Permalink
integrating cwerrors into callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
spoo-bar committed Mar 22, 2024
1 parent 9ae0d64 commit dd13046
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 12 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ func NewArchwayApp(
rewards.NewAppModule(app.appCodec, app.Keepers.RewardsKeeper),
cwfees.NewAppModule(app.Keepers.CWFeesKeeper),
genmsg.NewAppModule(app.MsgServiceRouter()),
callback.NewAppModule(app.appCodec, app.Keepers.CallbackKeeper, app.Keepers.WASMKeeper),
callback.NewAppModule(app.appCodec, app.Keepers.CallbackKeeper, app.Keepers.WASMKeeper, app.Keepers.CWErrorsKeeper),
cwica.NewAppModule(appCodec, app.Keepers.CWICAKeeper, app.Keepers.AccountKeeper),
cwerrors.NewAppModule(app.appCodec, app.Keepers.CWErrorsKeeper, app.Keepers.WASMKeeper),
crisis.NewAppModule(&app.Keepers.CrisisKeeper, skipGenesisInvariants, app.getSubspace(crisistypes.ModuleName)), // always be last to make sure that it checks for all invariants and not only part of them
Expand Down
14 changes: 14 additions & 0 deletions proto/archway/callback/v1/errors.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
syntax = "proto3";
package archway.callback.v1;

import "gogoproto/gogo.proto";

option go_package = "github.com/archway-network/archway/x/callback/types";

// ModuleErrors defines the module level error codes
enum ModuleErrors {
// ERR_UNKNOWN is the default error code
ERR_UNKNOWN = 0;
// ERR_OUT_OF_GAS is the error code when the contract callback exceeds the gas limit allowed by the module
ERR_OUT_OF_GAS = 1;
}
29 changes: 26 additions & 3 deletions x/callback/abci.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package callback

import (
"errors"

"cosmossdk.io/collections"
abci "github.com/cometbft/cometbft/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/archway-network/archway/pkg"
"github.com/archway-network/archway/x/callback/keeper"
"github.com/archway-network/archway/x/callback/types"
)

// EndBlocker fetches all the callbacks registered for the current block height and executes them
func EndBlocker(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected) []abci.ValidatorUpdate {
k.IterateCallbacksByHeight(ctx, ctx.BlockHeight(), callbackExec(ctx, k, wk))
func EndBlocker(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected, ek types.ErrorsKeeperExpected) []abci.ValidatorUpdate {
k.IterateCallbacksByHeight(ctx, ctx.BlockHeight(), callbackExec(ctx, k, wk, ek))
return nil
}

// callbackExec returns a function which executes the callback and deletes it from state after execution
func callbackExec(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected) func(types.Callback) bool {
func callbackExec(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected, ek types.ErrorsKeeperExpected) func(types.Callback) bool {
logger := k.Logger(ctx)
return func(callback types.Callback) bool {
// creating CallbackMsg which is encoded to json and passed as input to contract execution
Expand Down Expand Up @@ -53,12 +56,32 @@ func callbackExec(ctx sdk.Context, k keeper.Keeper, wk types.WasmKeeperExpected)
err.Error(),
)

errorCode := types.ModuleErrors_ERR_UNKNOWN
// check if out of gas error
var outOfGasError sdkerrors.Error

Check failure on line 61 in x/callback/abci.go

View workflow job for this annotation

GitHub Actions / lint

SA1019: sdkerrors.Error is deprecated: the type has been moved to cosmossdk.io/errors module. Please use the above module instead of this package. (staticcheck)
if errors.As(err, &outOfGasError) && outOfGasError.Is(sdkerrors.ErrOutOfGas) {
errorCode = types.ModuleErrors_ERR_OUT_OF_GAS
}

// Save error in the errors keeper
sudoErr := types.NewSudoError(
errorCode,
callback.ContractAddress,
callbackMsgString,
err.Error(),
)
err := ek.SetError(ctx, sudoErr)
if err != nil {
panic(err)
}

// This is because gasUsed amount returned is greater than the gas limit. cuz ofc.
// so we set it to callback.MaxGasLimit so when we do txFee refund, we arent trying to refund more than we should
// e.g if callback.MaxGasLimit is 10, but gasUsed is 100, we need to use 10 to calculate txFeeRefund.
// else the module will pay back more than it took from the user 💀
// TLDR; this ensures in case of "out of gas error", we keep all txFees and refund nothing.
gasUsed = callback.MaxGasLimit

} else {
logger.Info(
"callback executed successfully",
Expand Down
6 changes: 3 additions & 3 deletions x/callback/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestEndBlocker(t *testing.T) {
// Increment block height and run end blocker
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
require.Equal(t, ctx.BlockHeight(), reqMsg.CallbackHeight)
_ = callbackabci.EndBlocker(ctx, keeper, chain.GetApp().Keepers.WASMKeeper)
_ = callbackabci.EndBlocker(ctx, keeper, chain.GetApp().Keepers.WASMKeeper, chain.GetApp().Keepers.CWErrorsKeeper)

// Checking if the count value is as expected
count := getCount(t, chain, ctx, contractAddr)
Expand Down Expand Up @@ -118,7 +118,7 @@ func TestEndBlocker(t *testing.T) {

// Increment block height and run end blocker
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
_ = callbackabci.EndBlocker(ctx, keeper, chain.GetApp().Keepers.WASMKeeper)
_ = callbackabci.EndBlocker(ctx, keeper, chain.GetApp().Keepers.WASMKeeper, chain.GetApp().Keepers.CWErrorsKeeper)

// Checking if the count value has incremented.
// Should have incremented as the callback should have access to higher gas limit as it was registered before the gas limit was reduced
Expand All @@ -140,7 +140,7 @@ func TestEndBlocker(t *testing.T) {

// Increment block height and run end blocker
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
_ = callbackabci.EndBlocker(ctx, keeper, chain.GetApp().Keepers.WASMKeeper)
_ = callbackabci.EndBlocker(ctx, keeper, chain.GetApp().Keepers.WASMKeeper, chain.GetApp().Keepers.CWErrorsKeeper)

// Checking if the count value has incremented. Should not have incremented as the callback failed due to out of gas error
count = getCount(t, chain, ctx, contractAddr)
Expand Down
10 changes: 6 additions & 4 deletions x/callback/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,18 @@ func (a AppModuleBasic) GetQueryCmd() *cobra.Command {
type AppModule struct {
AppModuleBasic

keeper keeper.Keeper
wasmKeeper types.WasmKeeperExpected
keeper keeper.Keeper
wasmKeeper types.WasmKeeperExpected
errorsKeeper types.ErrorsKeeperExpected
}

// NewAppModule creates a new AppModule object.
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, wk types.WasmKeeperExpected) AppModule {
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, wk types.WasmKeeperExpected, ek types.ErrorsKeeperExpected) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{cdc: cdc},
keeper: keeper,
wasmKeeper: wk,
errorsKeeper: ek,
}
}

Expand Down Expand Up @@ -129,5 +131,5 @@ func (a AppModule) BeginBlock(ctx sdk.Context, block abci.RequestBeginBlock) {}

// EndBlock returns the end blocker for the module. It returns no validator updates.
func (a AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return EndBlocker(ctx, a.keeper, a.wasmKeeper)
return EndBlocker(ctx, a.keeper, a.wasmKeeper, a.errorsKeeper)
}
17 changes: 16 additions & 1 deletion x/callback/types/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package types

import errorsmod "cosmossdk.io/errors"
import (
errorsmod "cosmossdk.io/errors"

cwerrortypes "github.com/archway-network/archway/x/cwerrors/types"
)

var (
DefaultCodespace = ModuleName
Expand All @@ -14,3 +18,14 @@ var (
ErrCallbackHeightTooFarInFuture = errorsmod.Register(DefaultCodespace, 9, "callback request height is too far in the future")
ErrBlockFilled = errorsmod.Register(DefaultCodespace, 10, "block filled with max capacity of callbacks")
)

// NewSudoError creates a new sudo error instance to pass on to the errors module
func NewSudoError(errorCode ModuleErrors, contractAddr string, inputPayload string, errMsg string) cwerrortypes.SudoError {
return cwerrortypes.SudoError{
ModuleName: ModuleName,
ErrorCode: uint32(errorCode),
ContractAddress: contractAddr,
InputPayload: inputPayload,
ErrorMessage: errMsg,
}
}
73 changes: 73 additions & 0 deletions x/callback/types/errors.pb.go

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

5 changes: 5 additions & 0 deletions x/callback/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
wasmdtypes "github.com/CosmWasm/wasmd/x/wasm/types"

cwerrortypes "github.com/archway-network/archway/x/cwerrors/types"
rewardstypes "github.com/archway-network/archway/x/rewards/types"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -25,3 +26,7 @@ type BankKeeperExpected interface {
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
BlockedAddr(addr sdk.AccAddress) bool
}

type ErrorsKeeperExpected interface {
SetError(ctx sdk.Context, sudoErr cwerrortypes.SudoError) error
}

0 comments on commit dd13046

Please sign in to comment.