Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(accounts): Allow funds to be sent to accounts on init and execute #19360

Merged
merged 15 commits into from
Feb 8, 2024
261 changes: 211 additions & 50 deletions api/cosmos/accounts/testing/counter/v1/counter.pulsar.go

Large diffs are not rendered by default.

494 changes: 405 additions & 89 deletions api/cosmos/accounts/v1/tx.pulsar.go

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions core/appmodule/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"google.golang.org/grpc"
"google.golang.org/protobuf/runtime/protoiface"
)

// AppModule is a tag interface for app module implementations to use as a basis
Expand Down Expand Up @@ -86,3 +87,16 @@ type HasEndBlocker interface {
// a block.
EndBlock(context.Context) error
}

// MsgHandlerRouter is implemented by the runtime provider.
type MsgHandlerRouter interface {
// RegisterHandler is called by modules to register msg handler functions.
RegisterHandler(name string, handler func(ctx context.Context, msg protoiface.MessageV1) (msgResp protoiface.MessageV1, err error))
}

// HasMsgHandler is implemented by modules that instead of exposing msg server expose
// a set of handlers.
type HasMsgHandler interface {
// RegisterMsgHandlers is implemented by the module that will register msg handlers.
RegisterMsgHandlers(router MsgHandlerRouter)
}
Comment on lines +91 to +102
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of MsgHandlerRouter and HasMsgHandler interfaces introduces a new pattern for registering message handlers. Ensure the documentation for these interfaces is clear and provides enough context for developers to understand their usage.

// MsgHandlerRouter is implemented by the runtime provider.
type MsgHandlerRouter interface {
	// RegisterHandler is called by modules to register msg handler functions.
	// The name parameter is the message type, and the handler function takes a context and a message,
	// returning a message response and an error.
	RegisterHandler(name string, handler func(ctx context.Context, msg protoiface.MessageV1) (msgResp protoiface.MessageV1, err error))
}

// HasMsgHandler is implemented by modules that, instead of exposing msg server, expose
// a set of handlers directly.
type HasMsgHandler interface {
	// RegisterMsgHandlers is called during the module initialization phase to register the module's msg handlers.
	// The router parameter is used to register each handler with its corresponding message type.
	RegisterMsgHandlers(router MsgHandlerRouter)
}

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// MsgHandlerRouter is implemented by the runtime provider.
type MsgHandlerRouter interface {
// RegisterHandler is called by modules to register msg handler functions.
RegisterHandler(name string, handler func(ctx context.Context, msg protoiface.MessageV1) (msgResp protoiface.MessageV1, err error))
}
// HasMsgHandler is implemented by modules that instead of exposing msg server expose
// a set of handlers.
type HasMsgHandler interface {
// RegisterMsgHandlers is implemented by the module that will register msg handlers.
RegisterMsgHandlers(router MsgHandlerRouter)
}
// MsgHandlerRouter is implemented by the runtime provider.
type MsgHandlerRouter interface {
// RegisterHandler is called by modules to register msg handler functions.
// The name parameter is the message type, and the handler function takes a context and a message,
// returning a message response and an error.
RegisterHandler(name string, handler func(ctx context.Context, msg protoiface.MessageV1) (msgResp protoiface.MessageV1, err error))
}
// HasMsgHandler is implemented by modules that, instead of exposing msg server, expose
// a set of handlers directly.
type HasMsgHandler interface {
// RegisterMsgHandlers is called during the module initialization phase to register the module's msg handlers.
// The router parameter is used to register each handler with its corresponding message type.
RegisterMsgHandlers(router MsgHandlerRouter)
}

6 changes: 6 additions & 0 deletions proto/cosmos/accounts/testing/counter/v1/counter.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package cosmos.accounts.testing.counter.v1;

option go_package = "cosmossdk.io/x/accounts/testing/counter/v1";

import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";

