chore(auction): Moving bid extraction to from msg handler to ante handler (#135)

* moving bid extraction to antehandler

* lint

* fix

* no zero timeout

* reset integration tests
This commit is contained in:
David Terpay 2023-10-04 18:38:26 -04:00 committed by GitHub
parent d495b38954
commit 3374203c39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 524 additions and 925 deletions

View File

@ -162,7 +162,10 @@ func (handler *CheckTxHandler) CheckTx() CheckTx {
handler.baseApp.Logger().Info(
"invalid bid tx",
"err", err,
"tx", req.Tx,
"height", ctx.BlockHeight(),
"bid_height", bidInfo.Timeout,
"bidder", bidInfo.Bidder,
"bid", bidInfo.Bid,
"removing tx from mempool", true,
)
@ -187,7 +190,10 @@ func (handler *CheckTxHandler) CheckTx() CheckTx {
handler.baseApp.Logger().Info(
"valid bid tx",
"tx", req.Tx,
"height", ctx.BlockHeight(),
"bid_height", bidInfo.Timeout,
"bidder", bidInfo.Bidder,
"bid", bidInfo.Bid,
"inserting tx into mempool", true,
)

View File

@ -11,7 +11,6 @@ import (
type BSDKHandlerOptions struct {
BaseOptions ante.HandlerOptions
Mempool block.Mempool
MEVLane auctionante.MEVLane
TxDecoder sdk.TxDecoder
TxEncoder sdk.TxEncoder
@ -55,7 +54,7 @@ func NewBSDKAnteHandler(options BSDKHandlerOptions) sdk.AnteHandler {
ante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer),
ante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper),
auctionante.NewAuctionDecorator(options.auctionkeeper, options.TxEncoder, options.MEVLane, options.Mempool),
auctionante.NewAuctionDecorator(options.auctionkeeper, options.TxEncoder, options.MEVLane),
}
return sdk.ChainAnteDecorators(anteDecorators...)

View File

@ -328,7 +328,6 @@ func New(
TxEncoder: app.txConfig.TxEncoder(),
FreeLane: freeLane,
MEVLane: mevLane,
Mempool: mempool,
}
anteHandler := NewBSDKAnteHandler(options)

View File

@ -1,145 +0,0 @@
package testutils
import (
"context"
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/golang/mock/gomock"
)
type MockAccountKeeper struct {
ctrl *gomock.Controller
recorder *MockAccountKeeperMockRecorder
}
type MockAccountKeeperMockRecorder struct {
mock *MockAccountKeeper
}
func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper {
mock := &MockAccountKeeper{ctrl: ctrl}
mock.recorder = &MockAccountKeeperMockRecorder{mock}
return mock
}
func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder {
return m.recorder
}
func (m *MockAccountKeeper) GetModuleAddress(name string) sdk.AccAddress {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetModuleAddress", name)
ret0, _ := ret[0].(sdk.AccAddress)
return ret0
}
func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name)
}
type MockBankKeeper struct {
ctrl *gomock.Controller
recorder *MockBankKeeperMockRecorder
}
type MockBankKeeperMockRecorder struct {
mock *MockBankKeeper
}
func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper {
mock := &MockBankKeeper{ctrl: ctrl}
mock.recorder = &MockBankKeeperMockRecorder{mock}
return mock
}
func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
return m.recorder
}
func (m *MockBankKeeper) GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
ret0 := ret[0].(sdk.Coin)
return ret0
}
func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBalance", reflect.TypeOf((*MockBankKeeper)(nil).GetBalance), ctx, addr, denom)
}
func (m *MockBankKeeper) SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt)
return nil
}
func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoins", reflect.TypeOf((*MockBankKeeper)(nil).SendCoins), ctx, fromAddr, toAddr, amt)
}
type MockDistributionKeeperRecorder struct {
mock *MockDistributionKeeper
}
type MockDistributionKeeper struct {
ctrl *gomock.Controller
recorder *MockDistributionKeeperRecorder
}
func NewMockDistributionKeeper(ctrl *gomock.Controller) *MockDistributionKeeper {
mock := &MockDistributionKeeper{ctrl: ctrl}
mock.recorder = &MockDistributionKeeperRecorder{mock}
return mock
}
func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperRecorder {
return m.recorder
}
func (m *MockDistributionKeeper) GetPreviousProposerConsAddr(ctx context.Context) (sdk.ConsAddress, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPreviousProposerConsAddr", ctx)
ret0 := ret[0].(sdk.ConsAddress)
return ret0, nil
}
func (mr *MockDistributionKeeperRecorder) GetPreviousProposerConsAddr(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPreviousProposerConsAddr", reflect.TypeOf((*MockDistributionKeeper)(nil).GetPreviousProposerConsAddr), ctx)
}
type MockStakingKeeperRecorder struct {
mock *MockStakingKeeper
}
type MockStakingKeeper struct {
ctrl *gomock.Controller
recorder *MockStakingKeeperRecorder
}
func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper {
mock := &MockStakingKeeper{ctrl: ctrl}
mock.recorder = &MockStakingKeeperRecorder{mock}
return mock
}
func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperRecorder {
return m.recorder
}
func (m *MockStakingKeeper) GetValidatorByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (stakingtypes.Validator, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetValidatorByConsAddr", ctx, consAddr)
ret0 := ret[0].(stakingtypes.Validator)
return ret0, nil
}
func (mr *MockStakingKeeperRecorder) GetValidatorByConsAddr(ctx, consAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorByConsAddr", reflect.TypeOf((*MockStakingKeeper)(nil).GetValidatorByConsAddr), ctx, consAddr)
}

View File

