From 8a01b10b6afeed8e96b7cad779acd0f011713c20 Mon Sep 17 00:00:00 2001 From: ccamel Date: Fri, 4 Oct 2024 10:58:07 +0200 Subject: [PATCH] refactor(logic): use accounts iterator for balance-related predicates --- app/app.go | 2 + x/logic/keeper/interpreter.go | 9 +- x/logic/keeper/keeper.go | 43 +++--- x/logic/predicate/bank.go | 165 ++++++++++----------- x/logic/predicate/util.go | 70 ++++++++- x/logic/testutil/expected_keepers_mocks.go | 54 +++++-- x/logic/types/context.go | 4 + x/logic/types/expected_keepers.go | 7 +- x/logic/util/prolog.go | 40 +++-- 9 files changed, 242 insertions(+), 152 deletions(-) diff --git a/app/app.go b/app/app.go index 31d40dea..2a9eba08 100644 --- a/app/app.go +++ b/app/app.go @@ -568,10 +568,12 @@ func New( app.LogicKeeper = *logicmodulekeeper.NewKeeper( appCodec, + app.interfaceRegistry, keys[logicmoduletypes.StoreKey], keys[logicmoduletypes.MemStoreKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, + authkeeper.NewQueryServer(app.AccountKeeper), app.BankKeeper, app.provideFS, ) diff --git a/x/logic/keeper/interpreter.go b/x/logic/keeper/interpreter.go index 2be2f176..d25d79ec 100644 --- a/x/logic/keeper/interpreter.go +++ b/x/logic/keeper/interpreter.go @@ -38,10 +38,11 @@ type writerStringer interface { } func (k Keeper) enhanceContext(ctx context.Context) context.Context { - sdkCtx := sdk.UnwrapSDKContext(ctx) - sdkCtx = sdkCtx.WithValue(types.AuthKeeperContextKey, k.authKeeper) - sdkCtx = sdkCtx.WithValue(types.BankKeeperContextKey, k.bankKeeper) - return sdkCtx + return sdk.UnwrapSDKContext(ctx). + WithValue(types.InterfaceRegistryContextKey, k.interfaceRegistry). + WithValue(types.AuthKeeperContextKey, k.authKeeper). + WithValue(types.AuthQueryServiceContextKey, k.authQueryService). + WithValue(types.BankKeeperContextKey, k.bankKeeper) } func (k Keeper) execute( diff --git a/x/logic/keeper/keeper.go b/x/logic/keeper/keeper.go index c7659499..d5a3b9ab 100644 --- a/x/logic/keeper/keeper.go +++ b/x/logic/keeper/keeper.go @@ -7,6 +7,7 @@ import ( storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/axone-protocol/axoned/v10/x/logic/fs" @@ -15,40 +16,38 @@ import ( type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey + cdc codec.BinaryCodec + interfaceRegistry cdctypes.InterfaceRegistry + storeKey storetypes.StoreKey + memKey storetypes.StoreKey // the address capable of executing a MsgUpdateParams message. Typically, this should be the x/gov module account. authority sdk.AccAddress - authKeeper types.AccountKeeper - bankKeeper types.BankKeeper - fsProvider fs.Provider + authKeeper types.AccountKeeper + authQueryService types.AuthQueryService + bankKeeper types.BankKeeper + fsProvider fs.Provider } ) -func NewKeeper( - cdc codec.BinaryCodec, - storeKey, - memKey storetypes.StoreKey, - authority sdk.AccAddress, - authKeeper types.AccountKeeper, - bankKeeper types.BankKeeper, - fsProvider fs.Provider, -) *Keeper { +func NewKeeper(cdc codec.BinaryCodec, interfaceRegistry cdctypes.InterfaceRegistry, storeKey, memKey storetypes.StoreKey, + authority sdk.AccAddress, authKeeper types.AccountKeeper, authQueryService types.AuthQueryService, bankKeeper types.BankKeeper, + fsProvider fs.Provider) *Keeper { // ensure gov module account is set and is not nil if err := sdk.VerifyAddressFormat(authority); err != nil { panic(err) } return &Keeper{ - cdc: cdc, - storeKey: storeKey, - memKey: memKey, - authority: authority, - authKeeper: authKeeper, - bankKeeper: bankKeeper, - fsProvider: fsProvider, + cdc: cdc, + interfaceRegistry: interfaceRegistry, + storeKey: storeKey, + memKey: memKey, + authority: authority, + authKeeper: authKeeper, + authQueryService: authQueryService, + bankKeeper: bankKeeper, + fsProvider: fsProvider, } } diff --git a/x/logic/predicate/bank.go b/x/logic/predicate/bank.go index 5ed2638c..1d28eed9 100644 --- a/x/logic/predicate/bank.go +++ b/x/logic/predicate/bank.go @@ -4,6 +4,7 @@ import ( "context" "github.com/ichiban/prolog/engine" + "github.com/samber/lo" sdk "github.com/cosmos/cosmos-sdk/types" @@ -15,10 +16,10 @@ import ( // // The signature is as follows: // -// bank_balances(?Account, ?Balances) +// bank_balances(?Address, ?Balances) // // where: -// - Account represents the account address (in Bech32 format). +// - Address represents the account address (in Bech32 format). // - Balances represents the balances of the account as a list of pairs of coin denomination and amount. // // # Examples: @@ -31,29 +32,18 @@ import ( // // # Query the first balance of the given account by unifying the denomination and amount with the given terms. // - bank_balances('axone1ffd5wx65l407yvm478cxzlgygw07h79sw4jwpa', [-(D, A), _]). -func BankBalances(vm *engine.VM, account, balances engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { - return fetchBalances( - account, - balances, - vm, - env, - cont, - func(ctx sdk.Context, bankKeeper types.BankKeeper, coins sdk.Coins, address sdk.AccAddress) sdk.Coins { - if coins != nil { - return coins - } - return AllBalancesSorted(ctx, bankKeeper, address) - }) +func BankBalances(vm *engine.VM, address, balances engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { + return fetchBalances(vm, address, balances, AllBalancesSorted, cont, env) } // BankSpendableBalances is a predicate which unifies the given terms with the list of spendable coins of the given account. // // The signature is as follows: // -// bank_spendable_balances(?Account, ?Balances) +// bank_spendable_balances(?Address, ?Balances) // // where: -// - Account represents the account address (in Bech32 format). +// - Address represents the account address (in Bech32 format). // - Balances represents the spendable balances of the account as a list of pairs of coin denomination and amount. // // # Examples: @@ -66,26 +56,18 @@ func BankBalances(vm *engine.VM, account, balances engine.Term, cont engine.Cont // // # Query the first spendable balances of the given account by unifying the denomination and amount with the given terms. // - bank_spendable_balances('axone1ffd5wx65l407yvm478cxzlgygw07h79sw4jwpa', [-(D, A), _]). -func BankSpendableBalances(vm *engine.VM, account, balances engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { - return fetchBalances( - account, - balances, - vm, - env, - cont, - func(ctx sdk.Context, bankKeeper types.BankKeeper, _ sdk.Coins, address sdk.AccAddress) sdk.Coins { - return SpendableCoinsSorted(ctx, bankKeeper, address) - }) +func BankSpendableBalances(vm *engine.VM, address, balances engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { + return fetchBalances(vm, address, balances, SpendableCoinsSorted, cont, env) } // BankLockedBalances is a predicate which unifies the given terms with the list of locked coins of the given account. // // The signature is as follows: // -// bank_locked_balances(?Account, ?Balances) +// bank_locked_balances(?Address, ?Balances) // // where: -// - Account represents the account address (in Bech32 format). +// - Address represents the account address (in Bech32 format). // - Balances represents the locked balances of the account as a list of pairs of coin denomination and amount. // // # Examples: @@ -98,40 +80,70 @@ func BankSpendableBalances(vm *engine.VM, account, balances engine.Term, cont en // // # Query the first locked balances of the given account by unifying the denomination and amount with the given terms. // - bank_locked_balances('axone1ffd5wx65l407yvm478cxzlgygw07h79sw4jwpa', [-(D, A), _]). -func BankLockedBalances(vm *engine.VM, account, balances engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { - return fetchBalances( - account, - balances, - vm, - env, - cont, - func(ctx sdk.Context, bankKeeper types.BankKeeper, _ sdk.Coins, address sdk.AccAddress) sdk.Coins { - return LockedCoinsSorted(ctx, bankKeeper, address) - }) +func BankLockedBalances(vm *engine.VM, address, balances engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { + return fetchBalances(vm, address, balances, LockedCoinsSorted, cont, env) } -func getBech32(env *engine.Env, account engine.Term) (sdk.AccAddress, error) { - switch acc := env.Resolve(account).(type) { - case engine.Variable: - case engine.Atom: - addr, err := sdk.AccAddressFromBech32(acc.String()) +// account is a predicate which unifies the given term with the list of account addresses existing in the blockchain. +// +// The signature is as follows: +// +// account(?Address) +// +// where: +// - Address represents the account address (in Bech32 format). +// +// # Examples: +// +// # Query the locked coins of the account. +// - account('axone1ffd5wx65l407yvm478cxzlgygw07h79sw4jwpa', X). +// +// # Query the all accounts existing in the blockchain. +// - account(Acc). +func account(vm *engine.VM, address engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise { + return engine.Delay(func(ctx context.Context) *engine.Promise { + sdkContext, err := prolog.UnwrapSDKContext(ctx, env) if err != nil { - return nil, prolog.WithError(engine.ResourceError(prolog.ResourceModule("bank"), env), err, env) + return engine.Error(err) } - return addr, nil - default: - return nil, engine.TypeError(prolog.AtomTypeAtom, account, env) - } - return sdk.AccAddress(nil), nil + authKeeper := sdkContext.Value(types.AuthKeeperContextKey).(types.AccountKeeper) + authQueryService := sdkContext.Value(types.AuthQueryServiceContextKey).(types.AuthQueryService) + + switch acc := env.Resolve(address).(type) { + case engine.Atom: + return engine.Delay( + func(ctx context.Context) *engine.Promise { + addr, err := sdk.AccAddressFromBech32(acc.String()) + if err != nil { + return engine.Error(prolog.WithError(engine.ResourceError(prolog.ResourceModule("bank"), env), err, env)) + } + if exists := authKeeper.GetAccount(ctx, addr) != nil; !exists { + return engine.Bool(false) + } + + return cont(env) + }) + case engine.Variable: + return engine.DelaySeq(IterMap(Accounts(ctx, authQueryService), + func(it lo.Tuple2[sdk.AccountI, error]) engine.PromiseFunc { + return func(_ context.Context) *engine.Promise { + addr, err := lo.Unpack2(it) + if err != nil { + return engine.Error(err) + } + return engine.Unify(vm, address, engine.NewAtom(addr.GetAddress().String()), cont, env) + } + })) + default: + return engine.Error(engine.TypeError(prolog.AtomTypeAtom, address, env)) + } + }) } -func fetchBalances( - account, balances engine.Term, - vm *engine.VM, - env *engine.Env, - cont engine.Cont, - coinsFn func(ctx sdk.Context, bankKeeper types.BankKeeper, coins sdk.Coins, address sdk.AccAddress) sdk.Coins, -) *engine.Promise { +// fetchBalances is a helper function to fetch the balances of the given account using a given function which returns the coins for +// the given address. +func fetchBalances(vm *engine.VM, address engine.Term, balances engine.Term, coinsFn func(ctx context.Context, + bankKeeper types.BankKeeper, address sdk.AccAddress) sdk.Coins, cont engine.Cont, env *engine.Env) *engine.Promise { return engine.Delay(func(ctx context.Context) *engine.Promise { sdkContext, err := prolog.UnwrapSDKContext(ctx, env) if err != nil { @@ -139,38 +151,15 @@ func fetchBalances( } bankKeeper := sdkContext.Value(types.BankKeeperContextKey).(types.BankKeeper) - bech32Addr, err := getBech32(env, account) - if err != nil { - return engine.Error(err) - } - - if bech32Addr != nil { - fetchedBalances := coinsFn(sdkContext, bankKeeper, nil, bech32Addr) - return engine.Unify(vm, CoinsToTerm(fetchedBalances), balances, cont, env) - } + return account(vm, address, func(env *engine.Env) *engine.Promise { + switch acc := env.Resolve(address).(type) { + case engine.Atom: + coins := coinsFn(ctx, bankKeeper, sdk.MustAccAddressFromBech32(acc.String())) - allBalances := bankKeeper.GetAccountsBalances(sdkContext) - promises := make([]func(ctx context.Context) *engine.Promise, 0, len(allBalances)) - for _, balance := range allBalances { - address := balance.Address - bech32Addr, err = sdk.AccAddressFromBech32(address) - if err != nil { - return engine.Error(prolog.WithError(engine.ResourceError(prolog.ResourceModule("bank"), env), err, env)) + return engine.Unify(vm, CoinsToTerm(coins), balances, cont, env) + default: + return engine.Error(engine.TypeError(prolog.AtomTypeAtom, address, env)) } - coins := coinsFn(sdkContext, bankKeeper, balance.Coins, bech32Addr) - - promises = append( - promises, - func(_ context.Context) *engine.Promise { - return engine.Unify( - vm, - prolog.Tuple(engine.NewAtom(address), CoinsToTerm(coins)), - prolog.Tuple(account, balances), - cont, - env, - ) - }) - } - return engine.Delay(promises...) + }, env) }) } diff --git a/x/logic/predicate/util.go b/x/logic/predicate/util.go index 58ceb04a..88e4b93b 100644 --- a/x/logic/predicate/util.go +++ b/x/logic/predicate/util.go @@ -1,11 +1,16 @@ package predicate import ( + "context" "sort" "github.com/ichiban/prolog/engine" + "github.com/samber/lo" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/axone-protocol/axoned/v10/x/logic/prolog" "github.com/axone-protocol/axoned/v10/x/logic/types" @@ -18,23 +23,76 @@ func SortBalances(balances sdk.Coins) { }) } +// IterMap transforms the output of an iterator by applying a given mapping function to each element. +func IterMap[U any, V any](next func() (U, bool), f func(U) V) func() (V, bool) { + return func() (V, bool) { + u, ok := next() + if !ok { + var zeroV V + return zeroV, false + } + + return f(u), true + } +} + +// Accounts returns an iterator that iterates over all accounts. +func Accounts(ctx context.Context, authQueryService types.AuthQueryService) func() (lo.Tuple2[sdk.AccountI, error], bool) { + var ( + finished bool + key []byte + ) + + return func() (lo.Tuple2[sdk.AccountI, error], bool) { + if finished { + return lo.Tuple2[sdk.AccountI, error]{A: nil, B: nil}, false + } + res, err := authQueryService.Accounts(ctx, + &auth.QueryAccountsRequest{ + Pagination: &query.PageRequest{ + Key: key, + Limit: 1, + }, + }) + if err != nil { + finished = true + return lo.Tuple2[sdk.AccountI, error]{A: nil, B: err}, true + } + + if len(res.Accounts) == 0 { + finished = true + return lo.Tuple2[sdk.AccountI, error]{A: nil, B: nil}, false + } + + key = res.Pagination.NextKey + + interfaceRegistry := ctx.Value(types.InterfaceRegistryContextKey).(cdctypes.InterfaceRegistry) + var account sdk.AccountI + if err := interfaceRegistry.UnpackAny(res.Accounts[0], &account); err != nil { + return lo.Tuple2[sdk.AccountI, error]{A: nil, B: err}, true + } + + return lo.Tuple2[sdk.AccountI, error]{A: account, B: nil}, true + } +} + // AllBalancesSorted returns the list of balances for the given address, sorted by coin denomination. -func AllBalancesSorted(sdkContext sdk.Context, bankKeeper types.BankKeeper, bech32Addr sdk.AccAddress) sdk.Coins { - fetchedBalances := bankKeeper.GetAllBalances(sdkContext, bech32Addr) +func AllBalancesSorted(ctx context.Context, bankKeeper types.BankKeeper, bech32Addr sdk.AccAddress) sdk.Coins { + fetchedBalances := bankKeeper.GetAllBalances(ctx, bech32Addr) SortBalances(fetchedBalances) return fetchedBalances } // SpendableCoinsSorted returns the list of spendable coins for the given address, sorted by coin denomination. -func SpendableCoinsSorted(sdkContext sdk.Context, bankKeeper types.BankKeeper, bech32Addr sdk.AccAddress) sdk.Coins { - fetchedBalances := bankKeeper.SpendableCoins(sdkContext, bech32Addr) +func SpendableCoinsSorted(ctx context.Context, bankKeeper types.BankKeeper, bech32Addr sdk.AccAddress) sdk.Coins { + fetchedBalances := bankKeeper.SpendableCoins(ctx, bech32Addr) SortBalances(fetchedBalances) return fetchedBalances } // LockedCoinsSorted returns the list of spendable coins for the given address, sorted by coin denomination. -func LockedCoinsSorted(sdkContext sdk.Context, bankKeeper types.BankKeeper, bech32Addr sdk.AccAddress) sdk.Coins { - fetchedBalances := bankKeeper.LockedCoins(sdkContext, bech32Addr) +func LockedCoinsSorted(ctx context.Context, bankKeeper types.BankKeeper, bech32Addr sdk.AccAddress) sdk.Coins { + fetchedBalances := bankKeeper.LockedCoins(ctx, bech32Addr) SortBalances(fetchedBalances) return fetchedBalances } diff --git a/x/logic/testutil/expected_keepers_mocks.go b/x/logic/testutil/expected_keepers_mocks.go index 4c980f06..74767509 100644 --- a/x/logic/testutil/expected_keepers_mocks.go +++ b/x/logic/testutil/expected_keepers_mocks.go @@ -9,7 +9,7 @@ import ( reflect "reflect" types "github.com/cosmos/cosmos-sdk/types" - types0 "github.com/cosmos/cosmos-sdk/x/bank/types" + types0 "github.com/cosmos/cosmos-sdk/x/auth/types" gomock "github.com/golang/mock/gomock" ) @@ -50,6 +50,44 @@ func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr) } +// MockAuthQueryService is a mock of AuthQueryService interface. +type MockAuthQueryService struct { + ctrl *gomock.Controller + recorder *MockAuthQueryServiceMockRecorder +} + +// MockAuthQueryServiceMockRecorder is the mock recorder for MockAuthQueryService. +type MockAuthQueryServiceMockRecorder struct { + mock *MockAuthQueryService +} + +// NewMockAuthQueryService creates a new mock instance. +func NewMockAuthQueryService(ctrl *gomock.Controller) *MockAuthQueryService { + mock := &MockAuthQueryService{ctrl: ctrl} + mock.recorder = &MockAuthQueryServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAuthQueryService) EXPECT() *MockAuthQueryServiceMockRecorder { + return m.recorder +} + +// Accounts mocks base method. +func (m *MockAuthQueryService) Accounts(ctx context.Context, req *types0.QueryAccountsRequest) (*types0.QueryAccountsResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Accounts", ctx, req) + ret0, _ := ret[0].(*types0.QueryAccountsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Accounts indicates an expected call of Accounts. +func (mr *MockAuthQueryServiceMockRecorder) Accounts(ctx, req interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accounts", reflect.TypeOf((*MockAuthQueryService)(nil).Accounts), ctx, req) +} + // MockBankKeeper is a mock of BankKeeper interface. type MockBankKeeper struct { ctrl *gomock.Controller @@ -73,20 +111,6 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { return m.recorder } -// GetAccountsBalances mocks base method. -func (m *MockBankKeeper) GetAccountsBalances(ctx context.Context) []types0.Balance { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountsBalances", ctx) - ret0, _ := ret[0].([]types0.Balance) - return ret0 -} - -// GetAccountsBalances indicates an expected call of GetAccountsBalances. -func (mr *MockBankKeeperMockRecorder) GetAccountsBalances(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountsBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAccountsBalances), ctx) -} - // GetAllBalances mocks base method. func (m *MockBankKeeper) GetAllBalances(ctx context.Context, addr types.AccAddress) types.Coins { m.ctrl.T.Helper() diff --git a/x/logic/types/context.go b/x/logic/types/context.go index 780ae714..66021b96 100644 --- a/x/logic/types/context.go +++ b/x/logic/types/context.go @@ -4,8 +4,12 @@ package types type ContextKey string const ( + // InterfaceRegistryContextKey is the context key for the interface registry. + InterfaceRegistryContextKey = ContextKey("interfaceRegistry") // AuthKeeperContextKey is the context key for the auth keeper. AuthKeeperContextKey = ContextKey("authKeeper") + // AuthQueryServiceContextKey is the context key for the auth query service. + AuthQueryServiceContextKey = ContextKey("authQueryService") // BankKeeperContextKey is the context key for the bank keeper. BankKeeperContextKey = ContextKey("bankKeeper") ) diff --git a/x/logic/types/expected_keepers.go b/x/logic/types/expected_keepers.go index d775f962..29ecad20 100644 --- a/x/logic/types/expected_keepers.go +++ b/x/logic/types/expected_keepers.go @@ -4,7 +4,7 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" ) // AccountKeeper defines the expected account keeper used for simulations (noalias). @@ -12,11 +12,14 @@ type AccountKeeper interface { GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI } +type AuthQueryService interface { + Accounts(ctx context.Context, req *auth.QueryAccountsRequest) (*auth.QueryAccountsResponse, error) +} + // BankKeeper defines the expected interface needed to retrieve account balances. type BankKeeper interface { GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin GetAllBalances(ctx context.Context, addr sdk.AccAddress) sdk.Coins - GetAccountsBalances(ctx context.Context) []bank.Balance SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins LockedCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins } diff --git a/x/logic/util/prolog.go b/x/logic/util/prolog.go index bcf4ca81..18bd9486 100644 --- a/x/logic/util/prolog.go +++ b/x/logic/util/prolog.go @@ -5,6 +5,7 @@ import ( "errors" "strings" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ichiban/prolog" "github.com/ichiban/prolog/engine" "github.com/samber/lo" @@ -12,8 +13,6 @@ import ( errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/axone-protocol/axoned/v10/x/logic/types" ) @@ -52,24 +51,35 @@ func QueryInterpreter( if callErr != nil { if sdkmath.NewUint(uint64(len(results))).LT(solutionsLimit) { // error is not part of the look-ahead and should be included in the solutions - sdkCtx := sdk.UnwrapSDKContext(ctx) + if errors.Is(callErr, types.LimitExceeded) { + return nil, callErr + } var panicErr engine.PanicError - switch { - case errors.Is(callErr, types.LimitExceeded): - return nil, callErr - case errors.As(callErr, &panicErr) && errors.Is(panicErr.OriginErr, engine.ErrMaxVariables): + if errors.As(callErr, &panicErr) && errors.Is(panicErr.OriginErr, engine.ErrMaxVariables) { return nil, errorsmod.Wrapf(types.LimitExceeded, panicErr.OriginErr.Error()) - case sdkCtx.GasMeter().IsOutOfGas(): - return nil, errorsmod.Wrapf( - types.LimitExceeded, "out of gas: %s <%s> (%d/%d)", - types.ModuleName, callErr.Error(), sdkCtx.GasMeter().GasConsumed(), sdkCtx.GasMeter().Limit()) } - results = append(results, types.Result{Error: callErr.Error()}) - } else { - // error is part of the look-ahead, so let's consider that there's one more solution - count = count.Incr() + + if err = func() error { + defer func() { + _ = recover() + }() + sdkCtx := sdk.UnwrapSDKContext(ctx) + if sdkCtx.GasMeter().IsOutOfGas() { + return errorsmod.Wrapf( + types.LimitExceeded, "out of gas: %s <%s> (%d/%d)", + types.ModuleName, callErr.Error(), sdkCtx.GasMeter().GasConsumed(), sdkCtx.GasMeter().Limit()) + } + return nil + }(); err != nil { + return nil, err + } } + + results = append(results, types.Result{Error: callErr.Error()}) + } else { + // error is part of the look-ahead, so let's consider that there's one more solution + count = count.Incr() } return &types.Answer{