From 4da95939f56cf3f8766c08fd6d419a6480066171 Mon Sep 17 00:00:00 2001 From: David Terpay <35130517+davidterpay@users.noreply.github.com> Date: Thu, 11 May 2023 14:58:35 -0400 Subject: [PATCH] feat: Integrating POB into sim-app base app. (#130) Co-authored-by: Aleksandr Bezobchuk --- go.mod | 2 +- go.sum | 4 +-- mempool/mempool.go | 71 ++++++++++++++++++++++++------------- tests/app/ante.go | 55 ++++++++++++++++++++++++++++ tests/app/app.go | 41 +++++++++++++++++++++ tests/app/config.go | 11 +++++- tests/e2e/e2e_setup_test.go | 16 +++++++-- tests/e2e/genesis.go | 13 ++++++- x/builder/types/genesis.go | 18 ++++++++++ 9 files changed, 200 insertions(+), 31 deletions(-) create mode 100644 tests/app/ante.go diff --git a/go.mod b/go.mod index ee88397..f5880d2 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.3 github.com/cosmos/cosmos-sdk v0.47.2 github.com/cosmos/go-bip39 v1.0.0 - github.com/cosmos/gogoproto v1.4.9 + github.com/cosmos/gogoproto v1.4.8 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index e422c41..b7e4e7f 100644 --- a/go.sum +++ b/go.sum @@ -333,8 +333,8 @@ github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4x github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= -github.com/cosmos/gogoproto v1.4.9 h1:MjVmV6F1yk1rJLWtKeYdGQcTbE880t+VlRcayEBqUKQ= -github.com/cosmos/gogoproto v1.4.9/go.mod h1:c0ysUnwvnlR+RmCUvqqii7pp8kHBB/DBcp/5VLA/nQk= +github.com/cosmos/gogoproto v1.4.8 h1:BrHKc6WFZt8+jRV71vKSQE+JrfF+JAnzrKo2VP7wIZ4= +github.com/cosmos/gogoproto v1.4.8/go.mod h1:hnb0DIEWTv+wdNzNcqus5xCQXq5+CXauq1FJuurRfVY= github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= diff --git a/mempool/mempool.go b/mempool/mempool.go index ea09872..fed8a7d 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -11,36 +11,59 @@ import ( sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" ) -var _ sdkmempool.Mempool = (*AuctionMempool)(nil) +var _ Mempool = (*AuctionMempool)(nil) -// AuctionMempool defines an auction mempool. It can be seen as an extension of -// an SDK PriorityNonceMempool, i.e. a mempool that supports -// two-dimensional priority ordering, with the additional support of prioritizing -// and indexing auction bids. -type AuctionMempool struct { - // globalIndex defines the index of all transactions in the mempool. It uses - // the SDK's builtin PriorityNonceMempool. Once a bid is selected for top-of-block, - // all subsequent transactions in the mempool will be selected from this index. - globalIndex sdkmempool.Mempool +type ( + // Mempool defines the interface for a POB mempool. + Mempool interface { + // Inherit the methods of the SDK's Mempool interface. + sdkmempool.Mempool - // auctionIndex defines an index of auction bids. - auctionIndex sdkmempool.Mempool + // GetTopAuctionTx returns the top auction bid transaction in the mempool. + GetTopAuctionTx(ctx context.Context) sdk.Tx - // txDecoder defines the sdk.Tx decoder that allows us to decode transactions - // and construct sdk.Txs from the bundled transactions. - txDecoder sdk.TxDecoder + // CoutnAuctionTx returns the number of auction bid transactions in the mempool. + CountAuctionTx() int - // txEncoder defines the sdk.Tx encoder that allows us to encode transactions - // to bytes. - txEncoder sdk.TxEncoder + // AuctionBidSelect returns an iterator over the auction bid transactions in the mempool. + AuctionBidSelect(ctx context.Context) sdkmempool.Iterator - // txIndex is a map of all transactions in the mempool. It is used - // to quickly check if a transaction is already in the mempool. - txIndex map[string]struct{} + // Contains returns true if the mempool contains the given transaction. + Contains(tx sdk.Tx) (bool, error) - // AuctionFactory implements the functionality required to process auction transactions. - AuctionFactory -} + // AuctionFactory implements the functionality required to process auction transactions. + AuctionFactory + } + + // AuctionMempool defines an auction mempool. It can be seen as an extension of + // an SDK PriorityNonceMempool, i.e. a mempool that supports + // two-dimensional priority ordering, with the additional support of prioritizing + // and indexing auction bids. + AuctionMempool struct { + // globalIndex defines the index of all transactions in the mempool. It uses + // the SDK's builtin PriorityNonceMempool. Once a bid is selected for top-of-block, + // all subsequent transactions in the mempool will be selected from this index. + globalIndex sdkmempool.Mempool + + // auctionIndex defines an index of auction bids. + auctionIndex sdkmempool.Mempool + + // txDecoder defines the sdk.Tx decoder that allows us to decode transactions + // and construct sdk.Txs from the bundled transactions. + txDecoder sdk.TxDecoder + + // txEncoder defines the sdk.Tx encoder that allows us to encode transactions + // to bytes. + txEncoder sdk.TxEncoder + + // txIndex is a map of all transactions in the mempool. It is used + // to quickly check if a transaction is already in the mempool. + txIndex map[string]struct{} + + // AuctionFactory implements the functionality required to process auction transactions. + AuctionFactory + } +) // AuctionTxPriority returns a TxPriority over auction bid transactions only. It // is to be used in the auction index only. diff --git a/tests/app/ante.go b/tests/app/ante.go new file mode 100644 index 0000000..76ac1c1 --- /dev/null +++ b/tests/app/ante.go @@ -0,0 +1,55 @@ +package app + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/skip-mev/pob/mempool" + builderante "github.com/skip-mev/pob/x/builder/ante" + builderkeeper "github.com/skip-mev/pob/x/builder/keeper" +) + +type POBHandlerOptions struct { + BaseOptions ante.HandlerOptions + Mempool mempool.Mempool + TxDecoder sdk.TxDecoder + TxEncoder sdk.TxEncoder + BuilderKeeper builderkeeper.Keeper +} + +// NewPOBAnteHandler wraps all of the default Cosmos SDK AnteDecorators with the POB AnteHandler. +func NewPOBAnteHandler(options POBHandlerOptions) sdk.AnteHandler { + if options.BaseOptions.AccountKeeper == nil { + panic("account keeper is required for ante builder") + } + + if options.BaseOptions.BankKeeper == nil { + panic("bank keeper is required for ante builder") + } + + if options.BaseOptions.SignModeHandler == nil { + panic("sign mode handler is required for ante builder") + } + + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + ante.NewExtensionOptionsDecorator(options.BaseOptions.ExtensionOptionChecker), + ante.NewValidateBasicDecorator(), + ante.NewTxTimeoutHeightDecorator(), + ante.NewValidateMemoDecorator(options.BaseOptions.AccountKeeper), + ante.NewConsumeGasForTxSizeDecorator(options.BaseOptions.AccountKeeper), + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + ante.NewSetPubKeyDecorator(options.BaseOptions.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators + ante.NewValidateSigCountDecorator(options.BaseOptions.AccountKeeper), + ante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer), + ante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler), + ante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper), + builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxDecoder, options.TxEncoder, options.Mempool), + } + + return sdk.ChainAnteDecorators(anteDecorators...) +} diff --git a/tests/app/app.go b/tests/app/app.go index 001b507..9b61366 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -25,6 +25,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/ante" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import for side-effects "github.com/cosmos/cosmos-sdk/x/auth/vesting" @@ -65,6 +66,10 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/skip-mev/pob/abci" + "github.com/skip-mev/pob/mempool" + buildermodule "github.com/skip-mev/pob/x/builder" + builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ) var ( @@ -102,6 +107,7 @@ var ( vesting.AppModuleBasic{}, nftmodule.AppModuleBasic{}, consensus.AppModuleBasic{}, + buildermodule.AppModuleBasic{}, ) ) @@ -135,6 +141,7 @@ type TestApp struct { FeeGrantKeeper feegrantkeeper.Keeper GroupKeeper groupkeeper.Keeper ConsensusParamsKeeper consensuskeeper.Keeper + BuilderKeeper builderkeeper.Keeper } func init() { @@ -212,6 +219,7 @@ func New( &app.EvidenceKeeper, &app.FeeGrantKeeper, &app.GroupKeeper, + &app.BuilderKeeper, &app.ConsensusParamsKeeper, ); err != nil { panic(err) @@ -245,6 +253,39 @@ func New( app.App = appBuilder.Build(logger, db, traceStore, baseAppOptions...) + // Set POB's mempool into the app. + mempool := mempool.NewAuctionMempool(app.txConfig.TxDecoder(), app.txConfig.TxEncoder(), 0, mempool.NewDefaultAuctionFactory(app.txConfig.TxDecoder())) + app.App.SetMempool(mempool) + + // Create a global ante handler that will be called on each transaction when + // proposals are being built and verified. + handlerOptions := ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + SignModeHandler: app.txConfig.SignModeHandler(), + } + options := POBHandlerOptions{ + BaseOptions: handlerOptions, + BuilderKeeper: app.BuilderKeeper, + Mempool: mempool, + TxDecoder: app.txConfig.TxDecoder(), + TxEncoder: app.txConfig.TxEncoder(), + } + anteHandler := NewPOBAnteHandler(options) + + // Set the proposal handlers on the BaseApp. + proposalHandlers := abci.NewProposalHandler( + mempool, + app.App.Logger(), + anteHandler, + options.TxEncoder, + options.TxDecoder, + ) + app.App.SetPrepareProposal(proposalHandlers.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandlers.ProcessProposalHandler()) + // load state streaming if enabled if _, _, err := streaming.LoadStreamingServices(app.App.BaseApp, appOpts, app.appCodec, logger, app.kvStoreKeys()); err != nil { logger.Error("failed to load state streaming", "err", err) diff --git a/tests/app/config.go b/tests/app/config.go index 86dc42a..e34db9c 100644 --- a/tests/app/config.go +++ b/tests/app/config.go @@ -43,6 +43,8 @@ import ( slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + buildermodulev1 "github.com/skip-mev/pob/api/pob/builder/module/v1" + buildertypes "github.com/skip-mev/pob/x/builder/types" "google.golang.org/protobuf/types/known/durationpb" ) @@ -59,7 +61,7 @@ var ( distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, - vestingtypes.ModuleName, consensustypes.ModuleName, + vestingtypes.ModuleName, consensustypes.ModuleName, buildertypes.ModuleName, } // module account permissions @@ -70,6 +72,7 @@ var ( {Account: stakingtypes.BondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}}, {Account: stakingtypes.NotBondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}}, {Account: govtypes.ModuleName, Permissions: []string{authtypes.Burner}}, + {Account: buildertypes.ModuleName, Permissions: []string{}}, } // blocked account addresses @@ -113,6 +116,7 @@ var ( group.ModuleName, paramstypes.ModuleName, vestingtypes.ModuleName, + buildertypes.ModuleName, consensustypes.ModuleName, }, EndBlockers: []string{ @@ -134,6 +138,7 @@ var ( consensustypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, + buildertypes.ModuleName, }, OverrideStoreKeys: []*runtimev1alpha1.StoreKeyConfig{ { @@ -238,6 +243,10 @@ var ( Name: consensustypes.ModuleName, Config: appconfig.WrapAny(&consensusmodulev1.Module{}), }, + { + Name: buildertypes.ModuleName, + Config: appconfig.WrapAny(&buildermodulev1.Module{}), + }, }, }) ) diff --git a/tests/e2e/e2e_setup_test.go b/tests/e2e/e2e_setup_test.go index 18245b7..e0aaa77 100644 --- a/tests/e2e/e2e_setup_test.go +++ b/tests/e2e/e2e_setup_test.go @@ -23,6 +23,7 @@ import ( "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/skip-mev/pob/tests/app" + "github.com/skip-mev/pob/x/builder/types" "github.com/spf13/viper" "github.com/stretchr/testify/suite" ) @@ -105,10 +106,21 @@ func (s *IntegrationTestSuite) initNodes() { // initialize a genesis file for the first validator val0ConfigDir := s.chain.validators[0].configDir() + + // Define the builder module parameters + params := types.Params{ + MaxBundleSize: 5, + EscrowAccountAddress: "cosmos14j5j2lsx7629590jvpk3vj0xe9w8203jf4yknk", + ReserveFee: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)), + MinBuyInFee: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)), + MinBidIncrement: sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)), + ProposerFee: sdk.NewDecWithPrec(1, 2), + } + for _, val := range s.chain.validators { valAddr, err := val.keyInfo.GetAddress() s.Require().NoError(err) - s.Require().NoError(addGenesisAccount(val0ConfigDir, "", initBalanceStr, valAddr)) + s.Require().NoError(initGenesisFile(val0ConfigDir, "", initBalanceStr, valAddr, params)) } // copy the genesis file to the remaining validators @@ -283,7 +295,7 @@ func (s *IntegrationTestSuite) runValidators() { return true }, - 5*time.Minute, + 2*time.Minute, time.Second, "POB TestApp node failed to produce blocks", ) diff --git a/tests/e2e/genesis.go b/tests/e2e/genesis.go index 9314722..e7dc963 100644 --- a/tests/e2e/genesis.go +++ b/tests/e2e/genesis.go @@ -12,6 +12,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/skip-mev/pob/x/builder/types" ) func getGenDoc(path string) (*comettypes.GenesisDoc, error) { @@ -38,7 +39,7 @@ func getGenDoc(path string) (*comettypes.GenesisDoc, error) { return doc, nil } -func addGenesisAccount(path, moniker, amountStr string, accAddr sdk.AccAddress) error { +func initGenesisFile(path, moniker, amountStr string, accAddr sdk.AccAddress, params types.Params) error { serverCtx := server.NewDefaultContext() config := serverCtx.Config @@ -100,6 +101,16 @@ func addGenesisAccount(path, moniker, amountStr string, accAddr sdk.AccAddress) appState[banktypes.ModuleName] = bankGenStateBz + builderGenState := types.GetGenesisStateFromAppState(cdc, appState) + builderGenState.Params = params + + builderGenStateBz, err := cdc.MarshalJSON(&builderGenState) + if err != nil { + return fmt.Errorf("failed to marshal builder genesis state: %w", err) + } + + appState[types.ModuleName] = builderGenStateBz + appStateJSON, err := json.Marshal(appState) if err != nil { return fmt.Errorf("failed to marshal application genesis state: %w", err) diff --git a/x/builder/types/genesis.go b/x/builder/types/genesis.go index 56f3f24..b03f485 100644 --- a/x/builder/types/genesis.go +++ b/x/builder/types/genesis.go @@ -1,5 +1,11 @@ package types +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" +) + // NewGenesisState creates a new GenesisState instance. func NewGenesisState(params Params) *GenesisState { return &GenesisState{ @@ -18,3 +24,15 @@ func DefaultGenesisState() *GenesisState { func (gs GenesisState) Validate() error { return gs.Params.Validate() } + +// GetGenesisStateFromAppState returns x/builder GenesisState given raw application +// genesis state. +func GetGenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessage) GenesisState { + var genesisState GenesisState + + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return genesisState +}