-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: vesting bug fixes and simple integration testing
- Loading branch information
1 parent
d00699b
commit d52dbfb
Showing
5 changed files
with
846 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
package app | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
cmtabcitypes "github.com/cometbft/cometbft/abci/types" | ||
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" | ||
dbm "github.com/cosmos/cosmos-db" | ||
|
||
"cosmossdk.io/core/appmodule" | ||
"cosmossdk.io/log" | ||
"cosmossdk.io/store" | ||
"cosmossdk.io/store/metrics" | ||
storetypes "cosmossdk.io/store/types" | ||
|
||
"github.com/cosmos/cosmos-sdk/baseapp" | ||
"github.com/cosmos/cosmos-sdk/codec" | ||
codectypes "github.com/cosmos/cosmos-sdk/codec/types" | ||
"github.com/cosmos/cosmos-sdk/runtime" | ||
"github.com/cosmos/cosmos-sdk/testutil/integration" | ||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/module" | ||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" | ||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" | ||
consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" | ||
consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" | ||
) | ||
|
||
const appName = "integration-app" | ||
|
||
// IntegationApp is a test application that can be used to test the integration of modules. | ||
type IntegationApp struct { | ||
*baseapp.BaseApp | ||
|
||
ctx sdk.Context | ||
logger log.Logger | ||
moduleManager module.Manager | ||
queryHelper *baseapp.QueryServiceTestHelper | ||
} | ||
|
||
// NewIntegrationApp creates an application for testing purposes. This application | ||
// is able to route messages to their respective handlers. | ||
func NewIntegrationApp( | ||
sdkCtx sdk.Context, | ||
logger log.Logger, | ||
keys map[string]*storetypes.KVStoreKey, | ||
appCodec codec.Codec, | ||
modules map[string]appmodule.AppModule, | ||
) *IntegationApp { | ||
db := dbm.NewMemDB() | ||
|
||
interfaceRegistry := codectypes.NewInterfaceRegistry() | ||
moduleManager := module.NewManagerFromMap(modules) | ||
basicModuleManager := module.NewBasicManagerFromManager(moduleManager, nil) | ||
basicModuleManager.RegisterInterfaces(interfaceRegistry) | ||
|
||
txConfig := authtx.NewTxConfig(codec.NewProtoCodec(interfaceRegistry), authtx.DefaultSignModes) | ||
bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseapp.SetChainID(appName)) | ||
bApp.MountKVStores(keys) | ||
|
||
bApp.SetInitChainer(func(ctx sdk.Context, _ *cmtabcitypes.RequestInitChain) (*cmtabcitypes.ResponseInitChain, error) { | ||
for _, mod := range modules { | ||
if m, ok := mod.(module.HasGenesis); ok { | ||
m.InitGenesis(ctx, appCodec, m.DefaultGenesis(appCodec)) | ||
} | ||
} | ||
|
||
return &cmtabcitypes.ResponseInitChain{}, nil | ||
}) | ||
|
||
bApp.SetBeginBlocker(func(_ sdk.Context) (sdk.BeginBlock, error) { | ||
return moduleManager.BeginBlock(sdkCtx) | ||
}) | ||
bApp.SetEndBlocker(func(_ sdk.Context) (sdk.EndBlock, error) { | ||
return moduleManager.EndBlock(sdkCtx) | ||
}) | ||
|
||
router := baseapp.NewMsgServiceRouter() | ||
router.SetInterfaceRegistry(interfaceRegistry) | ||
bApp.SetMsgServiceRouter(router) | ||
|
||
if keys[consensusparamtypes.StoreKey] != nil { | ||
// set baseApp param store | ||
consensusParamsKeeper := consensusparamkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), authtypes.NewModuleAddress("gov").String(), runtime.EventService{}) | ||
bApp.SetParamStore(consensusParamsKeeper.ParamsStore) | ||
|
||
if err := bApp.LoadLatestVersion(); err != nil { | ||
panic(fmt.Errorf("failed to load application version from store: %w", err)) | ||
} | ||
|
||
if _, err := bApp.InitChain(&cmtabcitypes.RequestInitChain{ChainId: appName, ConsensusParams: simtestutil.DefaultConsensusParams}); err != nil { | ||
panic(fmt.Errorf("failed to initialize application: %w", err)) | ||
} | ||
} else { | ||
if err := bApp.LoadLatestVersion(); err != nil { | ||
panic(fmt.Errorf("failed to load application version from store: %w", err)) | ||
} | ||
|
||
// app.NewDefaultGenesisState(bApp.AppCodec()) | ||
// req := &abci.RequestInitChain{ | ||
// AppStateBytes: appState, | ||
// ChainId: appName, | ||
// ConsensusParams: consensusParams, | ||
// Time: genesisTimestamp, | ||
// } | ||
|
||
if _, err := bApp.InitChain(&cmtabcitypes.RequestInitChain{ChainId: appName}); err != nil { | ||
// if _, err := bApp.InitChain(req); err != nil { | ||
panic(fmt.Errorf("failed to initialize application: %w", err)) | ||
} | ||
} | ||
|
||
bApp.Commit() | ||
|
||
ctx := sdkCtx.WithBlockHeader(cmtproto.Header{ChainID: appName, Time: time.Now()}).WithIsCheckTx(true) | ||
|
||
return &IntegationApp{ | ||
BaseApp: bApp, | ||
logger: logger, | ||
ctx: ctx, | ||
moduleManager: *moduleManager, | ||
queryHelper: baseapp.NewQueryServerTestHelper(ctx, interfaceRegistry), | ||
} | ||
} | ||
|
||
// RunMsg provides the ability to run a message and return the response. | ||
// In order to run a message, the application must have a handler for it. | ||
// These handlers are registered on the application message service router. | ||
// The result of the message execution is returned as an Any type. | ||
// That any type can be unmarshaled to the expected response type. | ||
// If the message execution fails, an error is returned. | ||
func (app *IntegationApp) RunMsg(msg sdk.Msg, option ...integration.Option) (*codectypes.Any, error) { | ||
// set options | ||
cfg := &integration.Config{} | ||
for _, opt := range option { | ||
opt(cfg) | ||
} | ||
|
||
if cfg.AutomaticCommit { | ||
defer app.Commit() | ||
} | ||
|
||
if cfg.AutomaticFinalizeBlock { | ||
height := app.LastBlockHeight() + 1 | ||
if _, err := app.FinalizeBlock(&cmtabcitypes.RequestFinalizeBlock{Height: height}); err != nil { | ||
return nil, fmt.Errorf("failed to run finalize block: %w", err) | ||
} | ||
} | ||
|
||
app.logger.Info("Running msg", "msg", msg.String()) | ||
|
||
handler := app.MsgServiceRouter().Handler(msg) | ||
if handler == nil { | ||
return nil, fmt.Errorf("handler is nil, can't route message %s: %+v", sdk.MsgTypeURL(msg), msg) | ||
} | ||
|
||
msgResult, err := handler(app.ctx, msg) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to execute message %s: %w", sdk.MsgTypeURL(msg), err) | ||
} | ||
|
||
var response *codectypes.Any | ||
if len(msgResult.MsgResponses) > 0 { | ||
msgResponse := msgResult.MsgResponses[0] | ||
if msgResponse == nil { | ||
return nil, fmt.Errorf("got nil msg response %s in message result: %s", sdk.MsgTypeURL(msg), msgResult.String()) | ||
} | ||
|
||
response = msgResponse | ||
} | ||
|
||
return response, nil | ||
} | ||
|
||
// Context returns the application context. It can be unwrapped to a sdk.Context, | ||
// with the sdk.UnwrapSDKContext function. | ||
func (app *IntegationApp) Context() sdk.Context { | ||
return app.ctx | ||
} | ||
|
||
// AddTime adds time to the application context. | ||
func (app *IntegationApp) AddTime(seconds int64) { | ||
newTime := app.ctx.BlockHeader().Time.Add(time.Duration(int64(time.Second) * seconds)) | ||
app.ctx = app.ctx.WithBlockTime(newTime) | ||
} | ||
|
||
// QueryHelper returns the application query helper. | ||
// It can be used when registering query services. | ||
func (app *IntegationApp) QueryHelper() *baseapp.QueryServiceTestHelper { | ||
return app.queryHelper | ||
} | ||
|
||
// CreateMultiStore is a helper for setting up multiple stores for provided modules. | ||
func CreateMultiStore(keys map[string]*storetypes.KVStoreKey, logger log.Logger) storetypes.CommitMultiStore { | ||
db := dbm.NewMemDB() | ||
cms := store.NewCommitMultiStore(db, logger, metrics.NewNoOpMetrics()) | ||
|
||
for key := range keys { | ||
cms.MountStoreWithDB(keys[key], storetypes.StoreTypeIAVL, db) | ||
} | ||
|
||
_ = cms.LoadLatestVersion() | ||
return cms | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
syntax = "proto3"; | ||
package sedachain.vesting.v1; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "cosmos/base/v1beta1/coin.proto"; | ||
import "cosmos_proto/cosmos.proto"; | ||
import "cosmos/vesting/v1beta1/vesting.proto"; | ||
import "cosmos/msg/v1/msg.proto"; | ||
import "amino/amino.proto"; | ||
|
||
option go_package = "github.com/sedaprotocol/seda-chain/x/vesting/types"; | ||
|
||
// Msg defines the bank Msg service. | ||
service Msg { | ||
option (cosmos.msg.v1.service) = true; | ||
|
||
// CreateVestingAccount creates a new vesting account. | ||
rpc CreateVestingAccount(MsgCreateVestingAccount) returns (MsgCreateVestingAccountResponse); | ||
|
||
// Clawback returns the vesting funds back to the funder. | ||
rpc Clawback(MsgClawback) returns (MsgClawbackResponse); | ||
} | ||
|
||
// MsgCreateVestingAccount defines a message that creates a vesting account. | ||
message MsgCreateVestingAccount { | ||
option (cosmos.msg.v1.signer) = "from_address"; | ||
option (amino.name) = "cosmos-sdk/MsgCreateVestingAccount"; | ||
|
||
option (gogoproto.equal) = true; | ||
|
||
string from_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; | ||
string to_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; | ||
repeated cosmos.base.v1beta1.Coin amount = 3 [ | ||
(gogoproto.nullable) = false, | ||
(amino.dont_omitempty) = true, | ||
(amino.encoding) = "legacy_coins", | ||
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" | ||
]; | ||
|
||
// end of vesting as unix time (in seconds). | ||
int64 end_time = 4; | ||
} | ||
|
||
// MsgCreateVestingAccountResponse defines the CreateVestingAccount response type. | ||
message MsgCreateVestingAccountResponse {} | ||
|
||
// MsgClawback defines a message that returns the vesting funds to the funder. | ||
message MsgClawback { | ||
option (cosmos.msg.v1.signer) = "funder_address"; | ||
|
||
// funder_address is the address which funded the account. | ||
string funder_address = 1; | ||
// account_address is the address of the vesting to claw back from. | ||
string account_address = 2; | ||
} | ||
|
||
// MsgClawbackResponse defines the MsgClawback response type. | ||
message MsgClawbackResponse { | ||
// coins is the funds that have been clawed back to the funder. | ||
repeated cosmos.base.v1beta1.Coin coins = 1 | ||
[(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
syntax = "proto3"; | ||
package sedachain.vesting.v1; | ||
|
||
import "cosmos/vesting/v1beta1/vesting.proto"; | ||
import "gogoproto/gogo.proto"; | ||
import "google/protobuf/timestamp.proto"; | ||
|
||
option go_package = "github.com/sedaprotocol/seda-chain/x/vesting/types"; | ||
|
||
// ClawbackContinuousVestingAccount implements the VestingAccount interface. | ||
// It wraps a ContinuousVestingAccount provided by Cosmos SDK to provide | ||
// additional support for clawback. | ||
message ClawbackContinuousVestingAccount { | ||
option (gogoproto.goproto_getters) = false; | ||
option (gogoproto.goproto_stringer) = false; | ||
|
||
cosmos.vesting.v1beta1.ContinuousVestingAccount vesting_account = 1 [(gogoproto.embed) = true]; | ||
string funder_address = 2; | ||
} |
Oops, something went wrong.