app

package
v2.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2024 License: Apache-2.0 Imports: 118 Imported by: 0

README

Block SDK Enabled Test App (10 min)

Overview

This readme describes how to build a test app that uses the Block SDK. This assumes that you have already installed the Block SDK and have a working development environment. To install the Block SDK, please run the following:

go get github.com/skip-mev/block-sdk

Building the Test App

There are fix critical steps to building a test app that uses the Block SDK:

  1. Set up the signer extractor.
  2. Create the lane configurations for each individual lane i.e. LaneConfig.
  3. Configure the match handlers for each lane i.e. MatchHandler.
  4. Creating the Block SDK mempool i.e. LanedMempool.
  5. Setting the antehandlers - used for transaction validation - for each lane.
  6. Setting the proposal handlers - used for block creation and verification - for the application to utilize the Block SDK's Prepare and Process Proposal handlers.
1. Signer Extractor

The signer extractor is responsible for extracting signers and relevant information about who is signing the transaction. We recommend using the default implementation provided by the Block SDK.

signerAdapter := signerextraction.NewDefaultAdapter()
2. Lane Configurations

This controls how many transactions can be stored by each lane, how much block space is allocated to each lane, how to extract transacation information such as signers, fees, and more. Each lane should have a separate LaneConfig object.

For example, in lanes.go we see the following:

mevConfig := base.LaneConfig{
	Logger:          app.Logger(),
	TxEncoder:       app.txConfig.TxEncoder(),
	TxDecoder:       app.txConfig.TxDecoder(),
	MaxBlockSpace:   math.LegacyMustNewDecFromStr("0.2"),
	SignerExtractor: signerAdapter,
	MaxTxs:          1000,
}

Following the example above:

  • Logger: This is the logger that will be utilized by the lane when outputting information as blocks are being processed and constructed.
  • TxEncoder: This is the encoder that will be used to encode transactions.
  • TxDecoder: This is the decoder that will be used to decode transactions.
  • MaxBlockSpace: This is the maximum amount of block space that can be allocated to this lane. In this case, we allocate 20% of the block space to this lane.
  • SignerExtractor: This is the signer extractor that will be used to extract signers from transactions. In this case, we utilize the default signer extractor provided by the Block SDK. This is the recommended approach.
  • MaxTxs: This is the maximum number of transactions that can be stored in this lane. In this case, we allow up to 1000 transactions to be stored in this lane at any given time.
Match Handlers

Match handlers are responsible for matching transactions to lanes. Each lane should have a unique match handler. By default, we recommend that the default lane be the last lane in your application. This is because the default lane matches all transactions that do not match to any of the other lanes. If you want to have a lane after the default lane, please see the section below.

(OPTIONAL) Having Lanes after the Default Lane

If you want to have lanes after the default lane, you will need to utilize the base.NewMatchHandler function. This function allows you to construct a match handler that can ignore other lane's match handlers.

For example, if we wanted the free and MEV lanes to be processed after the default lane - default, MEV, free - we can do the following:

// Create the final match handler for the default lane.
defaultMatchHandler := base.NewMatchHandler(
	base.DefaultMatchHandler(),
	factory.MatchHandler(),
	freelane.DefaultMatchHandler(),
)

Following the example, we can see the following:

  • base.DefaultMatchHandler(): This is the default match handler provided by the Block SDK. This matches all transactions to the lane.
  • factory.MatchHandler(): This is the MEV lane's match handler. This is passed as a parameter to the base.NewMatchHandler function - which means that all transactions that match to the MEV lane will be ignored by the default match handler.
  • freelane.DefaultMatchHandler(): This is the default match handler for the free lane. This is passed as a parameter to the base.NewMatchHandler function - which means that all transactions that match to the free lane will be ignored by the default match handler.

This will allow the default match handler to only match transactions that do not match to the MEV lane or the free lane.

Block SDK Mempool

After constructing the lanes, we can create the Block SDK mempool - LanedMempool. This object is responsible for managing the lanes and processing transactions.

// STEP 1: Create the Block SDK lanes.
mevLane, freeLane, defaultLane := CreateLanes(app)