@ -2,46 +2,28 @@ package ante
import (
"bytes"
"context"
"fmt"
"cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/block-sdk/x/auction/keeper"
"github.com/skip-mev/block-sdk/x/auction/types"
)
var _ sdk.AnteDecorator = AuctionDecorator{}
type (
// MEVLane is an interface that defines the methods required to interact with the MEV
// lane.
MEVLane interface {
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
GetTopAuctionTx(ctx context.Context) sdk.Tx
}
// Mempool is an interface that defines the methods required to interact with the application-side mempool.
Mempool interface {
Contains(tx sdk.Tx) bool
}
// AuctionDecorator is an AnteDecorator that validates the auction bid and bundled transactions.
AuctionDecorator struct {
auctionkeeper keeper.Keeper
txEncoder sdk.TxEncoder
lane MEVLane
mempool Mempool
auctionkeeper AuctionKeeper
txEncoder sdk.TxEncoder
}
)
func NewAuctionDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane MEVLane, mempool Mempool) AuctionDecorator {
// NewAuctionDecorator returns a new AuctionDecorator.
func NewAuctionDecorator(ak AuctionKeeper, txEncoder sdk.TxEncoder, lane MEVLane) AuctionDecorator {
return AuctionDecorator{
auctionkeeper: ak,
txEncoder: txEncoder,
lane: lane,
mempool: mempool,
}
}
@ -55,25 +37,14 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
// Validate the auction bid if one exists.
if bidInfo != nil {
// If comet is re-checking a transaction, we only need to check if the transaction is in the application-side mempool.
if ctx.IsReCheckTx() {
if !ad.mempool.Contains(tx) {
return ctx, fmt.Errorf("transaction not found in application-side mempool")
}
}
// Auction transactions must have a timeout set to a valid block height.
if err := ad.ValidateTimeout(ctx, int64(bidInfo.Timeout)); err != nil {
if err := ValidateTimeout(ctx, int64(bidInfo.Timeout)); err != nil {
return ctx, err
}
// We only need to verify the auction bid relative to the local validator's mempool if the mode
// is checkTx or recheckTx. Otherwise, the ABCI handlers (VerifyVoteExtension, ExtendVoteExtension, etc.)
// will always compare the auction bid to the highest bidding transaction in the mempool leading to
// poor liveness guarantees.
// TODO(nikhil/david): refactor this logic (is this necessary?)
// Only compare the bid to the top bid if necessary.
topBid := sdk.Coin{}
if ctx.IsCheckTx() || ctx.IsReCheckTx() {
if _, ok := nextHeightExecModes[ctx.ExecMode()]; ok {
if topBidTx := ad.lane.GetTopAuctionTx(ctx); topBidTx != nil {
topBidBz, err := ad.txEncoder(topBidTx)
if err != nil {
@ -86,6 +57,8 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
}
// Compare the bytes to see if the current transaction is the highest bidding transaction.
// If it is the same transaction, we do not need to compare the bids as the bid check will
// fail.
if !bytes.Equal(topBidBz, currentTxBz) {
topBidInfo, err := ad.lane.GetAuctionBidInfo(topBidTx)
if err != nil {
@ -104,25 +77,3 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
return next(ctx, tx, simulate)
}
// ValidateTimeout validates that the timeout is greater than or equal to the expected block height
// the bid transaction will be executed in.
func (ad AuctionDecorator) ValidateTimeout(ctx sdk.Context, timeout int64) error {
currentBlockHeight := ctx.BlockHeight()
// If the mode is CheckTx or ReCheckTx, we increment the current block height by one to
// account for the fact that the transaction will be executed in the next block.
if ctx.IsCheckTx() || ctx.IsReCheckTx() {
currentBlockHeight++
}
if timeout < currentBlockHeight {
return fmt.Errorf(
"timeout height cannot be less than the current block height (timeout: %d, current block height: %d)",
timeout,
currentBlockHeight,
)
}
return nil
}

View File

@ -1,311 +0,0 @@
package ante_test
import (
"math/rand"
"testing"
"time"
"cosmossdk.io/log"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/suite"
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
"github.com/skip-mev/block-sdk/block/base"
defaultlane "github.com/skip-mev/block-sdk/lanes/base"
"github.com/skip-mev/block-sdk/lanes/mev"
testutils "github.com/skip-mev/block-sdk/testutils"
"github.com/skip-mev/block-sdk/x/auction/ante"
"github.com/skip-mev/block-sdk/x/auction/keeper"
auctiontypes "github.com/skip-mev/block-sdk/x/auction/types"
)
type AnteTestSuite struct {
suite.Suite
ctx sdk.Context
encodingConfig testutils.EncodingConfig
random *rand.Rand
// auction setup
auctionkeeper keeper.Keeper
bankKeeper *testutils.MockBankKeeper
accountKeeper *testutils.MockAccountKeeper
distrKeeper *testutils.MockDistributionKeeper
stakingKeeper *testutils.MockStakingKeeper
auctionDecorator ante.AuctionDecorator
key *storetypes.KVStoreKey
authorityAccount sdk.AccAddress
// mempool and lane set up
mempool block.Mempool
mevLane *mev.MEVLane
baseLane *defaultlane.DefaultLane
lanes []block.Lane
// Account set up
balance sdk.Coin
}
func TestAnteTestSuite(t *testing.T) {
suite.Run(t, new(AnteTestSuite))
}
func (suite *AnteTestSuite) SetupTest() {
// General config
suite.encodingConfig = testutils.CreateTestEncodingConfig()
suite.random = rand.New(rand.NewSource(time.Now().Unix()))
suite.key = storetypes.NewKVStoreKey(auctiontypes.StoreKey)
testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, storetypes.NewTransientStoreKey("transient_test"))
suite.ctx = testCtx.Ctx.WithIsCheckTx(true)
// Keepers set up
ctrl := gomock.NewController(suite.T())
suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl)
suite.accountKeeper.EXPECT().GetModuleAddress(auctiontypes.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
suite.bankKeeper = testutils.NewMockBankKeeper(ctrl)
suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl)
suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl)
suite.authorityAccount = sdk.AccAddress([]byte("authority"))
suite.auctionkeeper = keeper.NewKeeper(
suite.encodingConfig.Codec,
suite.key,
suite.accountKeeper,
suite.bankKeeper,
suite.distrKeeper,
suite.stakingKeeper,
suite.authorityAccount.String(),
)
err := suite.auctionkeeper.SetParams(suite.ctx, auctiontypes.DefaultParams())
suite.Require().NoError(err)
// Lanes configuration
//
// TOB lane set up
mevConfig := base.LaneConfig{
Logger: suite.ctx.Logger(),
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
SignerExtractor: signer_extraction.NewDefaultAdapter(),
AnteHandler: suite.anteHandler,
MaxBlockSpace: math.LegacyZeroDec(),
}
suite.mevLane = mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()),
)
// Base lane set up
baseConfig := base.LaneConfig{
Logger: suite.ctx.Logger(),
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: suite.anteHandler,
SignerExtractor: signer_extraction.NewDefaultAdapter(),
MaxBlockSpace: math.LegacyZeroDec(),
IgnoreList: []block.Lane{suite.mevLane},
}
suite.baseLane = defaultlane.NewDefaultLane(baseConfig)
// Mempool set up
suite.lanes = []block.Lane{suite.mevLane, suite.baseLane}
suite.mempool = block.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
}
func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {
suite.bankKeeper.EXPECT().GetBalance(ctx, gomock.Any(), suite.balance.Denom).AnyTimes().Return(suite.balance)
next := func(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {
return ctx, nil
}
return suite.auctionDecorator.AnteHandle(ctx, tx, false, next)
}
func (suite *AnteTestSuite) TestAnteHandler() {
var (
// Bid set up
bidder = testutils.RandomAccounts(suite.random, 1)[0]
bid = sdk.NewCoin("stake", math.NewInt(1000))
balance = sdk.NewCoin("stake", math.NewInt(10000))
signers = []testutils.Account{bidder}
// Top bidding auction tx set up
topBidder = testutils.RandomAccounts(suite.random, 1)[0]
topBid = sdk.NewCoin("stake", math.NewInt(100))
insertTopBid = true
timeout = uint64(1000)
// Auction setup
maxBundleSize uint32 = 5
reserveFee = sdk.NewCoin("stake", math.NewInt(100))
minBidIncrement = sdk.NewCoin("stake", math.NewInt(100))
frontRunningProtection = true
)
cases := []struct {
name string
malleate func()
pass bool
}{
{
"empty mempool, valid bid",
func() {
insertTopBid = false
},
true,
},
{
"smaller bid than winning bid, invalid auction tx",
func() {
insertTopBid = true
topBid = sdk.NewCoin("stake", math.NewInt(100000))
},
false,
},
{
"bidder has insufficient balance, invalid auction tx",
func() {
insertTopBid = false
balance = sdk.NewCoin("stake", math.NewInt(10))
},
false,
},
{
"bid is smaller than reserve fee, invalid auction tx",
func() {
balance = sdk.NewCoin("stake", math.NewInt(10000))
bid = sdk.NewCoin("stake", math.NewInt(101))
reserveFee = sdk.NewCoin("stake", math.NewInt(1000))
},
false,
},
{
"valid mev bid tx",
func() {
balance = sdk.NewCoin("stake", math.NewInt(10000))
bid = sdk.NewCoin("stake", math.NewInt(1000))
reserveFee = sdk.NewCoin("stake", math.NewInt(100))
},
true,
},
{
"invalid mev bid tx with no timeout",
func() {
timeout = 0
},
false,
},
{
"mev tx is the top bidding tx",
func() {
timeout = 1000
balance = sdk.NewCoin("stake", math.NewInt(10000))
bid = sdk.NewCoin("stake", math.NewInt(1000))
reserveFee = sdk.NewCoin("stake", math.NewInt(100))
insertTopBid = true
topBidder = bidder
topBid = bid
signers = []testutils.Account{}
},
true,
},
{
"invalid frontrunning mev bid tx",
func() {
randomAccount := testutils.RandomAccounts(suite.random, 2)
bidder := randomAccount[0]
otherUser := randomAccount[1]
insertTopBid = false
signers = []testutils.Account{bidder, otherUser}
},
false,
},
{
"valid frontrunning mev bid tx",
func() {
randomAccount := testutils.RandomAccounts(suite.random, 2)
bidder := randomAccount[0]
otherUser := randomAccount[1]
signers = []testutils.Account{bidder, otherUser}
frontRunningProtection = false
},
true,
},
{
"invalid sandwiching mev bid tx",
func() {
randomAccount := testutils.RandomAccounts(suite.random, 2)
bidder := randomAccount[0]
otherUser := randomAccount[1]
signers = []testutils.Account{bidder, otherUser, bidder}
frontRunningProtection = true
},
false,
},
{
"invalid mev bid tx with many signers",
func() {
signers = testutils.RandomAccounts(suite.random, 10)
frontRunningProtection = true
},
false,
},
}
for _, tc := range cases {
suite.Run(tc.name, func() {
suite.SetupTest()
tc.malleate()
suite.ctx = suite.ctx.WithBlockHeight(1)
// Set the mev params
err := suite.auctionkeeper.SetParams(suite.ctx, auctiontypes.Params{
MaxBundleSize: maxBundleSize,
ReserveFee: reserveFee,
MinBidIncrement: minBidIncrement,
FrontRunningProtection: frontRunningProtection,
})
suite.Require().NoError(err)
// Insert the top bid into the mempool
if insertTopBid {
topAuctionTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, topBidder, topBid, 0, timeout, []testutils.Account{})
suite.Require().NoError(err)
distribution := suite.mempool.GetTxDistribution()
suite.Require().Equal(0, distribution[mev.LaneName])
suite.Require().Equal(0, distribution[defaultlane.LaneName])
suite.Require().NoError(suite.mempool.Insert(suite.ctx, topAuctionTx))
distribution = suite.mempool.GetTxDistribution()
suite.Require().Equal(1, distribution[mev.LaneName])
suite.Require().Equal(0, distribution[defaultlane.LaneName])
}
// Create the actual mev tx and insert into the mempool
mevTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, timeout, signers)
suite.Require().NoError(err)
// Execute the ante handler
suite.balance = balance
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionkeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mevLane, suite.mempool)
_, err = suite.anteHandler(suite.ctx, mevTx, false)
if tc.pass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}

53
x/auction/ante/utils.go Normal file
View File

@ -0,0 +1,53 @@
package ante
import (
"context"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/block-sdk/x/auction/types"
)
// nextHeightExecModes is the subset of execution modes that need special treatment. In
// particular, these modes are used to signal that the transaction is being executed in the
// next block. This is used to determine if the current transaction should be compared
// against the top bid and what the expected timeout height should be.
var nextHeightExecModes = map[sdk.ExecMode]struct{}{
sdk.ExecModeCheck: {},
sdk.ExecModeReCheck: {},
sdk.ExecModeSimulate: {},
}
// MEVLane is an interface that defines the methods required to interact with the MEV
// lane.
type MEVLane interface {
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
GetTopAuctionTx(ctx context.Context) sdk.Tx
}
// AuctionKeeper is an interface that defines the methods required to interact with the
// auction keeper.
type AuctionKeeper interface {
ValidateBidInfo(ctx sdk.Context, highestBid sdk.Coin, bidInfo *types.BidInfo) error
}
// ValidateTimeout validates that the timeout is greater than or equal to the expected block height
// the bid transaction will be executed in.
func ValidateTimeout(ctx sdk.Context, timeout int64) error {
// Every transaction must have a timeout height greater than or equal to the height at which
// the bid transaction will be executed.
height := ctx.BlockHeight()
if _, ok := nextHeightExecModes[ctx.ExecMode()]; ok {
height++
}
if height > timeout {
return fmt.Errorf(
"timeout height cannot be less than the current block height (timeout: %d, current block height: %d)",
timeout,
height,
)
}
return nil
}

View File

@ -86,10 +86,49 @@ func (k Keeper) ValidateAuctionBid(ctx sdk.Context, bidder sdk.AccAddress, bid,
}
}
// ensure the bidder has enough funds to cover all the inclusion fees
balances := k.bankKeeper.GetBalance(ctx, bidder, bid.Denom)
if !balances.IsGTE(bid) {
return fmt.Errorf("insufficient funds to bid %s with balance %s", bid, balances)
// Extract the bid from the bidder.
return k.ExtractBid(ctx, bidder, bid)
}
// ExtractBid extracts the bid amount from the transaction.
func (k Keeper) ExtractBid(ctx sdk.Context, bidder sdk.AccAddress, bid sdk.Coin) error {
params, err := k.GetParams(ctx)
if err != nil {
return err
}
escrowAddress := sdk.AccAddress(params.EscrowAccountAddress)
var proposerReward sdk.Coins
if params.ProposerFee.IsZero() {
// send the entire bid to the escrow account when no proposer fee is set
if err := k.bankKeeper.SendCoins(ctx, bidder, escrowAddress, sdk.NewCoins(bid)); err != nil {
return err
}
} else {
rewardsAddress, err := k.rewardsAddressProvider.GetRewardsAddress(ctx)
if err != nil {
// In the case where the rewards address provider returns an error, the
// escrow account will receive the entire bid.
rewardsAddress = escrowAddress
}
// determine the amount of the bid that goes to the (previous) proposer
bid := sdk.NewDecCoinsFromCoins(bid)
proposerReward, _ = bid.MulDecTruncate(params.ProposerFee).TruncateDecimal()
if err := k.bankKeeper.SendCoins(ctx, bidder, rewardsAddress, proposerReward); err != nil {
return err
}
// Determine the amount of the remaining bid that goes to the escrow account.
// If a decimal remainder exists, it'll stay with the bidding account.
escrowTotal := bid.Sub(sdk.NewDecCoinsFromCoins(proposerReward...))
escrowReward, _ := escrowTotal.TruncateDecimal()
if err := k.bankKeeper.SendCoins(ctx, bidder, escrowAddress, escrowReward); err != nil {
return err
}
}
return nil

View File

@ -1,6 +1,7 @@
package keeper_test
import (
"fmt"
"math/rand"
"time"
@ -10,215 +11,114 @@ import (
testutils "github.com/skip-mev/block-sdk/testutils"
"github.com/skip-mev/block-sdk/x/auction/keeper"
"github.com/skip-mev/block-sdk/x/auction/types"
"github.com/skip-mev/block-sdk/x/auction/types/mocks"
mock "github.com/stretchr/testify/mock"
)
func (suite *KeeperTestSuite) TestValidateBidInfo() {
var (
// Tx building variables
accounts = []testutils.Account{} // tracks the order of signers in the bundle
balance = sdk.NewCoin("stake", math.NewInt(10000))
bid = sdk.NewCoin("stake", math.NewInt(1000))
func (s *KeeperTestSuite) TestValidateAuctionBid() {
rng := rand.New(rand.NewSource(time.Now().Unix()))
bidder := testutils.RandomAccounts(rng, 1)[0].Address
bankSendErr := fmt.Errorf("bank send error")
// Auction params
maxBundleSize uint32 = 10
reserveFee = sdk.NewCoin("stake", math.NewInt(1000))
minBidIncrement = sdk.NewCoin("stake", math.NewInt(1000))
escrowAddress = sdk.AccAddress([]byte("escrow"))
frontRunningProtection = true
// mempool variables
highestBid = sdk.Coin{}
)
rnd := rand.New(rand.NewSource(time.Now().Unix()))
bidder := testutils.RandomAccounts(rnd, 1)[0]
cases := []struct {
name string
malleate func()
pass bool
}{
{
"insufficient bid amount",
func() {
bid = sdk.Coin{}
},
false,
},
{
"insufficient balance",
func() {
bid = sdk.NewCoin("stake", math.NewInt(1000))
balance = sdk.NewCoin("stake", math.NewInt(100))
},
false,
},
{
"too many transactions in the bundle",
func() {
// reset the balance and bid to their original values
bid = sdk.NewCoin("stake", math.NewInt(1000))
balance = sdk.NewCoin("stake", math.NewInt(10000))
accounts = testutils.RandomAccounts(rnd, int(maxBundleSize+1))
},
false,
},
{
"frontrunning bundle",
func() {
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
accounts = []testutils.Account{bidder, randomAccount}
},
false,
},
{
"sandwiching bundle",
func() {
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
accounts = []testutils.Account{bidder, randomAccount, bidder}
},
false,
},
{
"valid bundle",
func() {
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
accounts = []testutils.Account{randomAccount, randomAccount, bidder, bidder, bidder}
},
true,
},
{
"valid bundle with only bidder txs",
func() {
accounts = []testutils.Account{bidder, bidder, bidder, bidder}
},
true,
},
{
"valid bundle with only random txs from single same user",
func() {
randomAccount := testutils.RandomAccounts(rnd, 1)[0]
accounts = []testutils.Account{randomAccount, randomAccount, randomAccount, randomAccount}
},
true,
},
{
"invalid bundle with random accounts",
func() {
accounts = testutils.RandomAccounts(rnd, 2)
},
false,
},
{
"disabled front-running protection",
func() {
accounts = testutils.RandomAccounts(rnd, 10)
frontRunningProtection = false
},
true,
},
{
"invalid bundle that does not outbid the highest bid",
func() {
accounts = []testutils.Account{bidder, bidder, bidder}
highestBid = sdk.NewCoin("stake", math.NewInt(500))
bid = sdk.NewCoin("stake", math.NewInt(500))
},
false,
},
{
"valid bundle that outbids the highest bid",
func() {
highestBid = sdk.NewCoin("stake", math.NewInt(500))
bid = sdk.NewCoin("stake", math.NewInt(1500))
},
true,
},
{
"attempting to bid with a different denom",
func() {
highestBid = sdk.NewCoin("stake", math.NewInt(500))
bid = sdk.NewCoin("stake2", math.NewInt(1500))
},
false,
},
{
"min bid increment is different from bid denom", // THIS SHOULD NEVER HAPPEN
func() {
highestBid = sdk.NewCoin("stake", math.NewInt(500))
bid = sdk.NewCoin("stake", math.NewInt(1500))
minBidIncrement = sdk.NewCoin("stake2", math.NewInt(1000))
},
false,
},
params := types.Params{
ReserveFee: sdk.NewCoin("stake", math.NewInt(1000)),
EscrowAccountAddress: sdk.AccAddress([]byte("escrow")),
MinBidIncrement: sdk.NewCoin("stake", math.NewInt(1000)),
ProposerFee: math.LegacyZeroDec(),
}
s.Require().NoError(s.auctionkeeper.SetParams(s.ctx, params))
for _, tc := range cases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
s.Run("nil bid", func() {
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, sdk.Coin{}, highestBid))
})
tc.malleate()
s.Run("reserve fee and bid denom mismatch", func() {
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
bid := sdk.NewCoin("stake2", math.NewInt(1000))
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid))
})
// Set up the new auction keeper with mocks customized for this test case
suite.bankKeeper.EXPECT().GetBalance(suite.ctx, bidder.Address, minBidIncrement.Denom).Return(balance).AnyTimes()
suite.bankKeeper.EXPECT().SendCoins(suite.ctx, bidder.Address, escrowAddress, reserveFee).Return(nil).AnyTimes()
s.Run("bid less than reserve fee", func() {
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
bid := sdk.NewCoin("stake", math.NewInt(500))
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid))
})
suite.auctionkeeper = keeper.NewKeeper(
suite.encCfg.Codec,
suite.key,
suite.accountKeeper,
suite.bankKeeper,
suite.distrKeeper,
suite.stakingKeeper,
suite.authorityAccount.String(),
)
params := types.Params{
MaxBundleSize: maxBundleSize,
ReserveFee: reserveFee,
EscrowAccountAddress: escrowAddress,
FrontRunningProtection: frontRunningProtection,
MinBidIncrement: minBidIncrement,
}
suite.auctionkeeper.SetParams(suite.ctx, params)
s.Run("bid less than highest bid + min bid increment", func() {
highestBid := sdk.NewCoin("stake", math.NewInt(1000))
bid := sdk.NewCoin("stake", math.NewInt(1500))
s.Require().Error(s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid))
})
// Create the bundle of transactions ordered by accounts
bundle := make([][]byte, 0)
for _, acc := range accounts {
tx, err := testutils.CreateRandomTx(suite.encCfg.TxConfig, acc, 0, 1, 100, 0)
suite.Require().NoError(err)
s.Run("valid bid", func() {
highestBid := sdk.Coin{}
bid := sdk.NewCoin("stake", math.NewInt(1500))
txBz, err := suite.encCfg.TxConfig.TxEncoder()(tx)
suite.Require().NoError(err)
bundle = append(bundle, txBz)
}
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(bid)).Return(nil)
signers := make([]map[string]struct{}, len(accounts))
for index, acc := range accounts {
txSigners := map[string]struct{}{
acc.Address.String(): {},
}
err := s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid)
s.Require().NoError(err)
})
signers[index] = txSigners
}
s.Run("insufficient funds", func() {
highestBid := sdk.Coin{}
bid := sdk.NewCoin("stake", math.NewInt(1500))
bidInfo := &types.BidInfo{
Bidder: bidder.Address,
Bid: bid,
Transactions: bundle,
Signers: signers,
}
s.bankKeeper = mocks.NewBankKeeper(s.T())
s.auctionkeeper = keeper.NewKeeper(
s.encCfg.Codec,
s.key,
s.accountKeeper,
s.bankKeeper,
s.distrKeeper,
s.stakingKeeper,
s.authorityAccount.String(),
)
err := suite.auctionkeeper.ValidateBidInfo(suite.ctx, highestBid, bidInfo)
if tc.pass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(bid)).Return(bankSendErr)
err := s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid)
s.Require().Error(err)
})
s.Run("valid bid with proposer split", func() {
highestBid := sdk.Coin{}
bid := sdk.NewCoin("stake", math.NewInt(1000))
s.bankKeeper = mocks.NewBankKeeper(s.T())
rewardsProvider := mocks.NewRewardsAddressProvider(s.T())
rewardsAddr := sdk.AccAddress([]byte("rewards"))
rewardsProvider.On("GetRewardsAddress", mock.Anything).Return(rewardsAddr, nil)
s.auctionkeeper = keeper.NewKeeperWithRewardsAddressProvider(
s.encCfg.Codec,
s.key,
s.accountKeeper,
s.bankKeeper,
rewardsProvider,
s.authorityAccount.String(),
)
params := types.Params{
ProposerFee: math.LegacyMustNewDecFromStr("0.1"),
ReserveFee: sdk.NewCoin("stake", math.NewInt(1000)),
EscrowAccountAddress: sdk.AccAddress([]byte("escrow")),
MinBidIncrement: sdk.NewCoin("stake", math.NewInt(1000)),
}
s.Require().NoError(s.auctionkeeper.SetParams(s.ctx, params))
proposalSplit := sdk.NewCoin("stake", math.NewInt(100))
escrowSplit := sdk.NewCoin("stake", math.NewInt(900))
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(proposalSplit)).Return(nil)
s.bankKeeper.On("SendCoins", mock.Anything, mock.Anything, mock.Anything, sdk.NewCoins(escrowSplit)).Return(nil)
err := s.auctionkeeper.ValidateAuctionBid(s.ctx, bidder, bid, highestBid)
s.Require().NoError(err)
})
}
func (suite *KeeperTestSuite) TestValidateBundle() {
func (s *KeeperTestSuite) TestValidateBundle() {
var accounts []testutils.Account // tracks the order of signers in the bundle
rng := rand.New(rand.NewSource(time.Now().Unix()))
@ -301,8 +201,8 @@ func (suite *KeeperTestSuite) TestValidateBundle() {
}
for _, tc := range cases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
s.Run(tc.name, func() {
s.SetupTest() // reset
// Malleate the test case
tc.malleate()
@ -317,11 +217,11 @@ func (suite *KeeperTestSuite) TestValidateBundle() {
}
// Validate the bundle
err := suite.auctionkeeper.ValidateAuctionBundle(bidder.Address, signers)
err := s.auctionkeeper.ValidateAuctionBundle(bidder.Address, signers)
if tc.pass {
suite.Require().NoError(err)
s.Require().NoError(err)
} else {
suite.Require().Error(err)
s.Require().Error(err)
}
})
}

View File

@ -6,11 +6,11 @@ import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/golang/mock/gomock"
testutils "github.com/skip-mev/block-sdk/testutils"
"github.com/skip-mev/block-sdk/x/auction/keeper"
"github.com/skip-mev/block-sdk/x/auction/types"
"github.com/skip-mev/block-sdk/x/auction/types/mocks"
"github.com/stretchr/testify/suite"
)
@ -19,10 +19,10 @@ type KeeperTestSuite struct {
suite.Suite
auctionkeeper keeper.Keeper
bankKeeper *testutils.MockBankKeeper
accountKeeper *testutils.MockAccountKeeper
distrKeeper *testutils.MockDistributionKeeper
stakingKeeper *testutils.MockStakingKeeper
bankKeeper *mocks.BankKeeper
accountKeeper *mocks.AccountKeeper
distrKeeper *mocks.DistributionKeeper
stakingKeeper *mocks.StakingKeeper
encCfg testutils.EncodingConfig
ctx sdk.Context
msgServer types.MsgServer
@ -34,33 +34,31 @@ func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
func (suite *KeeperTestSuite) SetupTest() {
suite.encCfg = testutils.CreateTestEncodingConfig()
suite.key = storetypes.NewKVStoreKey(types.StoreKey)
testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, storetypes.NewTransientStoreKey("transient_test"))
suite.ctx = testCtx.Ctx
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
ctrl := gomock.NewController(suite.T())
s.accountKeeper = mocks.NewAccountKeeper(s.T())
s.accountKeeper.On("GetModuleAddress", types.ModuleName).Return(sdk.AccAddress{}).Maybe()
suite.accountKeeper = testutils.NewMockAccountKeeper(ctrl)
suite.accountKeeper.EXPECT().GetModuleAddress(types.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
suite.bankKeeper = testutils.NewMockBankKeeper(ctrl)
suite.distrKeeper = testutils.NewMockDistributionKeeper(ctrl)
suite.stakingKeeper = testutils.NewMockStakingKeeper(ctrl)
suite.authorityAccount = sdk.AccAddress([]byte("authority"))
suite.auctionkeeper = keeper.NewKeeper(
suite.encCfg.Codec,
suite.key,
suite.accountKeeper,
suite.bankKeeper,
suite.distrKeeper,
suite.stakingKeeper,
suite.authorityAccount.String(),
s.bankKeeper = mocks.NewBankKeeper(s.T())
s.distrKeeper = mocks.NewDistributionKeeper(s.T())
s.stakingKeeper = mocks.NewStakingKeeper(s.T())
s.authorityAccount = sdk.AccAddress([]byte("authority"))
s.auctionkeeper = keeper.NewKeeper(
s.encCfg.Codec,
s.key,
s.accountKeeper,
s.bankKeeper,
s.distrKeeper,
s.stakingKeeper,
s.authorityAccount.String(),
)
err := suite.auctionkeeper.SetParams(suite.ctx, types.DefaultParams())
suite.Require().NoError(err)
err := s.auctionkeeper.SetParams(s.ctx, types.DefaultParams())
s.Require().NoError(err)
suite.msgServer = keeper.NewMsgServerImpl(suite.auctionkeeper)
s.msgServer = keeper.NewMsgServerImpl(s.auctionkeeper)
}

View File

@ -24,59 +24,11 @@ func NewMsgServerImpl(keeper Keeper) *MsgServer {
return &MsgServer{Keeper: keeper}
}
// AuctionBid is a no-op function that emits an event for the auction bid. The bid is extracted
// in the antehandler.
func (m MsgServer) AuctionBid(goCtx context.Context, msg *types.MsgAuctionBid) (*types.MsgAuctionBidResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
// This should never return an error because the address was validated when
// the message was ingressed.
bidder, err := sdk.AccAddressFromBech32(msg.Bidder)
if err != nil {
return nil, err
}
params, err := m.GetParams(ctx)
if err != nil {
return nil, err
}
if uint32(len(msg.Transactions)) > params.MaxBundleSize {
return nil, fmt.Errorf("the number of transactions in the bid is greater than the maximum allowed; expected <= %d, got %d", params.MaxBundleSize, len(msg.Transactions))
}
escrowAddress := params.EscrowAccountAddress
var proposerReward sdk.Coins
if params.ProposerFee.IsZero() {
// send the entire bid to the escrow account when no proposer fee is set
if err := m.bankKeeper.SendCoins(ctx, bidder, escrowAddress, sdk.NewCoins(msg.Bid)); err != nil {
return nil, err
}
} else {
rewardsAddress, err := m.rewardsAddressProvider.GetRewardsAddress(ctx)
if err != nil {
// In the case where the rewards address provider returns an error, the
// escrow account will receive the entire bid.
rewardsAddress = escrowAddress
}
// determine the amount of the bid that goes to the (previous) proposer
bid := sdk.NewDecCoinsFromCoins(msg.Bid)
proposerReward, _ = bid.MulDecTruncate(params.ProposerFee).TruncateDecimal()
if err := m.bankKeeper.SendCoins(ctx, bidder, rewardsAddress, proposerReward); err != nil {
return nil, err
}
// Determine the amount of the remaining bid that goes to the escrow account.
// If a decimal remainder exists, it'll stay with the bidding account.
escrowTotal := bid.Sub(sdk.NewDecCoinsFromCoins(proposerReward...))
escrowReward, _ := escrowTotal.TruncateDecimal()
if err := m.bankKeeper.SendCoins(ctx, bidder, escrowAddress, escrowReward); err != nil {
return nil, err
}
}
bundledTxHashes := make([]string, len(msg.Transactions))
for i, refTxRaw := range msg.Transactions {
hash := sha256.Sum256(refTxRaw)
@ -88,7 +40,6 @@ func (m MsgServer) AuctionBid(goCtx context.Context, msg *types.MsgAuctionBid) (
types.EventTypeAuctionBid,
sdk.NewAttribute(types.EventAttrBidder, msg.Bidder),
sdk.NewAttribute(types.EventAttrBid, msg.Bid.String()),
sdk.NewAttribute(types.EventAttrProposerReward, proposerReward.String()),
sdk.NewAttribute(types.EventAttrBundledTxs, strings.Join(bundledTxHashes, ",")),
),
)

View File

@ -6,126 +6,12 @@ import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
testutils "github.com/skip-mev/block-sdk/testutils"
"github.com/skip-mev/block-sdk/x/auction/types"
)
func (suite *KeeperTestSuite) TestMsgAuctionBid() {
rng := rand.New(rand.NewSource(time.Now().Unix()))
accounts := testutils.RandomAccounts(rng, 4)
bidder := accounts[0]
escrow := accounts[1]
proposerCons := accounts[2]
proposerOperator := accounts[3]
proposer := stakingtypes.Validator{
OperatorAddress: sdk.ValAddress(proposerOperator.Address).String(),
}
testCases := []struct {
name string
msg *types.MsgAuctionBid
malleate func()
expectErr bool
}{
{
name: "invalid bidder address",
msg: &types.MsgAuctionBid{
Bidder: "stake",
},
malleate: func() {},
expectErr: true,
},
{
name: "too many bundled transactions",
msg: &types.MsgAuctionBid{
Bidder: bidder.Address.String(),
Transactions: [][]byte{{0xFF}, {0xFF}, {0xFF}},
},
malleate: func() {
params := types.DefaultParams()
params.MaxBundleSize = 2
suite.auctionkeeper.SetParams(suite.ctx, params)
},
expectErr: true,
},
{
name: "valid bundle with no proposer fee",
msg: &types.MsgAuctionBid{
Bidder: bidder.Address.String(),
Bid: sdk.NewInt64Coin("stake", 1024),
Transactions: [][]byte{{0xFF}, {0xFF}},
},
malleate: func() {
params := types.DefaultParams()
params.ProposerFee = math.LegacyZeroDec()
params.EscrowAccountAddress = escrow.Address
suite.auctionkeeper.SetParams(suite.ctx, params)
suite.bankKeeper.EXPECT().
SendCoins(
suite.ctx,
bidder.Address,
escrow.Address,
sdk.NewCoins(sdk.NewInt64Coin("stake", 1024)),
).
Return(nil).
AnyTimes()
},
expectErr: false,
},
{
name: "valid bundle with proposer fee",
msg: &types.MsgAuctionBid{
Bidder: bidder.Address.String(),
Bid: sdk.NewInt64Coin("stake", 3416),
Transactions: [][]byte{{0xFF}, {0xFF}},
},
malleate: func() {
params := types.DefaultParams()
params.ProposerFee = math.LegacyMustNewDecFromStr("0.30")
params.EscrowAccountAddress = escrow.Address
suite.auctionkeeper.SetParams(suite.ctx, params)
suite.distrKeeper.EXPECT().
GetPreviousProposerConsAddr(suite.ctx).
Return(proposerCons.ConsKey.PubKey().Address().Bytes(), nil)
suite.stakingKeeper.EXPECT().
GetValidatorByConsAddr(suite.ctx, sdk.ConsAddress(proposerCons.ConsKey.PubKey().Address().Bytes())).
Return(proposer, nil).
AnyTimes()
suite.bankKeeper.EXPECT().
SendCoins(suite.ctx, bidder.Address, proposerOperator.Address, sdk.NewCoins(sdk.NewInt64Coin("stake", 1024))).
Return(nil).AnyTimes()
suite.bankKeeper.EXPECT().
SendCoins(suite.ctx, bidder.Address, escrow.Address, sdk.NewCoins(sdk.NewInt64Coin("stake", 2392))).
Return(nil).AnyTimes()
},
expectErr: false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
_, err := suite.msgServer.AuctionBid(suite.ctx, tc.msg)
if tc.expectErr {
suite.Require().Error(err)
} else {
suite.Require().NoError(err)
}
})
}
}
func (suite *KeeperTestSuite) TestMsgUpdateParams() {
func (s *KeeperTestSuite) TestMsgUpdateParams() {
rng := rand.New(rand.NewSource(time.Now().Unix()))
account := testutils.RandomAccounts(rng, 1)[0]
@ -139,7 +25,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
{
name: "invalid proposer fee",
msg: &types.MsgUpdateParams{
Authority: suite.authorityAccount.String(),
Authority: s.authorityAccount.String(),
Params: types.Params{
ProposerFee: math.LegacyMustNewDecFromStr("1.1"),
},
@ -150,7 +36,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
{
name: "invalid auction fees",
msg: &types.MsgUpdateParams{
Authority: suite.authorityAccount.String(),
Authority: s.authorityAccount.String(),
Params: types.Params{
ProposerFee: math.LegacyMustNewDecFromStr("0.1"),
},
@ -165,7 +51,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
Params: types.Params{
ProposerFee: math.LegacyMustNewDecFromStr("0.1"),
MaxBundleSize: 2,
EscrowAccountAddress: suite.authorityAccount,
EscrowAccountAddress: s.authorityAccount,
MinBidIncrement: sdk.NewInt64Coin("stake", 100),
ReserveFee: sdk.NewInt64Coin("stake", 100),
},
@ -176,16 +62,16 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
s.Run(tc.name, func() {
if !tc.passBasic {
suite.Require().Error(tc.msg.ValidateBasic())
s.Require().Error(tc.msg.ValidateBasic())
}
_, err := suite.msgServer.UpdateParams(suite.ctx, tc.msg)
_, err := s.msgServer.UpdateParams(s.ctx, tc.msg)
if tc.pass {
suite.Require().NoError(err)
s.Require().NoError(err)
} else {
suite.Require().Error(err)
s.Require().Error(err)
}
})
}

View File

@ -7,12 +7,16 @@ import (
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// BankKeeper defines the expected API contract for the x/auth module.
// BankKeeper defines the expected API contract for the x/account module.
//
//go:generate mockery --name AccountKeeper --output ./mocks --outpkg mocks --case underscore
type AccountKeeper interface {
GetModuleAddress(moduleName string) sdk.AccAddress
}
// BankKeeper defines the expected API contract for the x/bank module.
//
//go:generate mockery --name BankKeeper --output ./mocks --outpkg mocks --case underscore
type BankKeeper interface {
SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error
GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin
@ -20,16 +24,22 @@ type BankKeeper interface {
// DistributionKeeper defines the expected API contract for the x/distribution
// module.
//
//go:generate mockery --name DistributionKeeper --output ./mocks --outpkg mocks --case underscore
type DistributionKeeper interface {
GetPreviousProposerConsAddr(ctx context.Context) (sdk.ConsAddress, error)
}
// StakingKeeper defines the expected API contract for the x/staking module.
//
//go:generate mockery --name StakingKeeper --output ./mocks --outpkg mocks --case underscore
type StakingKeeper interface {
GetValidatorByConsAddr(context.Context, sdk.ConsAddress) (stakingtypes.Validator, error)
}
// RewardsAddressProvider is an interface that provides an address where proposer/subset of auction profits are sent.
//
//go:generate mockery --name RewardsAddressProvider --output ./mocks --outpkg mocks --case underscore
type RewardsAddressProvider interface {
GetRewardsAddress(context sdk.Context) (sdk.AccAddress, error)
}

View File

@ -0,0 +1,43 @@
// Code generated by mockery v2.30.1. DO NOT EDIT.
package mocks
import (
types "github.com/cosmos/cosmos-sdk/types"
mock "github.com/stretchr/testify/mock"
)
// AccountKeeper is an autogenerated mock type for the AccountKeeper type
type AccountKeeper struct {
mock.Mock
}
// GetModuleAddress provides a mock function with given fields: moduleName
func (_m *AccountKeeper) GetModuleAddress(moduleName string) types.AccAddress {
ret := _m.Called(moduleName)
var r0 types.AccAddress
if rf, ok := ret.Get(0).(func(string) types.AccAddress); ok {
r0 = rf(moduleName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.AccAddress)
}
}
return r0
}
// NewAccountKeeper creates a new instance of AccountKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewAccountKeeper(t interface {
mock.TestingT
Cleanup(func())
}) *AccountKeeper {
mock := &AccountKeeper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,57 @@
// Code generated by mockery v2.30.1. DO NOT EDIT.
package mocks
import (
context "context"
types "github.com/cosmos/cosmos-sdk/types"
mock "github.com/stretchr/testify/mock"
)
// BankKeeper is an autogenerated mock type for the BankKeeper type
type BankKeeper struct {
mock.Mock
}
// GetBalance provides a mock function with given fields: ctx, addr, denom
func (_m *BankKeeper) GetBalance(ctx context.Context, addr types.AccAddress, denom string) types.Coin {
ret := _m.Called(ctx, addr, denom)
var r0 types.Coin
if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, string) types.Coin); ok {
r0 = rf(ctx, addr, denom)
} else {
r0 = ret.Get(0).(types.Coin)
}
return r0
}
// SendCoins provides a mock function with given fields: ctx, fromAddr, toAddr, amt
func (_m *BankKeeper) SendCoins(ctx context.Context, fromAddr types.AccAddress, toAddr types.AccAddress, amt types.Coins) error {
ret := _m.Called(ctx, fromAddr, toAddr, amt)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, types.AccAddress, types.AccAddress, types.Coins) error); ok {
r0 = rf(ctx, fromAddr, toAddr, amt)
} else {
r0 = ret.Error(0)
}
return r0
}
// NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewBankKeeper(t interface {
mock.TestingT
Cleanup(func())
}) *BankKeeper {
mock := &BankKeeper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,55 @@
// Code generated by mockery v2.30.1. DO NOT EDIT.
package mocks
import (
context "context"
types "github.com/cosmos/cosmos-sdk/types"
mock "github.com/stretchr/testify/mock"
)
// DistributionKeeper is an autogenerated mock type for the DistributionKeeper type
type DistributionKeeper struct {
mock.Mock
}
// GetPreviousProposerConsAddr provides a mock function with given fields: ctx
func (_m *DistributionKeeper) GetPreviousProposerConsAddr(ctx context.Context) (types.ConsAddress, error) {
ret := _m.Called(ctx)
var r0 types.ConsAddress
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) (types.ConsAddress, error)); ok {
return rf(ctx)
}
if rf, ok := ret.Get(0).(func(context.Context) types.ConsAddress); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.ConsAddress)
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewDistributionKeeper creates a new instance of DistributionKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewDistributionKeeper(t interface {
mock.TestingT
Cleanup(func())
}) *DistributionKeeper {
mock := &DistributionKeeper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,53 @@
// Code generated by mockery v2.30.1. DO NOT EDIT.
package mocks
import (
types "github.com/cosmos/cosmos-sdk/types"
mock "github.com/stretchr/testify/mock"
)
// RewardsAddressProvider is an autogenerated mock type for the RewardsAddressProvider type
type RewardsAddressProvider struct {
mock.Mock
}
// GetRewardsAddress provides a mock function with given fields: context
func (_m *RewardsAddressProvider) GetRewardsAddress(context types.Context) (types.AccAddress, error) {
ret := _m.Called(context)
var r0 types.AccAddress
var r1 error
if rf, ok := ret.Get(0).(func(types.Context) (types.AccAddress, error)); ok {
return rf(context)
}
if rf, ok := ret.Get(0).(func(types.Context) types.AccAddress); ok {
r0 = rf(context)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.AccAddress)
}
}
if rf, ok := ret.Get(1).(func(types.Context) error); ok {
r1 = rf(context)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewRewardsAddressProvider creates a new instance of RewardsAddressProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewRewardsAddressProvider(t interface {
mock.TestingT
Cleanup(func())
}) *RewardsAddressProvider {
mock := &RewardsAddressProvider{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,55 @@
// Code generated by mockery v2.30.1. DO NOT EDIT.
package mocks
import (
context "context"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
mock "github.com/stretchr/testify/mock"
types "github.com/cosmos/cosmos-sdk/types"
)
// StakingKeeper is an autogenerated mock type for the StakingKeeper type
type StakingKeeper struct {
mock.Mock
}
// GetValidatorByConsAddr provides a mock function with given fields: _a0, _a1
func (_m *StakingKeeper) GetValidatorByConsAddr(_a0 context.Context, _a1 types.ConsAddress) (stakingtypes.Validator, error) {
ret := _m.Called(_a0, _a1)
var r0 stakingtypes.Validator
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, types.ConsAddress) (stakingtypes.Validator, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, types.ConsAddress) stakingtypes.Validator); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(stakingtypes.Validator)
}
if rf, ok := ret.Get(1).(func(context.Context, types.ConsAddress) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewStakingKeeper creates a new instance of StakingKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewStakingKeeper(t interface {
mock.TestingT
Cleanup(func())
}) *StakingKeeper {
mock := &StakingKeeper{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}