Skip to content

Commit

Permalink
feat: basic keeper implemenation (#9)
Browse files Browse the repository at this point in the history
* basic keeper funcs

* test4

* fix test

* test

* keeper

* beautify

* beautify

* keystate

* simplify

* docs

---------

Co-authored-by: aljo242 <[email protected]>
  • Loading branch information
Alex Johnson and aljo242 authored Nov 10, 2023
1 parent 10f90ed commit d8df2f7
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 2 deletions.
131 changes: 131 additions & 0 deletions testutils/testutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package testutils

import (
"math/rand"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types"
)

type EncodingConfig struct {
InterfaceRegistry types.InterfaceRegistry
Codec codec.Codec
TxConfig client.TxConfig
Amino *codec.LegacyAmino
}

func CreateTestEncodingConfig() EncodingConfig {
cdc := codec.NewLegacyAmino()
interfaceRegistry := types.NewInterfaceRegistry()

banktypes.RegisterInterfaces(interfaceRegistry)
cryptocodec.RegisterInterfaces(interfaceRegistry)
feemarkettypes.RegisterInterfaces(interfaceRegistry)
stakingtypes.RegisterInterfaces(interfaceRegistry)

codec := codec.NewProtoCodec(interfaceRegistry)

return EncodingConfig{
InterfaceRegistry: interfaceRegistry,
Codec: codec,
TxConfig: tx.NewTxConfig(codec, tx.DefaultSignModes),
Amino: cdc,
}
}

type Account struct {
PrivKey cryptotypes.PrivKey
PubKey cryptotypes.PubKey
Address sdk.AccAddress
ConsKey cryptotypes.PrivKey
}

func (acc Account) Equals(acc2 Account) bool {
return acc.Address.Equals(acc2.Address)
}

func RandomAccounts(r *rand.Rand, n int) []Account {
accs := make([]Account, n)

for i := 0; i < n; i++ {
pkSeed := make([]byte, 15)
r.Read(pkSeed)

accs[i].PrivKey = secp256k1.GenPrivKeyFromSecret(pkSeed)
accs[i].PubKey = accs[i].PrivKey.PubKey()
accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address())

accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(pkSeed)
}

return accs
}

func CreateRandomTx(txCfg client.TxConfig, account Account, nonce, numberMsgs, timeout uint64, gasLimit uint64, fees ...sdk.Coin) (authsigning.Tx, error) {
msgs := make([]sdk.Msg, numberMsgs)
for i := 0; i < int(numberMsgs); i++ {
msgs[i] = &banktypes.MsgSend{
FromAddress: account.Address.String(),
ToAddress: account.Address.String(),
}
}

txBuilder := txCfg.NewTxBuilder()
if err := txBuilder.SetMsgs(msgs...); err != nil {
return nil, err
}

sigV2 := signing.SignatureV2{
PubKey: account.PrivKey.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: nil,
},
Sequence: nonce,
}
if err := txBuilder.SetSignatures(sigV2); err != nil {
return nil, err
}

txBuilder.SetTimeoutHeight(timeout)

txBuilder.SetFeeAmount(fees)

txBuilder.SetGasLimit(gasLimit)

return txBuilder.GetTx(), nil
}

func CreateRandomTxBz(txCfg client.TxConfig, account Account, nonce, numberMsgs, timeout, gasLimit uint64) ([]byte, error) {
tx, err := CreateRandomTx(txCfg, account, nonce, numberMsgs, timeout, gasLimit)
if err != nil {
return nil, err
}

return txCfg.TxEncoder()(tx)
}

func CreateRandomMsgs(acc sdk.AccAddress, numberMsgs int) []sdk.Msg {
msgs := make([]sdk.Msg, numberMsgs)
for i := 0; i < numberMsgs; i++ {
msgs[i] = &banktypes.MsgSend{
FromAddress: acc.String(),
ToAddress: acc.String(),
}
}

return msgs
}
134 changes: 134 additions & 0 deletions x/feemarket/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package keeper

import (
"fmt"

"github.com/cometbft/cometbft/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/skip-mev/feemarket/x/feemarket/interfaces"
"github.com/skip-mev/feemarket/x/feemarket/types"
)

type Keeper struct {
cdc codec.BinaryCodec
storeKey storetypes.StoreKey

// plugin is the fee market implementation to be used.
plugin interfaces.FeeMarketImplementation

// The address that is capable of executing a MsgParams message.
// Typically, this will be the governance module's address.
authority string
}

// NewKeeper constructs a new feemarket keeper.
func NewKeeper(
cdc codec.BinaryCodec,
storeKey storetypes.StoreKey,
plugin interfaces.FeeMarketImplementation,
authority string,
) *Keeper {
k := &Keeper{
cdc,
storeKey,
plugin,
authority,
}

return k
}

// Logger returns a feemarket module-specific logger.
func (k *Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
}

// GetAuthority returns the address that is capable of executing a MsgUpdateParams message.
func (k *Keeper) GetAuthority() string {
return k.authority
}

// Plugin returns the plugged fee market implementation of the keeper.
func (k *Keeper) Plugin() interfaces.FeeMarketImplementation {
return k.plugin
}

// SetFeeMarket sets the fee market implementation data in the keeper.
func (k *Keeper) SetFeeMarket(ctx sdk.Context, fm interfaces.FeeMarketImplementation) error {
bz, err := fm.Marshal()
if err != nil {
return fmt.Errorf("unable to marshal fee market implemenation: %w", err)
}

k.setData(ctx, bz)
k.plugin = fm

return nil
}

// GetFeeMarket gets the fee market implementation data in the keeper. Will
func (k *Keeper) GetFeeMarket(ctx sdk.Context) (interfaces.FeeMarketImplementation, error) {
bz, err := k.getData(ctx)
if err != nil {
return nil, err
}

err = k.plugin.Unmarshal(bz)
return k.plugin, err
}

// setData sets arbitrary byte data in the keeper.
func (k *Keeper) setData(ctx sdk.Context, data []byte) {
// TODO: limit max data size?

store := ctx.KVStore(k.storeKey)
store.Set(types.KeyState, data)
}

// getData gets arbitrary byte data in the keeper.
func (k *Keeper) getData(ctx sdk.Context) ([]byte, error) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyState)

if len(bz) == 0 {
return nil, fmt.Errorf("no data set in the keeper")
}

return bz, nil
}

// GetParams returns the feemarket module's parameters.
func (k *Keeper) GetParams(ctx sdk.Context) (types.Params, error) {
store := ctx.KVStore(k.storeKey)

key := types.KeyParams
bz := store.Get(key)

if len(bz) == 0 {
return types.Params{}, fmt.Errorf("no params found for the feemarket module")
}

params := types.Params{}
if err := params.Unmarshal(bz); err != nil {
return types.Params{}, err
}

return params, nil
}

// SetParams sets the feemarket module's parameters.
func (k *Keeper) SetParams(ctx sdk.Context, params types.Params) error {
store := ctx.KVStore(k.storeKey)

bz, err := params.Marshal()
if err != nil {
return err
}

store.Set(types.KeyParams, bz)

return nil
}
66 changes: 66 additions & 0 deletions x/feemarket/keeper/keeper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package keeper_test

import (
"testing"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/stretchr/testify/suite"

"github.com/skip-mev/feemarket/testutils"
"github.com/skip-mev/feemarket/x/feemarket/keeper"
"github.com/skip-mev/feemarket/x/feemarket/plugins/defaultmarket"
"github.com/skip-mev/feemarket/x/feemarket/types"
)

type KeeperTestSuite struct {
suite.Suite

feemarketKeeper *keeper.Keeper
encCfg testutils.EncodingConfig
ctx sdk.Context
key *storetypes.KVStoreKey
authorityAccount sdk.AccAddress
}

func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}

func (s *KeeperTestSuite) SetupTest() {
s.encCfg = testutils.CreateTestEncodingConfig()
s.key = storetypes.NewKVStoreKey(types.StoreKey)
testCtx := testutil.DefaultContextWithDB(s.T(), s.key, storetypes.NewTransientStoreKey("transient_test"))
s.ctx = testCtx.Ctx

plugin := defaultmarket.NewDefaultFeeMarket()

s.authorityAccount = []byte("authority")
s.feemarketKeeper = keeper.NewKeeper(
s.encCfg.Codec,
s.key,
plugin,
s.authorityAccount.String(),
)

err := s.feemarketKeeper.SetParams(s.ctx, types.DefaultParams())
s.Require().NoError(err)
}

func (s *KeeperTestSuite) TestSetFeeMarket() {
s.Run("get with no data returns error", func() {
_, err := s.feemarketKeeper.GetFeeMarket(s.ctx)
s.Require().Error(err)
})

s.Run("set and get valid data", func() {
plugin := defaultmarket.NewDefaultFeeMarket()
s.feemarketKeeper.SetFeeMarket(s.ctx, plugin)

gotPlugin, err := s.feemarketKeeper.GetFeeMarket(s.ctx)
s.Require().NoError(err)
s.Require().Equal(plugin, gotPlugin)
})
}
17 changes: 15 additions & 2 deletions x/feemarket/types/keys.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
package types

const (
// ModuleName is the name of the module.
// ModuleName is the name of the feemarket module.
ModuleName = "feemarket"
// StoreKey is the store key string for the sla module.
// StoreKey is the store key string for the feemarket module.
StoreKey = ModuleName
)

const (
prefixParams = iota + 1
prefixState
)

var (
// KeyParams is the store key for the feemarket module's parameters.
KeyParams = []byte{prefixParams}

// KeyState is the store key for the feemarket module's data.
KeyState = []byte{prefixState}
)

0 comments on commit d8df2f7

Please sign in to comment.