[ENG-540]: Process Proposal + Process Proposal Testing (#31)
This commit is contained in:
parent
c8115a0d81
commit
717238ffd7
34
abci/abci.go
34
abci/abci.go
@ -1,6 +1,7 @@
|
||||
package abci
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
@ -175,7 +176,38 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
|
||||
// block proposal verification.
|
||||
func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
|
||||
return func(ctx sdk.Context, req abci.RequestProcessProposal) abci.ResponseProcessProposal {
|
||||
panic("not implemented")
|
||||
for index, txBz := range req.Txs {
|
||||
tx, err := h.txVerifier.ProcessProposalVerifyTx(txBz)
|
||||
if err != nil {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
|
||||
msgAuctionBid, err := mempool.GetMsgAuctionBidFromTx(tx)
|
||||
if err != nil {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
|
||||
if msgAuctionBid != nil {
|
||||
// Only the first transaction can be an auction bid tx
|
||||
if index != 0 {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
|
||||
// The order of transactions in the block proposal must follow the order of transactions in the bid.
|
||||
if len(req.Txs) < len(msgAuctionBid.Transactions)+1 {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
|
||||
for i, refTxRaw := range msgAuctionBid.Transactions {
|
||||
if !bytes.Equal(refTxRaw, req.Txs[i+1]) {
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package abci_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
@ -15,7 +16,7 @@ import (
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
"github.com/skip-mev/pob/x/auction/ante"
|
||||
"github.com/skip-mev/pob/x/auction/keeper"
|
||||
"github.com/skip-mev/pob/x/auction/types"
|
||||
auctiontypes "github.com/skip-mev/pob/x/auction/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
@ -50,7 +51,7 @@ type ABCITestSuite struct {
|
||||
nonces map[string]uint64
|
||||
}
|
||||
|
||||
func TestPrepareProposalSuite(t *testing.T) {
|
||||
func TestABCISuite(t *testing.T) {
|
||||
suite.Run(t, new(ABCITestSuite))
|
||||
}
|
||||
|
||||
@ -58,7 +59,7 @@ func (suite *ABCITestSuite) SetupTest() {
|
||||
// General config
|
||||
suite.encodingConfig = createTestEncodingConfig()
|
||||
suite.random = rand.New(rand.NewSource(time.Now().Unix()))
|
||||
suite.key = sdk.NewKVStoreKey(types.StoreKey)
|
||||
suite.key = sdk.NewKVStoreKey(auctiontypes.StoreKey)
|
||||
testCtx := testutil.DefaultContextWithDB(suite.T(), suite.key, sdk.NewTransientStoreKey("transient_test"))
|
||||
suite.ctx = testCtx.Ctx
|
||||
|
||||
@ -70,7 +71,7 @@ func (suite *ABCITestSuite) SetupTest() {
|
||||
// Mock keepers set up
|
||||
ctrl := gomock.NewController(suite.T())
|
||||
suite.accountKeeper = NewMockAccountKeeper(ctrl)
|
||||
suite.accountKeeper.EXPECT().GetModuleAddress(types.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
|
||||
suite.accountKeeper.EXPECT().GetModuleAddress(auctiontypes.ModuleName).Return(sdk.AccAddress{}).AnyTimes()
|
||||
suite.bankKeeper = NewMockBankKeeper(ctrl)
|
||||
suite.distrKeeper = NewMockDistributionKeeper(ctrl)
|
||||
suite.stakingKeeper = NewMockStakingKeeper(ctrl)
|
||||
@ -86,9 +87,9 @@ func (suite *ABCITestSuite) SetupTest() {
|
||||
suite.stakingKeeper,
|
||||
suite.authorityAccount.String(),
|
||||
)
|
||||
err := suite.auctionKeeper.SetParams(suite.ctx, types.DefaultParams())
|
||||
err := suite.auctionKeeper.SetParams(suite.ctx, auctiontypes.DefaultParams())
|
||||
suite.Require().NoError(err)
|
||||
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.mempool)
|
||||
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
// Accounts set up
|
||||
suite.accounts = RandomAccounts(suite.random, 1)
|
||||
@ -117,8 +118,18 @@ func (suite *ABCITestSuite) PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error) {
|
||||
return txBz, nil
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) ProcessProposalVerifyTx(_ []byte) (sdk.Tx, error) {
|
||||
return nil, nil
|
||||
func (suite *ABCITestSuite) ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error) {
|
||||
tx, err := suite.encodingConfig.TxConfig.TxDecoder()(txBz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = suite.executeAnteHandler(tx)
|
||||
if err != nil {
|
||||
return tx, err
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) executeAnteHandler(tx sdk.Tx) (sdk.Context, error) {
|
||||
@ -216,6 +227,41 @@ func (suite *ABCITestSuite) createFilledMempool(numNormalTxs, numAuctionTxs, num
|
||||
return totalNumTxs
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) exportMempool(exportRefTxs bool) [][]byte {
|
||||
txs := make([][]byte, 0)
|
||||
seenTxs := make(map[string]bool)
|
||||
|
||||
auctionIterator := suite.mempool.AuctionBidSelect(suite.ctx)
|
||||
for ; auctionIterator != nil; auctionIterator = auctionIterator.Next() {
|
||||
auctionTx := auctionIterator.Tx().(*mempool.WrappedBidTx).Tx
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(auctionTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
txs = append(txs, txBz)
|
||||
|
||||
if exportRefTxs {
|
||||
for _, refRawTx := range auctionTx.GetMsgs()[0].(*auctiontypes.MsgAuctionBid).GetTransactions() {
|
||||
txs = append(txs, refRawTx)
|
||||
seenTxs[string(refRawTx)] = true
|
||||
}
|
||||
}
|
||||
|
||||
seenTxs[string(txBz)] = true
|
||||
}
|
||||
|
||||
iterator := suite.mempool.Select(suite.ctx, nil)
|
||||
for ; iterator != nil; iterator = iterator.Next() {
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(iterator.Tx())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
if !seenTxs[string(txBz)] {
|
||||
txs = append(txs, txBz)
|
||||
}
|
||||
}
|
||||
|
||||
return txs
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
var (
|
||||
// the modified transactions cannot exceed this size
|
||||
@ -436,7 +482,7 @@ func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs)
|
||||
|
||||
// create a new auction
|
||||
params := types.Params{
|
||||
params := auctiontypes.Params{
|
||||
MaxBundleSize: maxBundleSize,
|
||||
ReserveFee: reserveFee,
|
||||
MinBuyInFee: minBuyInFee,
|
||||
@ -444,7 +490,7 @@ func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
suite.auctionKeeper.SetParams(suite.ctx, params)
|
||||
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.mempool)
|
||||
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
|
||||
handler := suite.proposalHandler.PrepareProposalHandler()
|
||||
res := handler(suite.ctx, abcitypes.RequestPrepareProposal{
|
||||
@ -500,6 +546,221 @@ func (suite *ABCITestSuite) TestPrepareProposal() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) TestProcessProposal() {
|
||||
var (
|
||||
// mempool set up
|
||||
numNormalTxs = 100
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 3
|
||||
insertRefTxs = true
|
||||
exportRefTxs = true
|
||||
frontRunningTx sdk.Tx
|
||||
|
||||
// auction set up
|
||||
maxBundleSize uint32 = 10
|
||||
reserveFee = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000)))
|
||||
minBuyInFee = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000)))
|
||||
frontRunningProtection = true
|
||||
)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
isTopBidValid bool
|
||||
response abcitypes.ResponseProcessProposal_ProposalStatus
|
||||
}{
|
||||
{
|
||||
"single normal tx, no auction tx",
|
||||
func() {
|
||||
numNormalTxs = 1
|
||||
numAuctionTxs = 0
|
||||
numBundledTxs = 0
|
||||
},
|
||||
false,
|
||||
abcitypes.ResponseProcessProposal_ACCEPT,
|
||||
},
|
||||
{
|
||||
"single auction tx, no normal txs",
|
||||
func() {
|
||||
numNormalTxs = 0
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 0
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_ACCEPT,
|
||||
},
|
||||
{
|
||||
"single auction tx, single auction tx",
|
||||
func() {
|
||||
numNormalTxs = 1
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 0
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_ACCEPT,
|
||||
},
|
||||
{
|
||||
"single auction tx, single auction tx with ref txs",
|
||||
func() {
|
||||
numNormalTxs = 1
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_ACCEPT,
|
||||
},
|
||||
{
|
||||
"single auction tx, single auction tx with no ref txs",
|
||||
func() {
|
||||
numNormalTxs = 1
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
insertRefTxs = false
|
||||
exportRefTxs = false
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_REJECT,
|
||||
},
|
||||
{
|
||||
"multiple auction txs, single normal tx",
|
||||
func() {
|
||||
numNormalTxs = 1
|
||||
numAuctionTxs = 2
|
||||
numBundledTxs = 4
|
||||
insertRefTxs = true
|
||||
exportRefTxs = true
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_REJECT,
|
||||
},
|
||||
{
|
||||
"single auction txs, multiple normal tx",
|
||||
func() {
|
||||
numNormalTxs = 100
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_ACCEPT,
|
||||
},
|
||||
{
|
||||
"single invalid auction tx, multiple normal tx",
|
||||
func() {
|
||||
numNormalTxs = 100
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
reserveFee = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100000000000000000)))
|
||||
insertRefTxs = true
|
||||
},
|
||||
false,
|
||||
abcitypes.ResponseProcessProposal_REJECT,
|
||||
},
|
||||
{
|
||||
"single valid auction txs but missing ref txs",
|
||||
func() {
|
||||
numNormalTxs = 0
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
reserveFee = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000)))
|
||||
insertRefTxs = false
|
||||
exportRefTxs = false
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_REJECT,
|
||||
},
|
||||
{
|
||||
"single valid auction txs but missing ref txs, with many normal txs",
|
||||
func() {
|
||||
numNormalTxs = 100
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
reserveFee = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000)))
|
||||
insertRefTxs = false
|
||||
exportRefTxs = false
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_REJECT,
|
||||
},
|
||||
{
|
||||
"auction tx with frontrunning",
|
||||
func() {
|
||||
randomAccount := RandomAccounts(suite.random, 1)[0]
|
||||
bidder := suite.accounts[0]
|
||||
bid := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(696969696969)))
|
||||
nonce := suite.nonces[bidder.Address.String()]
|
||||
frontRunningTx, _ = createAuctionTxWithSigners(suite.encodingConfig.TxConfig, suite.accounts[0], bid, nonce+1, []Account{bidder, randomAccount})
|
||||
suite.Require().NotNil(frontRunningTx)
|
||||
|
||||
numNormalTxs = 100
|
||||
numAuctionTxs = 1
|
||||
numBundledTxs = 4
|
||||
insertRefTxs = true
|
||||
exportRefTxs = true
|
||||
},
|
||||
false,
|
||||
abcitypes.ResponseProcessProposal_REJECT,
|
||||
},
|
||||
{
|
||||
"auction tx with frontrunning, but frontrunning protection disabled",
|
||||
func() {
|
||||
randomAccount := RandomAccounts(suite.random, 1)[0]
|
||||
bidder := suite.accounts[0]
|
||||
bid := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(696969696969)))
|
||||
nonce := suite.nonces[bidder.Address.String()]
|
||||
frontRunningTx, _ = createAuctionTxWithSigners(suite.encodingConfig.TxConfig, suite.accounts[0], bid, nonce+1, []Account{bidder, randomAccount})
|
||||
suite.Require().NotNil(frontRunningTx)
|
||||
|
||||
numAuctionTxs = 0
|
||||
frontRunningProtection = false
|
||||
},
|
||||
true,
|
||||
abcitypes.ResponseProcessProposal_ACCEPT,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
tc.malleate()
|
||||
|
||||
suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs)
|
||||
|
||||
if frontRunningTx != nil {
|
||||
suite.Require().NoError(suite.mempool.Insert(suite.ctx, frontRunningTx))
|
||||
}
|
||||
|
||||
// create a new auction
|
||||
params := auctiontypes.Params{
|
||||
MaxBundleSize: maxBundleSize,
|
||||
ReserveFee: reserveFee,
|
||||
MinBuyInFee: minBuyInFee,
|
||||
FrontRunningProtection: frontRunningProtection,
|
||||
MinBidIncrement: suite.minBidIncrement,
|
||||
}
|
||||
suite.auctionKeeper.SetParams(suite.ctx, params)
|
||||
suite.auctionDecorator = ante.NewAuctionDecorator(suite.auctionKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool)
|
||||
suite.Require().Equal(tc.isTopBidValid, suite.isTopBidValid())
|
||||
|
||||
txs := suite.exportMempool(exportRefTxs)
|
||||
|
||||
if frontRunningTx != nil {
|
||||
txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(frontRunningTx)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.Require().True(bytes.Equal(txs[0], txBz))
|
||||
}
|
||||
|
||||
handler := suite.proposalHandler.ProcessProposalHandler()
|
||||
res := handler(suite.ctx, abcitypes.RequestProcessProposal{
|
||||
Txs: txs,
|
||||
})
|
||||
|
||||
// Check if the response is valid
|
||||
suite.Require().Equal(tc.response, res.Status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// isTopBidValid returns true if the top bid is valid. We purposefully insert invalid
|
||||
// auction transactions into the mempool to test the handlers.
|
||||
func (suite *ABCITestSuite) isTopBidValid() bool {
|
||||
|
||||
@ -146,3 +146,45 @@ func createMsgAuctionBid(txCfg client.TxConfig, bidder Account, bid sdk.Coins, n
|
||||
|
||||
return bidMsg, nil
|
||||
}
|
||||
|
||||
func createAuctionTxWithSigners(txCfg client.TxConfig, bidder Account, bid sdk.Coins, nonce uint64, signers []Account) (authsigning.Tx, error) {
|
||||
bidMsg := &auctiontypes.MsgAuctionBid{
|
||||
Bidder: bidder.Address.String(),
|
||||
Bid: bid,
|
||||
Transactions: make([][]byte, len(signers)),
|
||||
}
|
||||
|
||||
for i := 0; i < len(signers); i++ {
|
||||
randomMsg := createRandomMsgs(signers[i].Address, 1)
|
||||
randomTx, err := createTx(txCfg, signers[i], 0, randomMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bz, err := txCfg.TxEncoder()(randomTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bidMsg.Transactions[i] = bz
|
||||
}
|
||||
|
||||
txBuilder := txCfg.NewTxBuilder()
|
||||
if err := txBuilder.SetMsgs(bidMsg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigV2 := signing.SignatureV2{
|
||||
PubKey: bidder.PrivKey.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: txCfg.SignModeHandler().DefaultMode(),
|
||||
Signature: nil,
|
||||
},
|
||||
Sequence: nonce,
|
||||
}
|
||||
if err := txBuilder.SetSignatures(sigV2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txBuilder.GetTx(), nil
|
||||
}
|
||||
|
||||
@ -54,6 +54,10 @@ func GetMsgAuctionBidFromTx(tx sdk.Tx) (*auctiontypes.MsgAuctionBid, error) {
|
||||
|
||||
// UnwrapBidTx attempts to unwrap a WrappedBidTx from an sdk.Tx if one exists.
|
||||
func UnwrapBidTx(tx sdk.Tx) sdk.Tx {
|
||||
if tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
wTx, ok := tx.(*WrappedBidTx)
|
||||
if ok {
|
||||
return wTx.Tx
|
||||
|
||||
@ -3,6 +3,7 @@ package mempool_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
pobcodec "github.com/skip-mev/pob/codec"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
@ -46,3 +47,22 @@ func TestGetMsgAuctionBidFromTx_NoBid(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, msg)
|
||||
}
|
||||
|
||||
func TestGetUnwrappedTx(t *testing.T) {
|
||||
encCfg := pobcodec.CreateEncodingConfig()
|
||||
|
||||
txBuilder := encCfg.TxConfig.NewTxBuilder()
|
||||
txBuilder.SetMsgs(&auctiontypes.MsgAuctionBid{})
|
||||
tx := txBuilder.GetTx()
|
||||
|
||||
bid := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000000)))
|
||||
wrappedTx := mempool.NewWrappedBidTx(tx, bid)
|
||||
unWrappedTx := mempool.UnwrapBidTx(wrappedTx)
|
||||
|
||||
unwrappedBz, err := encCfg.TxConfig.TxEncoder()(unWrappedTx)
|
||||
require.NoError(t, err)
|
||||
|
||||
txBz, err := encCfg.TxConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, txBz, unwrappedBz)
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/skip-mev/pob/mempool"
|
||||
@ -12,13 +14,15 @@ var _ sdk.AnteDecorator = AuctionDecorator{}
|
||||
type AuctionDecorator struct {
|
||||
auctionKeeper keeper.Keeper
|
||||
txDecoder sdk.TxDecoder
|
||||
txEncoder sdk.TxEncoder
|
||||
mempool *mempool.AuctionMempool
|
||||
}
|
||||
|
||||
func NewAuctionDecorator(ak keeper.Keeper, txDecoder sdk.TxDecoder, mempool *mempool.AuctionMempool) AuctionDecorator {
|
||||
func NewAuctionDecorator(ak keeper.Keeper, txDecoder sdk.TxDecoder, txEncoder sdk.TxEncoder, mempool *mempool.AuctionMempool) AuctionDecorator {
|
||||
return AuctionDecorator{
|
||||
auctionKeeper: ak,
|
||||
txDecoder: txDecoder,
|
||||
txEncoder: txEncoder,
|
||||
mempool: mempool,
|
||||
}
|
||||
}
|
||||
@ -48,12 +52,23 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
transactions[i] = decodedTx
|
||||
}
|
||||
|
||||
highestBid, err := ad.GetTopAuctionBid(ctx, tx)
|
||||
topBid := sdk.NewCoins()
|
||||
|
||||
// If the current transaction is the highest bidding transaction, then the highest bid is empty.
|
||||
isTopBidTx, err := ad.IsTopBidTx(ctx, tx)
|
||||
if err != nil {
|
||||
return ctx, errors.Wrap(err, "failed to get highest auction bid")
|
||||
return ctx, errors.Wrap(err, "failed to check if current transaction is highest bidding transaction")
|
||||
}
|
||||
|
||||
if err := ad.auctionKeeper.ValidateAuctionMsg(ctx, bidder, auctionMsg.Bid, highestBid, transactions); err != nil {
|
||||
if !isTopBidTx {
|
||||
// Set the top bid to the highest bidding transaction.
|
||||
topBid, err = ad.GetTopAuctionBid(ctx)
|
||||
if err != nil {
|
||||
return ctx, errors.Wrap(err, "failed to get highest auction bid")
|
||||
}
|
||||
}
|
||||
|
||||
if err := ad.auctionKeeper.ValidateAuctionMsg(ctx, bidder, auctionMsg.Bid, topBid, transactions); err != nil {
|
||||
return ctx, errors.Wrap(err, "failed to validate auction bid")
|
||||
}
|
||||
}
|
||||
@ -61,18 +76,33 @@ func (ad AuctionDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// GetTopAuctionBid returns the highest auction bid if one exists. If the current transaction is the highest
|
||||
// bidding transaction, then an empty coin set is returned.
|
||||
func (ad AuctionDecorator) GetTopAuctionBid(ctx sdk.Context, currTx sdk.Tx) (sdk.Coins, error) {
|
||||
// GetTopAuctionBid returns the highest auction bid if one exists.
|
||||
func (ad AuctionDecorator) GetTopAuctionBid(ctx sdk.Context) (sdk.Coins, error) {
|
||||
auctionTx := ad.mempool.GetTopAuctionTx(ctx)
|
||||
if auctionTx == nil {
|
||||
return sdk.NewCoins(), nil
|
||||
}
|
||||
|
||||
wrappedTx := auctionTx.(*mempool.WrappedBidTx)
|
||||
if wrappedTx.Tx == currTx {
|
||||
return sdk.NewCoins(), nil
|
||||
return auctionTx.(*mempool.WrappedBidTx).GetBid(), nil
|
||||
}
|
||||
|
||||
// IsTopBidTx returns true if the transaction inputted is the highest bidding auction transaction in the mempool.
|
||||
func (ad AuctionDecorator) IsTopBidTx(ctx sdk.Context, tx sdk.Tx) (bool, error) {
|
||||
auctionTx := ad.mempool.GetTopAuctionTx(ctx)
|
||||
if auctionTx == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return wrappedTx.GetBid(), nil
|
||||
topBidTx := mempool.UnwrapBidTx(auctionTx)
|
||||
topBidBz, err := ad.txEncoder(topBidTx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
currentTxBz, err := ad.txEncoder(tx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return bytes.Equal(topBidBz, currentTxBz), nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user