// MsgInit defines a message which initializes the counter with a given amount.
message MsgInit {
// initial_value is the initial amount to set the counter to.
Expand Down Expand Up @@ -39,6 +42,9 @@ message MsgTestDependenciesResponse {
uint64 before_gas = 3;
// after_gas is used to test gas meter increasing.
uint64 after_gas = 4;
// funds reports the funds from the implementation.Funds method.
repeated cosmos.base.v1beta1.Coin funds = 5
[(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false];
}

// QueryCounterRequest is used to query the counter value.
Expand Down
10 changes: 10 additions & 0 deletions proto/cosmos/accounts/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ option go_package = "cosmossdk.io/x/accounts/v1";
import "google/protobuf/any.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos/accounts/v1/account_abstraction.proto";
import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";

// Msg defines the Msg service for the x/accounts module.
service Msg {
Expand All @@ -33,6 +35,10 @@ message MsgInit {
string account_type = 2;
// message is the message to be sent to the account.
google.protobuf.Any message = 3;
// funds contains the coins that the account wants to
// send alongside the request.
repeated cosmos.base.v1beta1.Coin funds = 4
[(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false];
}

// MsgInitResponse defines the Create response type for the Msg/Create RPC method.
Expand All @@ -52,6 +58,10 @@ message MsgExecute {
string target = 2;
// message is the message to be sent to the account.
google.protobuf.Any message = 3;
// funds contains the coins that the account wants to
// send alongside the request.
repeated cosmos.base.v1beta1.Coin funds = 4
[(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false];
}

// MsgExecuteResponse defines the Execute response type for the Msg/Execute RPC method.
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/accounts/account_abstraction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ func TestAccountAbstraction(t *testing.T) {

_, aaAddr, err := ak.Init(ctx, "aa_minimal", accCreator, &rotationv1.MsgInit{
PubKeyBytes: privKey.PubKey().Bytes(),
})
}, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of nil as an argument in Init calls should correspond to the new funds parameter. Ensure tests are updated to cover scenarios where funds are actually transferred.

require.NoError(t, err)

_, aaFullAddr, err := ak.Init(ctx, "aa_full", accCreator, &rotationv1.MsgInit{
PubKeyBytes: privKey.PubKey().Bytes(),
})
}, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the previous comment, the nil argument addition in Init calls needs to be accompanied by tests that validate funds transfer logic.

require.NoError(t, err)

aaAddrStr, err := app.AuthKeeper.AddressCodec().BytesToString(aaAddr)
Expand Down
27 changes: 24 additions & 3 deletions tests/e2e/accounts/wiring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"testing"

"cosmossdk.io/core/header"
storetypes "cosmossdk.io/store/types"
counterv1 "cosmossdk.io/x/accounts/testing/counter/v1"
"cosmossdk.io/x/bank/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
Expand All @@ -17,24 +19,36 @@ import (
// - binary codec
// - header service
// - gas service
// - funds
func TestDependencies(t *testing.T) {
app := setupApp(t)
ak := app.AccountsKeeper
ctx := sdk.NewContext(app.CommitMultiStore(), false, app.Logger()).WithHeaderInfo(header.Info{ChainID: "chain-id"})
ctx = ctx.WithGasMeter(storetypes.NewGasMeter(500_000))

_, counterAddr, err := ak.Init(ctx, "counter", accCreator, &counterv1.MsgInit{
InitialValue: 0,
})
}, nil)
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
// test dependencies
r, err := ak.Execute(ctx, counterAddr, []byte("test"), &counterv1.MsgTestDependencies{})
creatorInitFunds := sdk.NewCoins(sdk.NewInt64Coin("stake", 100_000))
err = testutil.FundAccount(ctx, app.BankKeeper, accCreator, creatorInitFunds)
require.NoError(t, err)
sentFunds := sdk.NewCoins(sdk.NewInt64Coin("stake", 50_000))
r, err := ak.Execute(
ctx,
counterAddr,
accCreator,
&counterv1.MsgTestDependencies{},
sentFunds,
)
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
res := r.(*counterv1.MsgTestDependenciesResponse)

// test gas
require.NotZero(t, res.BeforeGas)
require.NotZero(t, res.AfterGas)
require.Equal(t, uint64(10), res.AfterGas-res.BeforeGas)
require.Equal(t, int(uint64(10)), int(res.AfterGas-res.BeforeGas))

// test header service
require.Equal(t, ctx.HeaderInfo().ChainID, res.ChainId)
Expand All @@ -43,4 +57,11 @@ func TestDependencies(t *testing.T) {
wantAddr, err := app.AuthKeeper.AddressCodec().BytesToString(counterAddr)
require.NoError(t, err)
require.Equal(t, wantAddr, res.Address)

// test funds
creatorFunds := app.BankKeeper.GetAllBalances(ctx, accCreator)
require.Equal(t, creatorInitFunds.Sub(sentFunds...), creatorFunds)

accFunds := app.BankKeeper.GetAllBalances(ctx, counterAddr)
require.Equal(t, sentFunds, accFunds)
}
5 changes: 5 additions & 0 deletions x/accounts/accountstd/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"cosmossdk.io/x/accounts/internal/implementation"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
)

Expand Down Expand Up @@ -77,6 +78,10 @@ func SenderIsAccountsModule(ctx context.Context) bool {
return bytes.Equal(Sender(ctx), accountsModuleAddress)
}

// Funds returns if any funds were sent during the execute or init request. In queries this
// returns nil.
func Funds(ctx context.Context) sdk.Coins { return implementation.Funds(ctx) }

// ExecModule can be used to execute a message towards a module.
func ExecModule[Resp any, RespProto implementation.ProtoMsgG[Resp], Req any, ReqProto implementation.ProtoMsgG[Req]](ctx context.Context, msg ReqProto) (RespProto, error) {
return implementation.ExecModule[Resp, RespProto, Req, ReqProto](ctx, msg)
Expand Down
52 changes: 52 additions & 0 deletions x/accounts/coin_transfer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package accounts

import (
"google.golang.org/protobuf/proto"

bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
v1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"cosmossdk.io/core/address"
"cosmossdk.io/x/accounts/internal/implementation"

sdk "github.com/cosmos/cosmos-sdk/types"
Comment on lines +6 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider grouping SDK-related imports together for better readability.

import (
	"google.golang.org/protobuf/proto"

	bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
	v1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
	"cosmossdk.io/core/address"
	"cosmossdk.io/x/accounts/internal/implementation"
+	
	sdk "github.com/cosmos/cosmos-sdk/types"
)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
v1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"cosmossdk.io/core/address"
"cosmossdk.io/x/accounts/internal/implementation"
sdk "github.com/cosmos/cosmos-sdk/types"
import (
"google.golang.org/protobuf/proto"
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
v1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"cosmossdk.io/core/address"
"cosmossdk.io/x/accounts/internal/implementation"
sdk "github.com/cosmos/cosmos-sdk/types"
)

)

// protoV2GogoWrapper is a wrapper of a protov2 message into a gogo message.
// this is exceptionally allowed to enable accounts to be decoupled from
// the SDK, since x/accounts can support only protov1 in its APIs.
// But in order to keep it decoupled from the SDK we need to use the API module.
// This is a hack to make an API module type work in x/accounts. Once the SDK
// has protov2 support, we can migrate internal/implementation/encoding.go to
// work with protov2.
Comment on lines +14 to +20
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment explains a workaround for protobuf version compatibility. Ensure this is tracked for future removal once the SDK supports protov2 natively.

Would you like me to open a GitHub issue to track the removal of this workaround once protov2 support is available in the SDK?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, please do!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@testinginprod: I'm creating a GitHub issue to track the removal of the protobuf version compatibility workaround once protov2 support is available in the SDK. Here's the command I used to create the issue:

gh issue create --title "Track Removal of Protobuf Version Compatibility Workaround" --body "This issue tracks the removal of the protobuf version compatibility workaround implemented in `x/accounts/coin_transfer.go`. The workaround is necessary until the Cosmos SDK natively supports protov2.

The relevant discussion can be found in PR https://github.com/cosmos/cosmos-sdk/pull/19360 and the specific comment at https://github.com/cosmos/cosmos-sdk/pull/19360#discussion_r1481734092.

The workaround involves wrapping a protov2 message into a gogo message to decouple `x/accounts` from the SDK while maintaining compatibility. This should be revisited and potentially removed once the SDK has protov2 support." --repo cosmos/cosmos-sdk

This issue will ensure that the necessary follow-up is tracked and addressed in due course. Thank you for highlighting the importance of this task! 🐇

type protoV2GogoWrapper struct {
gogoProtoPlusV2
}

func (h protoV2GogoWrapper) XXX_MessageName() string {
return string(proto.MessageName(h.gogoProtoPlusV2))
}

func defaultCoinsTransferMsgFunc(addrCdc address.Codec) coinsTransferMsgFunc {
return func(from, to []byte, coins sdk.Coins) (implementation.ProtoMsg, implementation.ProtoMsg, error) {
fromAddr, err := addrCdc.BytesToString(from)
if err != nil {
return nil, nil, err
}
toAddr, err := addrCdc.BytesToString(to)
if err != nil {
return nil, nil, err
}
v2Coins := make([]*v1beta1.Coin, len(coins))
for i, coin := range coins {
v2Coins[i] = &v1beta1.Coin{
Denom: coin.Denom,
Amount: coin.Amount.String(),
}
}
return protoV2GogoWrapper{&bankv1beta1.MsgSend{
FromAddress: fromAddr,
ToAddress: toAddr,
Amount: v2Coins,
}}, new(bankv1beta1.MsgSendResponse), nil
}
Comment on lines +29 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaultCoinsTransferMsgFunc function correctly translates SDK Coins to protobuf Coin messages and wraps the MsgSend in a protoV2GogoWrapper. However, ensure error messages are descriptive and consistent.

if err != nil {
-	return nil, nil, err
+	return nil, nil, fmt.Errorf("error converting from address bytes to string: %w", err)
}
...
if err != nil {
-	return nil, nil, err
+	return nil, nil, fmt.Errorf("error converting to address bytes to string: %w", err)
}

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func defaultCoinsTransferMsgFunc(addrCdc address.Codec) coinsTransferMsgFunc {
return func(from, to []byte, coins sdk.Coins) (implementation.ProtoMsg, implementation.ProtoMsg, error) {
fromAddr, err := addrCdc.BytesToString(from)
if err != nil {
return nil, nil, err
}
toAddr, err := addrCdc.BytesToString(to)
if err != nil {
return nil, nil, err
}
v2Coins := make([]*v1beta1.Coin, len(coins))
for i, coin := range coins {
v2Coins[i] = &v1beta1.Coin{
Denom: coin.Denom,
Amount: coin.Amount.String(),
}
}
return protoV2GogoWrapper{&bankv1beta1.MsgSend{
FromAddress: fromAddr,
ToAddress: toAddr,
Amount: v2Coins,
}}, new(bankv1beta1.MsgSendResponse), nil
}
func defaultCoinsTransferMsgFunc(addrCdc address.Codec) coinsTransferMsgFunc {
return func(from, to []byte, coins sdk.Coins) (implementation.ProtoMsg, implementation.ProtoMsg, error) {
fromAddr, err := addrCdc.BytesToString(from)
if err != nil {
return nil, nil, fmt.Errorf("error converting from address bytes to string: %w", err)
}
toAddr, err := addrCdc.BytesToString(to)
if err != nil {
return nil, nil, fmt.Errorf("error converting to address bytes to string: %w", err)
}
v2Coins := make([]*v1beta1.Coin, len(coins))
for i, coin := range coins {
v2Coins[i] = &v1beta1.Coin{
Denom: coin.Denom,
Amount: coin.Amount.String(),
}
}
return protoV2GogoWrapper{&bankv1beta1.MsgSend{
FromAddress: fromAddr,
ToAddress: toAddr,
Amount: v2Coins,
}}, new(bankv1beta1.MsgSendResponse), nil
}
}

}
8 changes: 4 additions & 4 deletions x/accounts/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ func TestGenesis(t *testing.T) {
// we init two accounts of the same type

// we set counter to 10
_, addr1, err := k.Init(ctx, "test", []byte("sender"), &types.Empty{})
_, addr1, err := k.Init(ctx, "test", []byte("sender"), &types.Empty{}, nil)
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
_, err = k.Execute(ctx, addr1, []byte("sender"), &types.UInt64Value{Value: 10})
_, err = k.Execute(ctx, addr1, []byte("sender"), &types.UInt64Value{Value: 10}, nil)
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

// we set counter to 20
_, addr2, err := k.Init(ctx, "test", []byte("sender"), &types.Empty{})
_, addr2, err := k.Init(ctx, "test", []byte("sender"), &types.Empty{}, nil)
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
_, err = k.Execute(ctx, addr2, []byte("sender"), &types.UInt64Value{Value: 20})
_, err = k.Execute(ctx, addr2, []byte("sender"), &types.UInt64Value{Value: 20}, nil)
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

// export state
Expand Down
46 changes: 28 additions & 18 deletions x/accounts/internal/implementation/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/x/accounts/internal/prefixstore"

sdk "github.com/cosmos/cosmos-sdk/types"
)

var AccountStatePrefix = collections.NewPrefix(255)
Expand All @@ -25,12 +27,21 @@ type contextValue struct {
store store.KVStore // store is the prefixed store for the account.
sender []byte // sender is the address of the entity invoking the account action.
whoami []byte // whoami is the address of the account being invoked.
funds sdk.Coins // funds reports the coins sent alongside the request.
parentContext context.Context // parentContext that was used to build the account context.
moduleExec ModuleExecFunc // moduleExec is a function that executes a module message, when the resp type is known.
moduleExecUntyped ModuleExecUntypedFunc // moduleExecUntyped is a function that executes a module message, when the resp type is unknown.
moduleQuery ModuleQueryFunc // moduleQuery is a function that queries a module.
}

func addCtx(ctx context.Context, value contextValue) context.Context {
return context.WithValue(ctx, contextKey{}, value)
}

func getCtx(ctx context.Context) contextValue {
return ctx.Value(contextKey{}).(contextValue)
}

// MakeAccountContext creates a new account execution context given:
// storeSvc: which fetches the x/accounts module store.
// accountAddr: the address of the account being invoked, which is used to give the
Expand All @@ -44,14 +55,16 @@ func MakeAccountContext(
accNumber uint64,
accountAddr []byte,
sender []byte,
funds sdk.Coins,
moduleExec ModuleExecFunc,
moduleExecUntyped ModuleExecUntypedFunc,
moduleQuery ModuleQueryFunc,
) context.Context {
return context.WithValue(ctx, contextKey{}, contextValue{
return addCtx(ctx, contextValue{
store: makeAccountStore(ctx, storeSvc, accNumber),
sender: sender,
whoami: accountAddr,
funds: funds,
parentContext: ctx,
moduleExec: moduleExec,
moduleExecUntyped: moduleExecUntyped,
Expand All @@ -71,7 +84,7 @@ func makeAccountStore(ctx context.Context, storeSvc store.KVStoreService, accNum
// ExecModuleUntyped can be used to execute a message towards a module, when the response type is unknown.
func ExecModuleUntyped(ctx context.Context, msg ProtoMsg) (ProtoMsg, error) {
// get sender
v := ctx.Value(contextKey{}).(contextValue)
v := getCtx(ctx)

resp, err := v.moduleExecUntyped(v.parentContext, v.whoami, msg)
if err != nil {
Expand All @@ -84,7 +97,7 @@ func ExecModuleUntyped(ctx context.Context, msg ProtoMsg) (ProtoMsg, error) {
// ExecModule can be used to execute a message towards a module.
func ExecModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsgG[Req]](ctx context.Context, msg ReqProto) (RespProto, error) {
// get sender
v := ctx.Value(contextKey{}).(contextValue)
v := getCtx(ctx)

// execute module, unwrapping the original context.
resp := RespProto(new(Resp))
Expand All @@ -100,7 +113,7 @@ func ExecModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsgG
func QueryModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsgG[Req]](ctx context.Context, req ReqProto) (RespProto, error) {
// we do not need to check the sender in a query because it is not a state transition.
// we also unwrap the original context.
v := ctx.Value(contextKey{}).(contextValue)
v := getCtx(ctx)
resp := RespProto(new(Resp))
err := v.moduleQuery(v.parentContext, req, resp)
if err != nil {
Expand All @@ -110,20 +123,21 @@ func QueryModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsg
}

// openKVStore returns the prefixed store for the account given the context.
func openKVStore(ctx context.Context) store.KVStore {
return ctx.Value(contextKey{}).(contextValue).store
}
func openKVStore(ctx context.Context) store.KVStore { return getCtx(ctx).store }

// Sender returns the address of the entity invoking the account action.
func Sender(ctx context.Context) []byte {
return ctx.Value(contextKey{}).(contextValue).sender
return getCtx(ctx).sender
}

// Whoami returns the address of the account being invoked.
func Whoami(ctx context.Context) []byte {
return ctx.Value(contextKey{}).(contextValue).whoami
return getCtx(ctx).whoami
}

// Funds returns the funds associated with the execution context.
func Funds(ctx context.Context) sdk.Coins { return getCtx(ctx).funds }

type headerService struct{ hs header.Service }

func (h headerService) GetHeaderInfo(ctx context.Context) header.Info {
Expand All @@ -132,9 +146,7 @@ func (h headerService) GetHeaderInfo(ctx context.Context) header.Info {

var _ gas.Service = (*gasService)(nil)

type gasService struct {
gs gas.Service
}
type gasService struct{ gs gas.Service }

func (g gasService) GetGasMeter(ctx context.Context) gas.Meter {
return g.gs.GetGasMeter(getParentContext(ctx))
Expand All @@ -145,17 +157,15 @@ func (g gasService) GetBlockGasMeter(ctx context.Context) gas.Meter {
}

func (g gasService) WithGasMeter(ctx context.Context, meter gas.Meter) context.Context {
v := ctx.Value(contextKey{}).(contextValue)
v := getCtx(ctx)
v.parentContext = g.gs.WithGasMeter(v.parentContext, meter)
return context.WithValue(v.parentContext, contextKey{}, v)
}

func (g gasService) WithBlockGasMeter(ctx context.Context, meter gas.Meter) context.Context {
v := ctx.Value(contextKey{}).(contextValue)
v := getCtx(ctx)
v.parentContext = g.gs.WithBlockGasMeter(v.parentContext, meter)
return context.WithValue(v.parentContext, contextKey{}, v)
return addCtx(v.parentContext, v)
}

func getParentContext(ctx context.Context) context.Context {
return ctx.Value(contextKey{}).(contextValue).parentContext
}
func getParentContext(ctx context.Context) context.Context { return getCtx(ctx).parentContext }
Loading
Loading