// STEP 2: Construct a mempool based off the lanes.
mempool, err := block.NewLanedMempool(
	app.Logger(),
	[]block.Lane{mevLane, freeLane, defaultLane},
	&app.blocksdkKeeper,
)
if err != nil {
	panic(err)
}

// STEP 3: Set the mempool on the app.
app.App.SetMempool(mempool)

Note that we pass the lanes to the block.NewLanedMempool function. The order of the lanes is important. Proposals will be constructed based on the order of lanes passed to the block.NewLanedMempool function. In the example above, the MEV lane will be processed first, followed by the free lane, and finally the default lane.

AnteHandlers

AnteHandlers are responsible for validating transactions. We recommend that developers utilize the same antehandler chain that is used by the application. In the example test app, we construct the AnteHandler with NewBSDKAnteHandler. In the case where the certain ante decorators should ignore certain lanes, we can wrap a Decorator with the block.NewIgnoreDecorator function as seen in ante.go.

After constructing the AnteHandler, we can set it on the application and on the lanes.

// STEP 4: 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 AnteHandlers on the lanes.
mevLane.SetAnteHandler(anteHandler)
freeLane.SetAnteHandler(anteHandler)
defaultLane.SetAnteHandler(anteHandler)
Proposal Handlers

The proposal handlers - PrepareProposal and ProcessProposal - are responsible for building and verifying block proposals. To add it to your application, follow the example below:

// Step 5: Create the proposal handler and set it on the app.
proposalHandler := abci.NewProposalHandler(
	app.Logger(),
	app.TxConfig().TxDecoder(),
	app.TxConfig().TxEncoder(),
	mempool,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())

Conclusion

Adding the Block SDK to your application is a simple 6 step process. If you have any questions, please feel free to reach out to the Skip team. We are happy to help!

Documentation

Index

Constants

View Source
const (
	ChainID = "chain-id-0"
)

Variables

