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): Add codec.BinaryCodec and Gas to dependencies. #19068

Merged
merged 7 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,141 changes: 1,102 additions & 39 deletions api/cosmos/accounts/testing/counter/v1/counter.pulsar.go

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion codec/collections.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ type protoMessage[T any] interface {
}

// CollValue inits a collections.ValueCodec for a generic gogo protobuf message.
func CollValue[T any, PT protoMessage[T]](cdc BinaryCodec) collcodec.ValueCodec[T] {
func CollValue[T any, PT protoMessage[T]](cdc interface {
Marshal(proto.Message) ([]byte, error)
Unmarshal([]byte, proto.Message) error
},
) collcodec.ValueCodec[T] {
return &collValue[T, PT]{cdc.(Codec), proto.MessageName(PT(new(T)))}
}

Expand Down
15 changes: 15 additions & 0 deletions proto/cosmos/accounts/testing/counter/v1/counter.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ message MsgIncreaseCounterResponse {
uint64 new_amount = 1;
}

// MsgTestDependencies is used to test the dependencies.
message MsgTestDependencies {}

// MsgTestDependenciesResponse is used to test the dependencies.
message MsgTestDependenciesResponse {
// chain_id is used to test that the header service correctly works.
string chain_id = 1;
// address is used to test address codec.
string address = 2;
// before_gas is used to test the gas meter reporting.
uint64 before_gas = 3;
// after_gas is used to test gas meter increasing.
uint64 after_gas = 4;
}

// QueryCounterRequest is used to query the counter value.
message QueryCounterRequest {}

Expand Down
30 changes: 30 additions & 0 deletions runtime/gas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package runtime

import (
"context"

"cosmossdk.io/core/gas"

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

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

type GasService struct{}

func (g GasService) GetGasMeter(ctx context.Context) gas.Meter {
sdkCtx := sdk.UnwrapSDKContext(ctx)
return sdkCtx.GasMeter()
}

func (g GasService) GetBlockGasMeter(ctx context.Context) gas.Meter {
return sdk.UnwrapSDKContext(ctx).BlockGasMeter()
}

func (g GasService) WithGasMeter(ctx context.Context, meter gas.Meter) context.Context {
return sdk.UnwrapSDKContext(ctx).WithGasMeter(meter)
}

func (g GasService) WithBlockGasMeter(ctx context.Context, meter gas.Meter) context.Context {
return sdk.UnwrapSDKContext(ctx).WithBlockGasMeter(meter)
}
11 changes: 7 additions & 4 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,16 +281,18 @@ func NewSimApp(
app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), authtypes.NewModuleAddress(govtypes.ModuleName).String(), runtime.EventService{})
bApp.SetParamStore(app.ConsensusParamsKeeper.ParamsStore)

// add keepers
addressCodec := authcodec.NewBech32Codec(sdk.Bech32MainPrefix)

app.AuthKeeper = authkeeper.NewAccountKeeper(appCodec, runtime.NewKVStoreService(keys[authtypes.StoreKey]), authtypes.ProtoBaseAccount, maccPerms, authcodec.NewBech32Codec(sdk.Bech32MainPrefix), sdk.Bech32MainPrefix, authtypes.NewModuleAddress(govtypes.ModuleName).String())
// add keepers

accountsKeeper, err := accounts.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[accounts.StoreKey]),
runtime.EventService{},
runtime.HeaderService{},
runtime.BranchService{},
app.AuthKeeper.AddressCodec(),
runtime.GasService{},
addressCodec,
appCodec,
app.MsgServiceRouter(),
app.GRPCQueryRouter(),
Expand All @@ -302,9 +304,10 @@ func NewSimApp(
if err != nil {
panic(err)
}

app.AccountsKeeper = accountsKeeper

app.AuthKeeper = authkeeper.NewAccountKeeper(appCodec, runtime.NewKVStoreService(keys[authtypes.StoreKey]), authtypes.ProtoBaseAccount, maccPerms, addressCodec, sdk.Bech32MainPrefix, authtypes.NewModuleAddress(govtypes.ModuleName).String())

app.BankKeeper = bankkeeper.NewBaseKeeper(
appCodec,
runtime.NewKVStoreService(keys[banktypes.StoreKey]),
Expand Down
46 changes: 46 additions & 0 deletions tests/e2e/accounts/wiring_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//go:build app_v1

package accounts

import (
"testing"

"cosmossdk.io/core/header"
counterv1 "cosmossdk.io/x/accounts/testing/counter/v1"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)

// TestDependencies aims to test wiring between different account components,
// inherited from the runtime, specifically:
// - address codec
// - binary codec
// - header service
// - gas service
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"})

_, counterAddr, err := ak.Init(ctx, "counter", accCreator, &counterv1.MsgInit{
InitialValue: 0,
})
require.NoError(t, err)
// test dependencies
r, err := ak.Execute(ctx, counterAddr, []byte("test"), &counterv1.MsgTestDependencies{})
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)

// test header service
require.Equal(t, ctx.HeaderInfo().ChainID, res.ChainId)

// test address codec
wantAddr, err := app.AuthKeeper.AddressCodec().BytesToString(counterAddr)
require.NoError(t, err)
require.Equal(t, wantAddr, res.Address)
}
46 changes: 38 additions & 8 deletions x/accounts/internal/implementation/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/binary"

