Skip to content

Commit

Permalink
feat(accounts): Add codec.BinaryCodec and Gas to dependencies. (#19068)
Browse files Browse the repository at this point in the history
Co-authored-by: unknown unknown <unknown@unknown>
  • Loading branch information
testinginprod and unknown unknown authored Jan 17, 2024
1 parent 822d90c commit 498cd6a
Show file tree
Hide file tree
Showing 14 changed files with 1,773 additions and 93 deletions.
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 {
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],
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

0 comments on commit 498cd6a

Please sign in to comment.