View Source
var (
	BondDenom = sdk.DefaultBondDenom

	// DefaultNodeHome default home directories for the application daemon
	DefaultNodeHome string

	// ModuleBasics defines the module BasicManager is in charge of setting up basic,
	// non-dependant module elements, such as codec registration
	// and genesis verification.
	ModuleBasics = module.NewBasicManager(
		auth.AppModuleBasic{},
		genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
		bank.AppModuleBasic{},
		staking.AppModuleBasic{},
		mint.AppModuleBasic{},
		distr.AppModuleBasic{},
		gov.NewAppModuleBasic(
			[]govclient.ProposalHandler{
				paramsclient.ProposalHandler,
			},
		),
		params.AppModuleBasic{},
		slashing.AppModuleBasic{},
		upgrade.AppModuleBasic{},
		authzmodule.AppModuleBasic{},
		groupmodule.AppModuleBasic{},
		vesting.AppModuleBasic{},
		consensus.AppModuleBasic{},
		auctionmodule.AppModuleBasic{},
		feegrantmodule.AppModuleBasic{},
	)
)
View Source
var (

	// application configuration (used by depinject)
	AppConfig = depinject.Configs(appconfig.Compose(&appv1alpha1.Config{
		Modules: []*appv1alpha1.ModuleConfig{
			{
				Name: runtime.ModuleName,
				Config: appconfig.WrapAny(&runtimev1alpha1.Module{
					AppName: "TestApp",

					BeginBlockers: []string{
						upgradetypes.ModuleName,
						minttypes.ModuleName,
						distrtypes.ModuleName,
						slashingtypes.ModuleName,
						stakingtypes.ModuleName,
						genutiltypes.ModuleName,
						authz.ModuleName,
						auctiontypes.ModuleName,
						feegrant.ModuleName,
					},
					EndBlockers: []string{
						crisistypes.ModuleName,
						govtypes.ModuleName,
						stakingtypes.ModuleName,
						genutiltypes.ModuleName,
						group.ModuleName,
						auctiontypes.ModuleName,
						feegrant.ModuleName,
					},
					OverrideStoreKeys: []*runtimev1alpha1.StoreKeyConfig{
						{
							ModuleName: authtypes.ModuleName,
							KvStoreKey: "acc",
						},
					},

					InitGenesis: []string{
						authtypes.ModuleName,
						banktypes.ModuleName,
						distrtypes.ModuleName,
						stakingtypes.ModuleName,
						slashingtypes.ModuleName,
						govtypes.ModuleName,
						minttypes.ModuleName,
						crisistypes.ModuleName,
						genutiltypes.ModuleName,
						authz.ModuleName,
						group.ModuleName,
						paramstypes.ModuleName,
						upgradetypes.ModuleName,
						vestingtypes.ModuleName,
						consensustypes.ModuleName,
						circuittypes.ModuleName,
						auctiontypes.ModuleName,
						feegrant.ModuleName,
					},
				}),
			},
			{
				Name: authtypes.ModuleName,
				Config: appconfig.WrapAny(&authmodulev1.Module{
					Bech32Prefix:             "cosmos",
					ModuleAccountPermissions: moduleAccPerms,
				}),
			},
			{
				Name:   vestingtypes.ModuleName,
				Config: appconfig.WrapAny(&vestingmodulev1.Module{}),
			},
			{
				Name:   feegrant.ModuleName,
				Config: appconfig.WrapAny(&feegrantmodulev1.Module{}),
			},
			{
				Name: banktypes.ModuleName,
				Config: appconfig.WrapAny(&bankmodulev1.Module{
					BlockedModuleAccountsOverride: blockAccAddrs,
				}),
			},
			{
				Name:   stakingtypes.ModuleName,
				Config: appconfig.WrapAny(&stakingmodulev1.Module{}),
			},
			{
				Name:   slashingtypes.ModuleName,
				Config: appconfig.WrapAny(&slashingmodulev1.Module{}),
			},
			{
				Name:   paramstypes.ModuleName,
				Config: appconfig.WrapAny(&paramsmodulev1.Module{}),
			},
			{
				Name:   "tx",
				Config: appconfig.WrapAny(&txconfigv1.Config{}),
			},
			{
				Name:   genutiltypes.ModuleName,
				Config: appconfig.WrapAny(&genutilmodulev1.Module{}),
			},
			{
				Name:   authz.ModuleName,
				Config: appconfig.WrapAny(&authzmodulev1.Module{}),
			},
			{
				Name:   upgradetypes.ModuleName,
				Config: appconfig.WrapAny(&upgrademodulev1.Module{}),
			},
			{
				Name:   distrtypes.ModuleName,
				Config: appconfig.WrapAny(&distrmodulev1.Module{}),
			},
			{
				Name:   minttypes.ModuleName,
				Config: appconfig.WrapAny(&mintmodulev1.Module{}),
			},
			{
				Name: group.ModuleName,
				Config: appconfig.WrapAny(&groupmodulev1.Module{
					MaxExecutionPeriod: durationpb.New(time.Second * 1209600),
					MaxMetadataLen:     255,
				}),
			},
			{
				Name:   govtypes.ModuleName,
				Config: appconfig.WrapAny(&govmodulev1.Module{}),
			},
			{
				Name:   crisistypes.ModuleName,
				Config: appconfig.WrapAny(&crisismodulev1.Module{}),
			},
			{
				Name:   consensustypes.ModuleName,
				Config: appconfig.WrapAny(&consensusmodulev1.Module{}),
			},
			{
				Name:   circuittypes.ModuleName,
				Config: appconfig.WrapAny(&circuitmodulev1.Module{}),
			},
			{
				Name:   auctiontypes.ModuleName,
				Config: appconfig.WrapAny(&auctionmodulev1.Module{}),
			},
		},
	}),
		depinject.Supply(

			map[string]module.AppModuleBasic{
				genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
				govtypes.ModuleName: gov.NewAppModuleBasic(
					[]govclient.ProposalHandler{
						paramsclient.ProposalHandler,
					},
				),
			},
		))
)

Functions

func BlockedAddresses

func BlockedAddresses() map[string]bool

BlockedAddresses returns all the app's blocked account addresses.

func CreateLanes

func CreateLanes(app *TestApp) (*mevlane.MEVLane, *base.BaseLane, *base.BaseLane)

CreateLanes walks through the process of creating the lanes for the block sdk. In this function we create three separate lanes - MEV, Free, and Default - and then return them.