"cosmossdk.io/collections"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/x/accounts/internal/prefixstore"
Expand All @@ -24,7 +25,7 @@ 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.
originalContext context.Context // originalContext that was used to build the account context.
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.
Expand All @@ -51,7 +52,7 @@ func MakeAccountContext(
store: makeAccountStore(ctx, storeSvc, accNumber),
sender: sender,
whoami: accountAddr,
originalContext: ctx,
parentContext: ctx,
moduleExec: moduleExec,
moduleExecUntyped: moduleExecUntyped,
moduleQuery: moduleQuery,
Expand All @@ -72,7 +73,7 @@ func ExecModuleUntyped(ctx context.Context, msg ProtoMsg) (ProtoMsg, error) {
// get sender
v := ctx.Value(contextKey{}).(contextValue)

resp, err := v.moduleExecUntyped(v.originalContext, v.whoami, msg)
resp, err := v.moduleExecUntyped(v.parentContext, v.whoami, msg)
if err != nil {
return nil, err
}
Expand All @@ -87,7 +88,7 @@ func ExecModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsgG

// execute module, unwrapping the original context.
resp := RespProto(new(Resp))
err := v.moduleExec(v.originalContext, v.whoami, msg, resp)
err := v.moduleExec(v.parentContext, v.whoami, msg, resp)
if err != nil {
return nil, err
}
Expand All @@ -101,7 +102,7 @@ func QueryModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsg
// we also unwrap the original context.
v := ctx.Value(contextKey{}).(contextValue)
resp := RespProto(new(Resp))
err := v.moduleQuery(v.originalContext, req, resp)
err := v.moduleQuery(v.parentContext, req, resp)
if err != nil {
return nil, err
}
Expand All @@ -123,9 +124,38 @@ func Whoami(ctx context.Context) []byte {
return ctx.Value(contextKey{}).(contextValue).whoami
}

type headerService struct{ header.Service }
type headerService struct{ hs header.Service }

func (h headerService) GetHeaderInfo(ctx context.Context) header.Info {
originalContext := ctx.Value(contextKey{}).(contextValue).originalContext
return h.Service.GetHeaderInfo(originalContext)
return h.hs.GetHeaderInfo(getParentContext(ctx))
}

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

type gasService struct {
gs gas.Service
}

func (g gasService) GetGasMeter(ctx context.Context) gas.Meter {
return g.gs.GetGasMeter(getParentContext(ctx))
}

func (g gasService) GetBlockGasMeter(ctx context.Context) gas.Meter {
return g.gs.GetBlockGasMeter(getParentContext(ctx))
}

func (g gasService) WithGasMeter(ctx context.Context, meter gas.Meter) context.Context {
v := ctx.Value(contextKey{}).(contextValue)
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.parentContext = g.gs.WithBlockGasMeter(v.parentContext, meter)
return context.WithValue(v.parentContext, contextKey{}, v)
}

func getParentContext(ctx context.Context) context.Context {
return ctx.Value(contextKey{}).(contextValue).parentContext
}
9 changes: 9 additions & 0 deletions x/accounts/internal/implementation/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ import (
"strings"

"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/runtime/protoiface"

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

type ProtoMsg = protoiface.MessageV1

// ProtoMsgG is a generic interface for protobuf messages.
type ProtoMsgG[T any] interface {
*T
protoiface.MessageV1
}

type Any = codectypes.Any

func FindMessageByName(name string) (ProtoMsg, error) {
Expand Down
26 changes: 20 additions & 6 deletions x/accounts/internal/implementation/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@ import (
"context"
"fmt"

gogoproto "github.com/cosmos/gogoproto/proto"

"cosmossdk.io/collections"
"cosmossdk.io/core/address"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"

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

// Dependencies are passed to the constructor of a smart account.
type Dependencies struct {
SchemaBuilder *collections.SchemaBuilder
AddressCodec address.Codec
HeaderService header.Service
SchemaBuilder *collections.SchemaBuilder
AddressCodec address.Codec
HeaderService header.Service
GasService gas.Service
LegacyStateCodec interface {
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
Marshal(gogoproto.Message) ([]byte, error)
Unmarshal([]byte, gogoproto.Message) error
}
}

// AccountCreatorFunc is a function that creates an account.
Expand All @@ -22,17 +32,21 @@ type AccountCreatorFunc = func(deps Dependencies) (string, Account, error)
// MakeAccountsMap creates a map of account names to account implementations
// from a list of account creator functions.
func MakeAccountsMap(
cdc codec.BinaryCodec,
addressCodec address.Codec,
hs header.Service,
gs gas.Service,
accounts []AccountCreatorFunc,
) (map[string]Implementation, error) {
accountsMap := make(map[string]Implementation, len(accounts))
for _, makeAccount := range accounts {
stateSchemaBuilder := collections.NewSchemaBuilderFromAccessor(openKVStore)
deps := Dependencies{
SchemaBuilder: stateSchemaBuilder,
AddressCodec: addressCodec,
HeaderService: headerService{hs},
SchemaBuilder: stateSchemaBuilder,
AddressCodec: addressCodec,
HeaderService: headerService{hs},
GasService: gasService{gs},
LegacyStateCodec: cdc,
}
name, accountInterface, err := makeAccount(deps)
if err != nil {
Expand Down
9 changes: 0 additions & 9 deletions x/accounts/internal/implementation/protoaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,8 @@ import (
"fmt"

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

type ProtoMsg = protoiface.MessageV1

// ProtoMsgG is a generic interface for protobuf messages.
type ProtoMsgG[T any] interface {
*T
protoiface.MessageV1
}

// RegisterInitHandler registers an initialisation handler for a smart account that uses protobuf.
func RegisterInitHandler[
Req any, ProtoReq ProtoMsgG[Req], Resp any, ProtoResp ProtoMsgG[Resp],
testinginprod marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
7 changes: 6 additions & 1 deletion x/accounts/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import (
"cosmossdk.io/core/address"
"cosmossdk.io/core/branch"
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/header"
"cosmossdk.io/core/store"
"cosmossdk.io/x/accounts/accountstd"
"cosmossdk.io/x/accounts/internal/implementation"

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

var (
Expand Down Expand Up @@ -61,10 +64,12 @@ type InterfaceRegistry interface {
}

func NewKeeper(
cdc codec.BinaryCodec,
ss store.KVStoreService,
es event.Service,
hs header.Service,
bs branch.Service,
gs gas.Service,
addressCodec address.Codec,
signerProvider SignerProvider,
execRouter MsgRouter,
Expand Down Expand Up @@ -92,7 +97,7 @@ func NewKeeper(
return Keeper{}, err
}
keeper.Schema = schema
keeper.accounts, err = implementation.MakeAccountsMap(keeper.addressCodec, hs, accounts)
keeper.accounts, err = implementation.MakeAccountsMap(cdc, keeper.addressCodec, hs, gs, accounts)
if err != nil {
return Keeper{}, err
}
Expand Down
Loading
Loading