block-sdk/tests/app/app.go
Alex Johnson f1cde2acec
fix: mempool lane size check on CheckTx (#561)
* push

* init

* fix setup

* format

* fix test

* use lane

* ok

* finalize

* fix everything

* lint fix:

* Update abci/checktx/mempool_parity_check_tx.go

Co-authored-by: David Terpay <35130517+davidterpay@users.noreply.github.com>

* lint fix

* tidy

* remove

* cleanup

---------

Co-authored-by: David Terpay <david.terpay@gmail.com>
Co-authored-by: David Terpay <35130517+davidterpay@users.noreply.github.com>
2024-07-02 19:19:22 -04:00

462 lines
15 KiB
Go

package app
import (
"io"
"os"
"path/filepath"
"cosmossdk.io/log"
dbm "github.com/cosmos/cosmos-db"
"cosmossdk.io/depinject"
storetypes "cosmossdk.io/store/types"
circuitkeeper "cosmossdk.io/x/circuit/keeper"
upgradekeeper "cosmossdk.io/x/upgrade/keeper"
feegrantkeeper "cosmossdk.io/x/feegrant/keeper"
cometabci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"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/server"
"github.com/cosmos/cosmos-sdk/server/api"
"github.com/cosmos/cosmos-sdk/server/config"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper"
crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper"
mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/skip-mev/block-sdk/v2/abci"
"github.com/skip-mev/block-sdk/v2/abci/checktx"
"github.com/skip-mev/block-sdk/v2/block"
"github.com/skip-mev/block-sdk/v2/block/base"
service "github.com/skip-mev/block-sdk/v2/block/service"
"github.com/skip-mev/block-sdk/v2/block/utils"
auctionkeeper "github.com/skip-mev/block-sdk/v2/x/auction/keeper"
)
const (
ChainID = "chain-id-0"
)
var (
BondDenom = sdk.DefaultBondDenom
// DefaultNodeHome default home directories for the application daemon
DefaultNodeHome string
)
var (
_ runtime.AppI = (*TestApp)(nil)
_ servertypes.Application = (*TestApp)(nil)
)
type TestApp struct {
*runtime.App
legacyAmino *codec.LegacyAmino
appCodec codec.Codec
txConfig client.TxConfig
interfaceRegistry codectypes.InterfaceRegistry
// keepers
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.Keeper
StakingKeeper *stakingkeeper.Keeper
SlashingKeeper slashingkeeper.Keeper
MintKeeper mintkeeper.Keeper
DistrKeeper distrkeeper.Keeper
GovKeeper *govkeeper.Keeper
CrisisKeeper *crisiskeeper.Keeper
UpgradeKeeper *upgradekeeper.Keeper
ParamsKeeper paramskeeper.Keeper
AuthzKeeper authzkeeper.Keeper
GroupKeeper groupkeeper.Keeper
ConsensusParamsKeeper consensuskeeper.Keeper
CircuitBreakerKeeper circuitkeeper.Keeper
auctionkeeper auctionkeeper.Keeper
FeeGrantKeeper feegrantkeeper.Keeper
// custom checkTx handler
checkTxHandler checktx.CheckTx
}
func init() {
userHomeDir, err := os.UserHomeDir()
if err != nil {
panic(err)
}
DefaultNodeHome = filepath.Join(userHomeDir, ".testapp")
}
func New(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
loadLatest bool,
appOpts servertypes.AppOptions,
baseAppOptions ...func(*baseapp.BaseApp),
) *TestApp {
var (
app = &TestApp{}
appBuilder *runtime.AppBuilder
// merge the AppConfig and other configuration in one config
appConfig = depinject.Configs(
AppConfig,
depinject.Supply(
// supply the application options
appOpts,
logger,
// ADVANCED CONFIGURATION
//
// AUTH
//
// For providing a custom function required in auth to generate custom account types
// add it below. By default the auth module uses simulation.RandomGenesisAccounts.
//
// authtypes.RandomGenesisAccountsFn(simulation.RandomGenesisAccounts),
// For providing a custom a base account type add it below.
// By default the auth module uses authtypes.ProtoBaseAccount().
//
// func() authtypes.AccountI { return authtypes.ProtoBaseAccount() },
//
// MINT
//
// For providing a custom inflation function for x/mint add here your
// custom function that implements the minttypes.InflationCalculationFn
// interface.
),
)
)
if err := depinject.Inject(appConfig,
&appBuilder,
&app.appCodec,
&app.legacyAmino,
&app.txConfig,
&app.interfaceRegistry,
&app.AccountKeeper,
&app.BankKeeper,
&app.StakingKeeper,
&app.SlashingKeeper,
&app.MintKeeper,
&app.DistrKeeper,
&app.GovKeeper,
&app.CrisisKeeper,
&app.UpgradeKeeper,
&app.ParamsKeeper,
&app.AuthzKeeper,
&app.GroupKeeper,
&app.auctionkeeper,
&app.ConsensusParamsKeeper,
&app.FeeGrantKeeper,
&app.CircuitBreakerKeeper,
); err != nil {
panic(err)
}
// Below we could construct and set an application specific mempool and
// ABCI 1.0 PrepareProposal and ProcessProposal handlers. These defaults are
// already set in the SDK's BaseApp, this shows an example of how to override
// them.
//
// Example:
//
// app.App = appBuilder.Build(...)
// nonceMempool := mempool.NewSenderNonceMempool()
// abciPropHandler := NewDefaultProposalHandler(nonceMempool, app.App.BaseApp)
//
// app.App.BaseApp.SetMempool(nonceMempool)
// app.App.BaseApp.SetPrepareProposal(abciPropHandler.PrepareProposalHandler())
// app.App.BaseApp.SetProcessProposal(abciPropHandler.ProcessProposalHandler())
//
// Alternatively, you can construct BaseApp options, append those to
// baseAppOptions and pass them to the appBuilder.
//
// Example:
//
// prepareOpt = func(app *baseapp.BaseApp) {
// abciPropHandler := baseapp.NewDefaultProposalHandler(nonceMempool, app)
// app.SetPrepareProposal(abciPropHandler.PrepareProposalHandler())
// }
// baseAppOptions = append(baseAppOptions, prepareOpt)
app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
// ---------------------------------------------------------------------------- //
// ------------------------- Begin Custom Code -------------------------------- //
// ---------------------------------------------------------------------------- //
// STEP 1-3: Create the Block SDK lanes.
mevLane, freeLane, defaultLane := CreateLanes(app)
// STEP 4: Construct a mempool based off the lanes. Note that the order of the lanes
// matters. Blocks are constructed from the top lane to the bottom lane. The top lane
// is the first lane in the array and the bottom lane is the last lane in the array.
mempool, err := block.NewLanedMempool(
app.Logger(),
[]block.Lane{mevLane, freeLane, defaultLane},
)
if err != nil {
panic(err)
}
// The application's mempool is now powered by the Block SDK!
app.App.SetMempool(mempool)
// STEP 5: Create a global ante handler that will be called on each transaction when
// proposals are being built and verified. Note that this step must be done before
// setting the ante handler on the lanes.
handlerOptions := ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
FeegrantKeeper: app.FeeGrantKeeper,
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
SignModeHandler: app.txConfig.SignModeHandler(),
}
options := BSDKHandlerOptions{
BaseOptions: handlerOptions,
auctionkeeper: app.auctionkeeper,
TxDecoder: app.txConfig.TxDecoder(),
TxEncoder: app.txConfig.TxEncoder(),
FreeLane: freeLane,
MEVLane: mevLane,
}
anteHandler := NewBSDKAnteHandler(options)
app.App.SetAnteHandler(anteHandler)
// Set the ante handler on the lanes.
opt := []base.LaneOption{
base.WithAnteHandler(anteHandler),
}
mevLane.WithOptions(
opt...,
)
freeLane.WithOptions(
opt...,
)
defaultLane.WithOptions(
opt...,
)
// Step 6: Create the proposal handler and set it on the app. Now the application
// will build and verify proposals using the Block SDK!
//
// NOTE: It is recommended to use the default proposal handler by constructing
// using the NewDefaultProposalHandler function. This will use the correct prepare logic
// for the lanes, but the process logic will be a no-op. To read more about the default
// proposal handler, see the documentation in readme.md in this directory.
//
// If you want to customize the prepare and process logic, you can construct the proposal
// handler using the New function and setting the useProcess flag to true.
proposalHandler := abci.New( // use NewDefaultProposalHandler instead for default behavior (RECOMMENDED)
app.Logger(),
app.TxConfig().TxDecoder(),
app.TxConfig().TxEncoder(),
mempool,
true,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
cacheDecoder, err := utils.NewDefaultCacheTxDecoder(app.txConfig.TxDecoder())
if err != nil {
panic(err)
}
// Step 7: Set the custom CheckTx handler on BaseApp. This is only required if you
// use the MEV lane.
mevCheckTx := checktx.NewMEVCheckTxHandler(
app.App,
cacheDecoder.TxDecoder(),
mevLane,
anteHandler,
app.App.CheckTx,
)
checkTxHandler := checktx.NewMempoolParityCheckTx(
app.Logger(),
mempool,
cacheDecoder.TxDecoder(),
mevCheckTx.CheckTx(),
app.BaseApp,
)
app.SetCheckTx(checkTxHandler.CheckTx())
// ---------------------------------------------------------------------------- //
// ------------------------- End Custom Code ---------------------------------- //
// ---------------------------------------------------------------------------- //
/**** Module Options ****/
app.ModuleManager.RegisterInvariants(app.CrisisKeeper)
// RegisterUpgradeHandlers is used for registering any on-chain upgrades.
// app.RegisterUpgradeHandlers()
// add test gRPC service for testing gRPC queries in isolation
// testdata_pulsar.RegisterQueryServer(app.GRPCQueryRouter(), testdata_pulsar.QueryImpl{})
// A custom InitChainer can be set if extra pre-init-genesis logic is required.
// By default, when using app wiring enabled module, this is not required.
// For instance, the upgrade module will set automatically the module version map in its init genesis thanks to app wiring.
// However, when registering a module manually (i.e. that does not support app wiring), the module version map
// must be set manually as follow. The upgrade module will de-duplicate the module version map.
//
// app.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
// app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap())
// return app.App.InitChainer(ctx, req)
// })
if err := app.Load(loadLatest); err != nil {
panic(err)
}
return app
}
// CheckTx will check the transaction with the provided checkTxHandler. We override the default
// handler so that we can verify bid transactions before they are inserted into the mempool.
// With the POB CheckTx, we can verify the bid transaction and all of the bundled transactions
// before inserting the bid transaction into the mempool.
func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) (*cometabci.ResponseCheckTx, error) {
return app.checkTxHandler(req)
}
// SetCheckTx sets the checkTxHandler for the app.
func (app *TestApp) SetCheckTx(handler checktx.CheckTx) {
app.checkTxHandler = handler
}
// Name returns the name of the App
func (app *TestApp) Name() string { return app.BaseApp.Name() }
// LegacyAmino returns SimApp's amino codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
// for modules to register their own custom testing types.
func (app *TestApp) LegacyAmino() *codec.LegacyAmino {
return app.legacyAmino
}
// AppCodec returns SimApp's app codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
// for modules to register their own custom testing types.
func (app *TestApp) AppCodec() codec.Codec {
return app.appCodec
}
// InterfaceRegistry returns SimApp's InterfaceRegistry
func (app *TestApp) InterfaceRegistry() codectypes.InterfaceRegistry {
return app.interfaceRegistry
}
// TxConfig returns SimApp's TxConfig
func (app *TestApp) TxConfig() client.TxConfig {
return app.txConfig
}
// GetKey returns the KVStoreKey for the provided store key.
//
// NOTE: This is solely to be used for testing purposes.
func (app *TestApp) GetKey(storeKey string) *storetypes.KVStoreKey {
sk := app.UnsafeFindStoreKey(storeKey)
kvStoreKey, ok := sk.(*storetypes.KVStoreKey)
if !ok {
return nil
}
return kvStoreKey
}
// GetSubspace returns a param subspace for a given module name.
//
// NOTE: This is solely to be used for testing purposes.
func (app *TestApp) GetSubspace(moduleName string) paramstypes.Subspace {
subspace, _ := app.ParamsKeeper.GetSubspace(moduleName)
return subspace
}
// SimulationManager implements the SimulationApp interface
func (app *TestApp) SimulationManager() *module.SimulationManager {
return nil
}
// RegisterAPIRoutes registers all application module routes with the provided
// API server.
func (app *TestApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
// Register the base app API routes.
app.App.RegisterAPIRoutes(apiSvr, apiConfig)
// Register the Block SDK mempool API routes.
service.RegisterGRPCGatewayRoutes(apiSvr.ClientCtx, apiSvr.GRPCGatewayRouter)
// register swagger API in app.go so that other applications can override easily
if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil {
panic(err)
}
}
// RegisterTxService implements the Application.RegisterTxService method.
func (app *TestApp) RegisterTxService(clientCtx client.Context) {
// Register the base app transaction service.
app.App.RegisterTxService(clientCtx)
// Register the Block SDK mempool transaction service.
mempool, ok := app.App.Mempool().(block.Mempool)
if !ok {
panic("mempool is not a block.Mempool")
}
service.RegisterMempoolService(app.GRPCQueryRouter(), mempool)
}
// GetMaccPerms returns a copy of the module account permissions
//
// NOTE: This is solely to be used for testing purposes.
func GetMaccPerms() map[string][]string {
dup := make(map[string][]string)
for _, perms := range moduleAccPerms {
dup[perms.Account] = perms.Permissions
}
return dup
}
// BlockedAddresses returns all the app's blocked account addresses.
func BlockedAddresses() map[string]bool {
result := make(map[string]bool)
if len(blockAccAddrs) > 0 {
for _, addr := range blockAccAddrs {
result[addr] = true
}
} else {
for addr := range GetMaccPerms() {
result[addr] = true
}
}
return result
}