NOTE: Application Developers should closely replicate this function in their own application.

func GetMaccPerms

func GetMaccPerms() map[string][]string

GetMaccPerms returns a copy of the module account permissions

NOTE: This is solely to be used for testing purposes.

func NewBSDKAnteHandler

func NewBSDKAnteHandler(options BSDKHandlerOptions) sdk.AnteHandler

NewBSDKAnteHandler wraps all of the default Cosmos SDK AnteDecorators with the custom block-sdk AnteHandlers.

func NewTestNetworkFixture

func NewTestNetworkFixture() network.TestFixture

NewTestNetworkFixture returns a new simapp AppConstructor for network simulation tests

Types

type BSDKHandlerOptions

type BSDKHandlerOptions struct {
	BaseOptions ante.HandlerOptions
	MEVLane     auctionante.MEVLane
	TxDecoder   sdk.TxDecoder
	TxEncoder   sdk.TxEncoder

	FreeLane block.Lane
	// contains filtered or unexported fields
}

type TestApp

type TestApp struct {
	*runtime.App

	// keepers
	AccountKeeper         authkeeper.AccountKeeper
	BankKeeper            bankkeeper.Keeper
	StakingKeeper         *stakingkeeper.Keeper
	SlashingKeeper        slashingkeeper.Keeper
	MintKeeper            mintkeeper.Keeper
	DistrKeeper           distrkeeper.Keeper
	GovKeeper             *govkeeper.Keeper
	UpgradeKeeper         *upgradekeeper.Keeper
	ParamsKeeper          paramskeeper.Keeper
	AuthzKeeper           authzkeeper.Keeper
	GroupKeeper           groupkeeper.Keeper
	ConsensusParamsKeeper consensuskeeper.Keeper
	CircuitBreakerKeeper  circuitkeeper.Keeper

	FeeGrantKeeper feegrantkeeper.Keeper
	// contains filtered or unexported fields
}

func New

func New(
	logger log.Logger,
	db dbm.DB,
	traceStore io.Writer,
	loadLatest bool,
	appOpts servertypes.AppOptions,
	baseAppOptions ...func(*baseapp.BaseApp),
) *TestApp

func (*TestApp) AppCodec

func (app *TestApp) AppCodec() codec.Codec

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 (*TestApp) CheckTx

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 (*TestApp) ExportAppStateAndValidators

func (app *TestApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs, modulesToExport []string) (servertypes.ExportedApp, error)

ExportAppStateAndValidators exports the state of the application for a genesis file.

func (*TestApp) GetKey

func (app *TestApp) GetKey(storeKey string) *storetypes.KVStoreKey

GetKey returns the KVStoreKey for the provided store key.

NOTE: This is solely to be used for testing purposes.

func (*TestApp) GetSubspace

func (app *TestApp) GetSubspace(moduleName string) paramstypes.Subspace

GetSubspace returns a param subspace for a given module name.

NOTE: This is solely to be used for testing purposes.

func (*TestApp) InterfaceRegistry

func (app *TestApp) InterfaceRegistry() codectypes.InterfaceRegistry

InterfaceRegistry returns SimApp's InterfaceRegistry

func (*TestApp) LegacyAmino

func (app *TestApp) LegacyAmino() *codec.LegacyAmino

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 (*TestApp) Name

func (app *TestApp) Name() string

Name returns the name of the App

func (*TestApp) RegisterAPIRoutes

func (app *TestApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig)

RegisterAPIRoutes registers all application module routes with the provided API server.

func (*TestApp) RegisterTxService

func (app *TestApp) RegisterTxService(clientCtx client.Context)

RegisterTxService implements the Application.RegisterTxService method.

func (*TestApp) SetCheckTx

func (app *TestApp) SetCheckTx(handler checktx.CheckTx)

SetCheckTx sets the checkTxHandler for the app.

func (*TestApp) SimulationManager

func (app *TestApp) SimulationManager() *module.SimulationManager

SimulationManager implements the SimulationApp interface

func (*TestApp) TxConfig

func (app *TestApp) TxConfig() client.TxConfig

TxConfig returns SimApp's TxConfig

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL