From 7797110514a779e697626404b162ec9390057f51 Mon Sep 17 00:00:00 2001 From: David Terpay <35130517+davidterpay@users.noreply.github.com> Date: Tue, 9 May 2023 16:47:27 -0400 Subject: [PATCH] feat(v1): Vote Extension Auction (#106) --- abci/abci_test.go | 145 ++- abci/auction.go | 231 +++++ abci/auction.pb.go | 395 ++++++++ abci/auction_test.go | 512 +++++++++++ abci/proposals.go | 223 ++--- abci/proposals_test.go | 683 +++++++++----- api/pob/abci/v1/auction.pulsar.go | 1414 +++++++++++++++++++++++++++++ proto/pob/abci/v1/auction.proto | 17 + testutils/utils.go | 23 + 9 files changed, 3244 insertions(+), 399 deletions(-) create mode 100644 abci/auction.go create mode 100644 abci/auction.pb.go create mode 100644 abci/auction_test.go create mode 100644 api/pob/abci/v1/auction.pulsar.go create mode 100644 proto/pob/abci/v1/auction.proto diff --git a/abci/abci_test.go b/abci/abci_test.go index e995ea4..2c15956 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -1,13 +1,11 @@ package abci_test import ( - "crypto/sha256" - "encoding/hex" - "fmt" "math/rand" "testing" "time" + comettypes "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/testutil" @@ -99,7 +97,7 @@ func (suite *ABCITestSuite) SetupTest() { suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool) // Accounts set up - suite.accounts = testutils.RandomAccounts(suite.random, 1) + suite.accounts = testutils.RandomAccounts(suite.random, 10) suite.balances = sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1000000000000000000))) suite.nonces = make(map[string]uint64) for _, acc := range suite.accounts { @@ -125,20 +123,6 @@ func (suite *ABCITestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, simulate boo return ctx, err } - bz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) - if err != nil { - return ctx, err - } - - if !simulate { - hash := sha256.Sum256(bz) - txHash := hex.EncodeToString(hash[:]) - if _, ok := suite.txs[txHash]; ok { - return ctx, fmt.Errorf("tx already in mempool") - } - suite.txs[txHash] = struct{}{} - } - return ctx, nil } @@ -228,28 +212,10 @@ func (suite *ABCITestSuite) createFilledMempool(numNormalTxs, numAuctionTxs, num return totalNumTxs } -func (suite *ABCITestSuite) exportMempool(exportRefTxs bool) [][]byte { +func (suite *ABCITestSuite) exportMempool() [][]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() - txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(auctionTx) - suite.Require().NoError(err) - - txs = append(txs, txBz) - - if exportRefTxs { - for _, refRawTx := range auctionTx.GetMsgs()[0].(*buildertypes.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()) @@ -262,3 +228,108 @@ func (suite *ABCITestSuite) exportMempool(exportRefTxs bool) [][]byte { return txs } + +func (suite *ABCITestSuite) createPrepareProposalRequest(maxBytes int64) comettypes.RequestPrepareProposal { + voteExtensions := make([]comettypes.ExtendedVoteInfo, 0) + + auctionIterator := suite.mempool.AuctionBidSelect(suite.ctx) + for ; auctionIterator != nil; auctionIterator = auctionIterator.Next() { + tx := auctionIterator.Tx() + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) + suite.Require().NoError(err) + + voteExtensions = append(voteExtensions, comettypes.ExtendedVoteInfo{ + VoteExtension: txBz, + }) + } + + return comettypes.RequestPrepareProposal{ + MaxTxBytes: maxBytes, + LocalLastCommit: comettypes.ExtendedCommitInfo{ + Votes: voteExtensions, + }, + } +} + +func (suite *ABCITestSuite) createExtendedCommitInfoFromTxBzs(txs [][]byte) []byte { + voteExtensions := make([]comettypes.ExtendedVoteInfo, 0) + + for _, txBz := range txs { + voteExtensions = append(voteExtensions, comettypes.ExtendedVoteInfo{ + VoteExtension: txBz, + }) + } + + commitInfo := comettypes.ExtendedCommitInfo{ + Votes: voteExtensions, + } + + commitInfoBz, err := commitInfo.Marshal() + suite.Require().NoError(err) + + return commitInfoBz +} + +func (suite *ABCITestSuite) createAuctionInfoFromTxBzs(txs [][]byte, numTxs uint64) []byte { + auctionInfo := abci.AuctionInfo{ + ExtendedCommitInfo: suite.createExtendedCommitInfoFromTxBzs(txs), + NumTxs: numTxs, + MaxTxBytes: int64(len(txs[0])), + } + + auctionInfoBz, err := auctionInfo.Marshal() + suite.Require().NoError(err) + + return auctionInfoBz +} + +func (suite *ABCITestSuite) getAllAuctionTxs() ([]sdk.Tx, [][]byte) { + auctionIterator := suite.mempool.AuctionBidSelect(suite.ctx) + txs := make([]sdk.Tx, 0) + txBzs := make([][]byte, 0) + + for ; auctionIterator != nil; auctionIterator = auctionIterator.Next() { + txs = append(txs, auctionIterator.Tx()) + + bz, err := suite.encodingConfig.TxConfig.TxEncoder()(auctionIterator.Tx()) + suite.Require().NoError(err) + + txBzs = append(txBzs, bz) + } + + return txs, txBzs +} + +func (suite *ABCITestSuite) createExtendedCommitInfoFromTxs(txs []sdk.Tx) comettypes.ExtendedCommitInfo { + voteExtensions := make([][]byte, 0) + for _, tx := range txs { + bz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) + suite.Require().NoError(err) + + voteExtensions = append(voteExtensions, bz) + } + + return suite.createExtendedCommitInfo(voteExtensions) +} + +func (suite *ABCITestSuite) createExtendedVoteInfo(voteExtensions [][]byte) []comettypes.ExtendedVoteInfo { + commitInfo := make([]comettypes.ExtendedVoteInfo, 0) + for _, voteExtension := range voteExtensions { + info := comettypes.ExtendedVoteInfo{ + VoteExtension: voteExtension, + } + + commitInfo = append(commitInfo, info) + } + + return commitInfo +} + +func (suite *ABCITestSuite) createExtendedCommitInfo(voteExtensions [][]byte) comettypes.ExtendedCommitInfo { + commitInfo := comettypes.ExtendedCommitInfo{ + Votes: suite.createExtendedVoteInfo(voteExtensions), + } + + return commitInfo +} diff --git a/abci/auction.go b/abci/auction.go new file mode 100644 index 0000000..5aced5e --- /dev/null +++ b/abci/auction.go @@ -0,0 +1,231 @@ +package abci + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "reflect" + "sort" + + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// TopOfBlock contains information about how the top of block should be built. +type TopOfBlock struct { + // Txs contains the transactions that should be included in the top of block. + Txs [][]byte + + // Size is the total size of the top of block. + Size int64 + + // Cache is the cache of transactions that were seen, stored in order to ignore them + // when building the rest of the block. + Cache map[string]struct{} +} + +func NewTopOfBlock() TopOfBlock { + return TopOfBlock{ + Cache: make(map[string]struct{}), + } +} + +// BuildTOB inputs all of the vote extensions and outputs a top of block proposal that includes +// the highest bidding valid transaction along with all the bundled transactions. +func (h *ProposalHandler) BuildTOB(ctx sdk.Context, voteExtensionInfo abci.ExtendedCommitInfo, maxBytes int64) TopOfBlock { + // Get the bid transactions from the vote extensions. + sortedBidTxs := h.GetBidsFromVoteExtensions(voteExtensionInfo.Votes) + + // Track the transactions we can remove from the mempool + txsToRemove := make(map[sdk.Tx]struct{}) + + // Attempt to select the highest bid transaction that is valid and whose + // bundled transactions are valid. + var topOfBlock TopOfBlock + for _, bidTx := range sortedBidTxs { + // Cache the context so that we can write it back to the original context + // when we know we have a valid top of block bundle. + cacheCtx, write := ctx.CacheContext() + + // Attempt to build the top of block using the bid transaction. + proposal, err := h.buildTOB(cacheCtx, bidTx) + if err != nil { + h.logger.Info( + "vote extension auction failed to verify auction tx", + "err", err, + ) + txsToRemove[bidTx] = struct{}{} + continue + } + + if proposal.Size <= maxBytes { + // At this point, both the bid transaction itself and all the bundled + // transactions are valid. So we select the bid transaction along with + // all the bundled transactions and apply the state changes to the cache + // context. + topOfBlock = proposal + write() + + break + } + + h.logger.Info( + "failed to select auction bid tx; auction tx size is too large", + "tx_size", proposal.Size, + "max_size", maxBytes, + ) + } + + // Remove all of the transactions that were not valid. + for tx := range txsToRemove { + h.RemoveTx(tx) + } + + return topOfBlock +} + +// VerifyTOB verifies that the set of vote extensions used in prepare proposal deterministically +// produce the same top of block proposal. +func (h *ProposalHandler) VerifyTOB(ctx sdk.Context, proposalTxs [][]byte) (*AuctionInfo, error) { + // Proposal must include at least the auction info. + if len(proposalTxs) < NumInjectedTxs { + return nil, fmt.Errorf("proposal is too small; expected at least %d slots", NumInjectedTxs) + } + + // Extract the auction info from the proposal. + auctionInfo := &AuctionInfo{} + if err := auctionInfo.Unmarshal(proposalTxs[AuctionInfoIndex]); err != nil { + return nil, fmt.Errorf("failed to unmarshal auction info: %w", err) + } + + // Verify that the proposal contains the expected number of top of block transactions. + if len(proposalTxs) < int(auctionInfo.NumTxs)+NumInjectedTxs { + return nil, fmt.Errorf("number of txs in proposal do not match expected in auction info; expected at least %d slots", auctionInfo.NumTxs+NumInjectedTxs) + } + + // Unmarshall the vote extension information from the auction info. + lastCommitInfo := abci.ExtendedCommitInfo{} + if err := lastCommitInfo.Unmarshal(auctionInfo.ExtendedCommitInfo); err != nil { + return nil, fmt.Errorf("failed to unmarshal last commit info from auction info: %w", err) + } + + // Build the top of block proposal from the auction info. + expectedTOB := h.BuildTOB(ctx, lastCommitInfo, auctionInfo.MaxTxBytes) + + // Verify that the top of block txs matches the top of block proposal txs. + actualTOBTxs := proposalTxs[NumInjectedTxs : auctionInfo.NumTxs+NumInjectedTxs] + if !reflect.DeepEqual(actualTOBTxs, expectedTOB.Txs) { + return nil, fmt.Errorf("expected top of block txs does not match top of block proposal") + } + + return auctionInfo, nil +} + +// GetBidsFromVoteExtensions returns all of the auction bid transactions from +// the vote extensions in sorted descending order. +func (h *ProposalHandler) GetBidsFromVoteExtensions(voteExtensions []abci.ExtendedVoteInfo) []sdk.Tx { + bidTxs := make([]sdk.Tx, 0) + + // Iterate through all vote extensions and extract the auction transactions. + for _, voteInfo := range voteExtensions { + voteExtension := voteInfo.VoteExtension + + // Check if the vote extension contains an auction transaction. + if bidTx, err := h.getAuctionTxFromVoteExtension(voteExtension); err == nil { + bidTxs = append(bidTxs, bidTx) + } + } + + // Sort the auction transactions by their bid amount in descending order. + sort.Slice(bidTxs, func(i, j int) bool { + // In the case of an error, we want to sort the transaction to the end of the list. + bidInfoI, err := h.mempool.GetAuctionBidInfo(bidTxs[i]) + if err != nil { + return false + } + + bidInfoJ, err := h.mempool.GetAuctionBidInfo(bidTxs[j]) + if err != nil { + return true + } + + return bidInfoI.Bid.IsGTE(bidInfoJ.Bid) + }) + + return bidTxs +} + +// buildTOB verifies that the auction and bundled transactions are valid and returns the transactions that +// should be included in the top of block, size of the auction transaction and bundle, and a cache +// of all transactions that should be ignored. +func (h *ProposalHandler) buildTOB(ctx sdk.Context, bidTx sdk.Tx) (TopOfBlock, error) { + proposal := NewTopOfBlock() + + // Ensure that the bid transaction is valid + bidTxBz, err := h.PrepareProposalVerifyTx(ctx, bidTx) + if err != nil { + return proposal, err + } + + bidInfo, err := h.mempool.GetAuctionBidInfo(bidTx) + if err != nil { + return proposal, err + } + + // store the bytes of each ref tx as sdk.Tx bytes in order to build a valid proposal + sdkTxBytes := make([][]byte, len(bidInfo.Transactions)) + + // Ensure that the bundled transactions are valid + for index, rawRefTx := range bidInfo.Transactions { + // convert the bundled raw transaction to a sdk.Tx + refTx, err := h.mempool.WrapBundleTransaction(rawRefTx) + if err != nil { + return TopOfBlock{}, err + } + + txBz, err := h.PrepareProposalVerifyTx(ctx, refTx) + if err != nil { + return TopOfBlock{}, err + } + + hashBz := sha256.Sum256(txBz) + hash := hex.EncodeToString(hashBz[:]) + + proposal.Cache[hash] = struct{}{} + sdkTxBytes[index] = txBz + } + + // cache the bytes of the bid transaction + hashBz := sha256.Sum256(bidTxBz) + hash := hex.EncodeToString(hashBz[:]) + proposal.Cache[hash] = struct{}{} + + txs := [][]byte{bidTxBz} + txs = append(txs, sdkTxBytes...) + + // Set the top of block transactions and size. + proposal.Txs = txs + proposal.Size = int64(len(bidTxBz)) + + return proposal, nil +} + +// getAuctionTxFromVoteExtension extracts the auction transaction from the vote extension. +func (h *ProposalHandler) getAuctionTxFromVoteExtension(voteExtension []byte) (sdk.Tx, error) { + if len(voteExtension) == 0 { + return nil, fmt.Errorf("vote extension is empty") + } + + // Attempt to unmarshal the auction transaction. + bidTx, err := h.txDecoder(voteExtension) + if err != nil { + return nil, err + } + + // Verify the auction transaction has bid information. + if bidInfo, err := h.mempool.GetAuctionBidInfo(bidTx); err != nil || bidInfo == nil { + return nil, fmt.Errorf("vote extension does not contain an auction transaction") + } + + return bidTx, nil +} diff --git a/abci/auction.pb.go b/abci/auction.pb.go new file mode 100644 index 0000000..3115410 --- /dev/null +++ b/abci/auction.pb.go @@ -0,0 +1,395 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pob/abci/v1/auction.proto + +package abci + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// AuctionInfo contains information about the top of block auction +// that was run in PrepareProposal using vote extensions. +type AuctionInfo struct { + // extended_commit_info contains the vote extensions that were used to run the auction. + ExtendedCommitInfo []byte `protobuf:"bytes,1,opt,name=extended_commit_info,json=extendedCommitInfo,proto3" json:"extended_commit_info,omitempty"` + // max_tx_bytes is the maximum number of bytes that were allowed for the proposal. + MaxTxBytes int64 `protobuf:"varint,2,opt,name=max_tx_bytes,json=maxTxBytes,proto3" json:"max_tx_bytes,omitempty"` + // num_txs is the number of transactions that were included in the proposal. + NumTxs uint64 `protobuf:"varint,3,opt,name=num_txs,json=numTxs,proto3" json:"num_txs,omitempty"` +} + +func (m *AuctionInfo) Reset() { *m = AuctionInfo{} } +func (m *AuctionInfo) String() string { return proto.CompactTextString(m) } +func (*AuctionInfo) ProtoMessage() {} +func (*AuctionInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_ea32f9b647554bf5, []int{0} +} +func (m *AuctionInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AuctionInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AuctionInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AuctionInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuctionInfo.Merge(m, src) +} +func (m *AuctionInfo) XXX_Size() int { + return m.Size() +} +func (m *AuctionInfo) XXX_DiscardUnknown() { + xxx_messageInfo_AuctionInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_AuctionInfo proto.InternalMessageInfo + +func (m *AuctionInfo) GetExtendedCommitInfo() []byte { + if m != nil { + return m.ExtendedCommitInfo + } + return nil +} + +func (m *AuctionInfo) GetMaxTxBytes() int64 { + if m != nil { + return m.MaxTxBytes + } + return 0 +} + +func (m *AuctionInfo) GetNumTxs() uint64 { + if m != nil { + return m.NumTxs + } + return 0 +} + +func init() { + proto.RegisterType((*AuctionInfo)(nil), "pob.abci.v1.AuctionInfo") +} + +func init() { proto.RegisterFile("pob/abci/v1/auction.proto", fileDescriptor_ea32f9b647554bf5) } + +var fileDescriptor_ea32f9b647554bf5 = []byte{ + // 224 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0xc8, 0x4f, 0xd2, + 0x4f, 0x4c, 0x4a, 0xce, 0xd4, 0x2f, 0x33, 0xd4, 0x4f, 0x2c, 0x4d, 0x2e, 0xc9, 0xcc, 0xcf, 0xd3, + 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2e, 0xc8, 0x4f, 0xd2, 0x03, 0x49, 0xe9, 0x95, 0x19, + 0x2a, 0x55, 0x71, 0x71, 0x3b, 0x42, 0x64, 0x3d, 0xf3, 0xd2, 0xf2, 0x85, 0x0c, 0xb8, 0x44, 0x52, + 0x2b, 0x4a, 0x52, 0xf3, 0x52, 0x52, 0x53, 0xe2, 0x93, 0xf3, 0x73, 0x73, 0x33, 0x4b, 0xe2, 0x33, + 0xf3, 0xd2, 0xf2, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x84, 0x60, 0x72, 0xce, 0x60, 0x29, + 0xb0, 0x0e, 0x05, 0x2e, 0x9e, 0xdc, 0xc4, 0x8a, 0xf8, 0x92, 0x8a, 0xf8, 0xa4, 0xca, 0x92, 0xd4, + 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d, 0xe6, 0x20, 0xae, 0xdc, 0xc4, 0x8a, 0x90, 0x0a, 0x27, 0x90, + 0x88, 0x90, 0x38, 0x17, 0x7b, 0x5e, 0x69, 0x6e, 0x7c, 0x49, 0x45, 0xb1, 0x04, 0xb3, 0x02, 0xa3, + 0x06, 0x4b, 0x10, 0x5b, 0x5e, 0x69, 0x6e, 0x48, 0x45, 0xb1, 0x93, 0xd9, 0x89, 0x47, 0x72, 0x8c, + 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, + 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xc9, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, + 0xe7, 0xea, 0x17, 0x67, 0x67, 0x16, 0xe8, 0xe6, 0xa6, 0x96, 0xe9, 0xc3, 0x7c, 0x94, 0xc4, 0x06, + 0xf6, 0x87, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xce, 0x6f, 0x84, 0x78, 0xe4, 0x00, 0x00, 0x00, +} + +func (m *AuctionInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AuctionInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AuctionInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.NumTxs != 0 { + i = encodeVarintAuction(dAtA, i, uint64(m.NumTxs)) + i-- + dAtA[i] = 0x18 + } + if m.MaxTxBytes != 0 { + i = encodeVarintAuction(dAtA, i, uint64(m.MaxTxBytes)) + i-- + dAtA[i] = 0x10 + } + if len(m.ExtendedCommitInfo) > 0 { + i -= len(m.ExtendedCommitInfo) + copy(dAtA[i:], m.ExtendedCommitInfo) + i = encodeVarintAuction(dAtA, i, uint64(len(m.ExtendedCommitInfo))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAuction(dAtA []byte, offset int, v uint64) int { + offset -= sovAuction(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AuctionInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ExtendedCommitInfo) + if l > 0 { + n += 1 + l + sovAuction(uint64(l)) + } + if m.MaxTxBytes != 0 { + n += 1 + sovAuction(uint64(m.MaxTxBytes)) + } + if m.NumTxs != 0 { + n += 1 + sovAuction(uint64(m.NumTxs)) + } + return n +} + +func sovAuction(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuction(x uint64) (n int) { + return sovAuction(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *AuctionInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuction + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AuctionInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AuctionInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtendedCommitInfo", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuction + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAuction + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAuction + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExtendedCommitInfo = append(m.ExtendedCommitInfo[:0], dAtA[iNdEx:postIndex]...) + if m.ExtendedCommitInfo == nil { + m.ExtendedCommitInfo = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTxBytes", wireType) + } + m.MaxTxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuction + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxTxBytes |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumTxs", wireType) + } + m.NumTxs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuction + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumTxs |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAuction(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuction + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuction(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuction + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuction + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuction + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuction + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuction + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuction + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuction = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuction = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuction = fmt.Errorf("proto: unexpected end of group") +) diff --git a/abci/auction_test.go b/abci/auction_test.go new file mode 100644 index 0000000..f3fb60d --- /dev/null +++ b/abci/auction_test.go @@ -0,0 +1,512 @@ +package abci_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + testutils "github.com/skip-mev/pob/testutils" + buildertypes "github.com/skip-mev/pob/x/builder/types" +) + +func (suite *ABCITestSuite) TestGetBidsFromVoteExtensions() { + testCases := []struct { + name string + createVoteExtensions func() ([][]byte, [][]byte) // returns (vote extensions, expected bids) + }{ + { + "no vote extensions", + func() ([][]byte, [][]byte) { + return nil, [][]byte{} + }, + }, + { + "no vote extensions", + func() ([][]byte, [][]byte) { + return [][]byte{}, [][]byte{} + }, + }, + { + "single vote extension", + func() ([][]byte, [][]byte) { + bidTxBz, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + voteExtensions := [][]byte{ + bidTxBz, + } + + expectedBids := [][]byte{ + bidTxBz, + } + + return voteExtensions, expectedBids + }, + }, + { + "multiple vote extensions", + func() ([][]byte, [][]byte) { + bidTxBz1, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + bidTxBz2, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + voteExtensions := [][]byte{ + bidTxBz1, + bidTxBz2, + } + + expectedBids := [][]byte{ + bidTxBz1, + bidTxBz2, + } + + return voteExtensions, expectedBids + }, + }, + { + "multiple vote extensions with some noise", + func() ([][]byte, [][]byte) { + bidTxBz1, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + bidTxBz2, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + voteExtensions := [][]byte{ + bidTxBz1, + nil, + bidTxBz2, + []byte("noise"), + []byte("noise p2"), + } + + expectedBids := [][]byte{ + bidTxBz1, + bidTxBz2, + } + + return voteExtensions, expectedBids + }, + }, + { + "multiple vote extensions with some normal txs", + func() ([][]byte, [][]byte) { + bidTxBz1, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + bidTxBz2, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + randomBz, err := testutils.CreateRandomTxBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + 0, + 1, + 0, + ) + suite.Require().NoError(err) + + voteExtensions := [][]byte{ + bidTxBz1, + bidTxBz2, + nil, + randomBz, + []byte("noise p2"), + } + + expectedBids := [][]byte{ + bidTxBz1, + bidTxBz2, + } + + return voteExtensions, expectedBids + }, + }, + { + "multiple vote extensions with some normal txs in unsorted order", + func() ([][]byte, [][]byte) { + bidTxBz1, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(1001)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + bidTxBz2, err := testutils.CreateAuctionTxWithSignerBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + randomBz, err := testutils.CreateRandomTxBz( + suite.encodingConfig.TxConfig, + suite.accounts[0], + 0, + 1, + 0, + ) + suite.Require().NoError(err) + + voteExtensions := [][]byte{ + bidTxBz2, + bidTxBz1, + nil, + randomBz, + []byte("noise p2"), + } + + expectedBids := [][]byte{ + bidTxBz1, + bidTxBz2, + } + + return voteExtensions, expectedBids + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + voteExtensions, expectedBids := tc.createVoteExtensions() + + commitInfo := suite.createExtendedVoteInfo(voteExtensions) + + // get the bids from the vote extensions + bids := suite.proposalHandler.GetBidsFromVoteExtensions(commitInfo) + + // Check invarients + suite.Require().Equal(len(expectedBids), len(bids)) + for i, bid := range expectedBids { + actualBz, err := suite.encodingConfig.TxConfig.TxEncoder()(bids[i]) + suite.Require().NoError(err) + + suite.Require().Equal(bid, actualBz) + } + }) + } +} + +func (suite *ABCITestSuite) TestBuildTOB() { + params := buildertypes.Params{ + MaxBundleSize: 4, + MinBuyInFee: sdk.NewCoin("foo", sdk.NewInt(100)), + ReserveFee: sdk.NewCoin("foo", sdk.NewInt(100)), + MinBidIncrement: sdk.NewCoin("foo", sdk.NewInt(100)), + FrontRunningProtection: true, + } + suite.builderKeeper.SetParams(suite.ctx, params) + + testCases := []struct { + name string + getBidTxs func() ([]sdk.Tx, sdk.Tx) // returns the bids and the winning bid + maxBytes int64 + }{ + { + "no bids", + func() ([]sdk.Tx, sdk.Tx) { + return []sdk.Tx{}, nil + }, + 1000000000, + }, + { + "single bid", + func() ([]sdk.Tx, sdk.Tx) { + bidTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx}, bidTx + }, + 1000000000, + }, + { + "single invalid bid (bid is too small)", + func() ([]sdk.Tx, sdk.Tx) { + bidTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(1)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx}, nil + }, + 1000000000, + }, + { + "single invalid bid with front-running", + func() ([]sdk.Tx, sdk.Tx) { + bidTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(1000)), + 0, + 1, + []testutils.Account{suite.accounts[0], suite.accounts[1]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx}, nil + }, + 1000000000, + }, + { + "single invalid bid with too many transactions in the bundle", + func() ([]sdk.Tx, sdk.Tx) { + bidTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{ + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + }, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx}, nil + }, + 1000000000, + }, + { + "single bid but max bytes is too small", + func() ([]sdk.Tx, sdk.Tx) { + bidTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx}, nil + }, + 1, + }, + { + "multiple bids", + func() ([]sdk.Tx, sdk.Tx) { + bidTx1, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + bidTx2, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[1], + sdk.NewCoin("foo", sdk.NewInt(102)), + 0, + 1, + []testutils.Account{suite.accounts[1]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx2, bidTx1}, bidTx2 + }, + 1000000000, + }, + { + "multiple bids with front-running", + func() ([]sdk.Tx, sdk.Tx) { + bidTx1, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(1000)), + 0, + 1, + []testutils.Account{suite.accounts[0], suite.accounts[1]}, + ) + suite.Require().NoError(err) + + bidTx2, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[1], + sdk.NewCoin("foo", sdk.NewInt(200)), + 0, + 1, + []testutils.Account{suite.accounts[1]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx1, bidTx2}, bidTx2 + }, + 1000000000, + }, + { + "multiple bids with too many transactions in the bundle", + func() ([]sdk.Tx, sdk.Tx) { + bidTx1, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{ + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + suite.accounts[0], + }, + ) + suite.Require().NoError(err) + + bidTx2, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[1], + sdk.NewCoin("foo", sdk.NewInt(102)), + 0, + 1, + []testutils.Account{suite.accounts[1]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx1, bidTx2}, bidTx2 + }, + 1000000000, + }, + { + "multiple bids unsorted", + func() ([]sdk.Tx, sdk.Tx) { + bidTx1, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(101)), + 0, + 1, + []testutils.Account{suite.accounts[0]}, + ) + suite.Require().NoError(err) + + bidTx2, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[1], + sdk.NewCoin("foo", sdk.NewInt(102)), + 0, + 1, + []testutils.Account{suite.accounts[1]}, + ) + suite.Require().NoError(err) + + return []sdk.Tx{bidTx1, bidTx2}, bidTx2 + }, + 1000000000, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + bidTxs, winningBid := tc.getBidTxs() + + commitInfo := suite.createExtendedCommitInfoFromTxs(bidTxs) + + // Host the auction + proposal := suite.proposalHandler.BuildTOB(suite.ctx, commitInfo, tc.maxBytes) + + // Size of the proposal should be less than or equal to the max bytes + suite.Require().LessOrEqual(proposal.Size, tc.maxBytes) + + if winningBid == nil { + suite.Require().Len(proposal.Txs, 0) + suite.Require().Equal(proposal.Size, int64(0)) + } else { + // Get info about the winning bid + winningBidBz, err := suite.encodingConfig.TxConfig.TxEncoder()(winningBid) + suite.Require().NoError(err) + + auctionBidInfo, err := suite.mempool.GetAuctionBidInfo(winningBid) + suite.Require().NoError(err) + + // Verify that the size of the proposal is the size of the winning bid + // plus the size of the bundle + suite.Require().Equal(len(proposal.Txs), len(auctionBidInfo.Transactions)+1) + + // Verify that the winning bid is the first transaction in the proposal + suite.Require().Equal(proposal.Txs[0], winningBidBz) + + // Verify the ordering of transactions in the proposal + for index, tx := range proposal.Txs[1:] { + suite.Equal(tx, auctionBidInfo.Transactions[index]) + } + } + }) + } +} diff --git a/abci/proposals.go b/abci/proposals.go index f00b016..18a1b4e 100644 --- a/abci/proposals.go +++ b/abci/proposals.go @@ -1,7 +1,6 @@ package abci import ( - "bytes" "context" "crypto/sha256" "encoding/hex" @@ -12,7 +11,17 @@ import ( "github.com/cometbft/cometbft/libs/log" sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" - "github.com/skip-mev/pob/mempool" + mempool "github.com/skip-mev/pob/mempool" +) + +const ( + // NumInjectedTxs is the minimum number of transactions that were injected into + // the proposal but are not actual transactions. In this case, the auction + // info is injected into the proposal but should be ignored by the application.ß + NumInjectedTxs = 1 + + // AuctionInfoIndex is the index of the auction info in the proposal. + AuctionInfoIndex = 0 ) type ( @@ -63,132 +72,72 @@ func NewProposalHandler( // top-of-block auctioning and general block proposal construction. func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { return func(ctx sdk.Context, req abci.RequestPrepareProposal) abci.ResponsePrepareProposal { - var ( - selectedTxs [][]byte - totalTxBytes int64 - ) + // Proposal includes all of the transactions that will be included in the + // block along with the vote extensions from the previous block included at + // the beginning of the proposal. Vote extensions must be included in the + // first slot of the proposal because they are inaccessible in ProcessProposal. + proposal := make([][]byte, 0) - bidTxIterator := h.mempool.AuctionBidSelect(ctx) - txsToRemove := make(map[sdk.Tx]struct{}, 0) - seenTxs := make(map[string]struct{}, 0) + // Build the top of block portion of the proposal given the vote extensions + // from the previous block. + topOfBlock := h.BuildTOB(ctx, req.LocalLastCommit, req.MaxTxBytes) - // Attempt to select the highest bid transaction that is valid and whose - // bundled transactions are valid. - selectBidTxLoop: - for ; bidTxIterator != nil; bidTxIterator = bidTxIterator.Next() { - cacheCtx, write := ctx.CacheContext() - tmpBidTx := bidTxIterator.Tx() - - bidTxBz, err := h.PrepareProposalVerifyTx(cacheCtx, tmpBidTx) - if err != nil { - txsToRemove[tmpBidTx] = struct{}{} - continue selectBidTxLoop - } - - bidTxSize := int64(len(bidTxBz)) - if bidTxSize <= req.MaxTxBytes { - bidInfo, err := h.mempool.GetAuctionBidInfo(tmpBidTx) - if err != nil { - // Some transactions in the bundle may be malformatted or invalid, so - // we remove the bid transaction and try the next top bid. - txsToRemove[tmpBidTx] = struct{}{} - continue selectBidTxLoop - } - - // store the bytes of each ref tx as sdk.Tx bytes in order to build a valid proposal - bundledTransactions := bidInfo.Transactions - sdkTxBytes := make([][]byte, len(bundledTransactions)) - - // Ensure that the bundled transactions are valid - for index, rawRefTx := range bundledTransactions { - refTx, err := h.mempool.WrapBundleTransaction(rawRefTx) - if err != nil { - // Malformed bundled transaction, so we remove the bid transaction - // and try the next top bid. - txsToRemove[tmpBidTx] = struct{}{} - continue selectBidTxLoop - } - - txBz, err := h.PrepareProposalVerifyTx(cacheCtx, refTx) - if err != nil { - // Invalid bundled transaction, so we remove the bid transaction - // and try the next top bid. - txsToRemove[tmpBidTx] = struct{}{} - continue selectBidTxLoop - } - - sdkTxBytes[index] = txBz - } - - // At this point, both the bid transaction itself and all the bundled - // transactions are valid. So we select the bid transaction along with - // all the bundled transactions. We also mark these transactions as seen and - // update the total size selected thus far. - totalTxBytes += bidTxSize - selectedTxs = append(selectedTxs, bidTxBz) - selectedTxs = append(selectedTxs, sdkTxBytes...) - - for _, refTxRaw := range sdkTxBytes { - hash := sha256.Sum256(refTxRaw) - txHash := hex.EncodeToString(hash[:]) - seenTxs[txHash] = struct{}{} - } - - // Write the cache context to the original context when we know we have a - // valid top of block bundle. - write() - - break selectBidTxLoop - } - - txsToRemove[tmpBidTx] = struct{}{} - h.logger.Info( - "failed to select auction bid tx; tx size is too large", - "tx_size", bidTxSize, - "max_size", req.MaxTxBytes, - ) + // If information is unable to be marshaled, we return an empty proposal. This will + // cause another proposal to be generated after it is rejected in ProcessProposal. + lastCommitInfo, err := req.LocalLastCommit.Marshal() + if err != nil { + return abci.ResponsePrepareProposal{Txs: proposal} } - // Remove all invalid transactions from the mempool. - for tx := range txsToRemove { - h.RemoveTx(tx) + auctionInfo := AuctionInfo{ + ExtendedCommitInfo: lastCommitInfo, + MaxTxBytes: req.MaxTxBytes, + NumTxs: uint64(len(topOfBlock.Txs)), } - iterator := h.mempool.Select(ctx, nil) - txsToRemove = map[sdk.Tx]struct{}{} + // Add the auction info and top of block transactions into the proposal. + auctionInfoBz, err := auctionInfo.Marshal() + if err != nil { + return abci.ResponsePrepareProposal{Txs: proposal} + } + + proposal = append(proposal, auctionInfoBz) + proposal = append(proposal, topOfBlock.Txs...) // Select remaining transactions for the block proposal until we've reached // size capacity. - selectTxLoop: - for ; iterator != nil; iterator = iterator.Next() { + totalTxBytes := topOfBlock.Size + txsToRemove := make(map[sdk.Tx]struct{}, 0) + for iterator := h.mempool.Select(ctx, nil); iterator != nil; iterator = iterator.Next() { memTx := iterator.Tx() - // If the transaction is already included in the proposal, then we skip it. + // If the transaction has already been seen in the top of block, skip it. txBz, err := h.txEncoder(memTx) if err != nil { txsToRemove[memTx] = struct{}{} - continue selectTxLoop + continue } - hash := sha256.Sum256(txBz) - txHash := hex.EncodeToString(hash[:]) - if _, ok := seenTxs[txHash]; ok { - continue selectTxLoop + hashBz := sha256.Sum256(txBz) + hash := hex.EncodeToString(hashBz[:]) + if _, ok := topOfBlock.Cache[hash]; ok { + continue } + // Verify that the transaction is valid. txBz, err = h.PrepareProposalVerifyTx(ctx, memTx) if err != nil { txsToRemove[memTx] = struct{}{} - continue selectTxLoop + continue } txSize := int64(len(txBz)) if totalTxBytes += txSize; totalTxBytes <= req.MaxTxBytes { - selectedTxs = append(selectedTxs, txBz) + proposal = append(proposal, txBz) } else { // We've reached capacity per req.MaxTxBytes so we cannot select any // more transactions. - break selectTxLoop + break } } @@ -197,7 +146,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { h.RemoveTx(tx) } - return abci.ResponsePrepareProposal{Txs: selectedTxs} + return abci.ResponsePrepareProposal{Txs: proposal} } } @@ -205,50 +154,44 @@ 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 { - for index, txBz := range req.Txs { + proposal := req.Txs + + // Verify that the same top of block transactions can be built from the vote + // extensions included in the proposal. + auctionInfo, err := h.VerifyTOB(ctx, proposal) + if err != nil { + return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} + } + + // Track the transactions that need to be removed from the mempool. + txsToRemove := make(map[sdk.Tx]struct{}, 0) + invalidProposal := false + + // Verify that the remaining transactions in the proposal are valid. + for _, txBz := range proposal[auctionInfo.NumTxs+NumInjectedTxs:] { tx, err := h.ProcessProposalVerifyTx(ctx, txBz) - if err != nil { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - - bidInfo, err := h.mempool.GetAuctionBidInfo(tx) - if err != nil { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - - // If the transaction is an auction bid, then we need to ensure that it is - // the first transaction in the block proposal and that the order of - // transactions in the block proposal follows the order of transactions in - // the bid. - if bidInfo != nil { - if index != 0 { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} + if tx == nil || err != nil { + invalidProposal = true + if tx != nil { + txsToRemove[tx] = struct{}{} } - bundledTransactions := bidInfo.Transactions - if len(req.Txs) < len(bundledTransactions)+1 { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - - for i, refTxRaw := range bundledTransactions { - // Wrap and then encode the bundled transaction to ensure that the underlying - // reference transaction can be processed as an sdk.Tx. - wrappedTx, err := h.mempool.WrapBundleTransaction(refTxRaw) - if err != nil { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - - refTxBz, err := h.txEncoder(wrappedTx) - if err != nil { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - - if !bytes.Equal(refTxBz, req.Txs[i+1]) { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - } + continue } + // The only auction transactions that should be included in the block proposal + // must be at the top of the block. + if bidInfo, err := h.mempool.GetAuctionBidInfo(tx); err != nil || bidInfo != nil { + invalidProposal = true + } + } + // Remove all invalid transactions from the mempool. + for tx := range txsToRemove { + h.RemoveTx(tx) + } + + if invalidProposal { + return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} diff --git a/abci/proposals_test.go b/abci/proposals_test.go index 6875f59..9e0c150 100644 --- a/abci/proposals_test.go +++ b/abci/proposals_test.go @@ -1,9 +1,7 @@ package abci_test import ( - "bytes" - - abcitypes "github.com/cometbft/cometbft/abci/types" + comettypes "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/pob/abci" testutils "github.com/skip-mev/pob/testutils" @@ -17,10 +15,11 @@ func (suite *ABCITestSuite) TestPrepareProposal() { maxTxBytes int64 = 1000000000000000000 // mempool configuration - numNormalTxs = 100 - numAuctionTxs = 100 - numBundledTxs = 3 - insertRefTxs = false + numNormalTxs = 100 + numAuctionTxs = 100 + numBundledTxs = 3 + insertRefTxs = false + expectedTopAuctionTx sdk.Tx // auction configuration maxBundleSize uint32 = 10 @@ -30,11 +29,11 @@ func (suite *ABCITestSuite) TestPrepareProposal() { ) cases := []struct { - name string - malleate func() - expectedNumberProposalTxs int - expectedNumberTxsInMempool int - isTopBidValid bool + name string + malleate func() + expectedNumberProposalTxs int + expectedNumberTxsInMempool int + expectedNumberTxsInAuctionMempool int }{ { "single bundle in the mempool", @@ -43,10 +42,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numAuctionTxs = 1 numBundledTxs = 3 insertRefTxs = true + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 4, + 5, 3, - true, + 1, }, { "single bundle in the mempool, no ref txs in mempool", @@ -55,10 +58,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numAuctionTxs = 1 numBundledTxs = 3 insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 4, + 5, 0, - true, + 1, }, { "single bundle in the mempool, not valid", @@ -68,10 +75,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numNormalTxs = 0 numAuctionTxs = 1 numBundledTxs = 3 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = nil }, + 1, 0, 0, - false, }, { "single bundle in the mempool, not valid with ref txs in mempool", @@ -82,10 +93,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numAuctionTxs = 1 numBundledTxs = 3 insertRefTxs = true + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = nil }, + 4, 3, - 3, - false, + 0, }, { "multiple bundles in the mempool, no normal txs + no ref txs in mempool", @@ -96,22 +111,30 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numAuctionTxs = 10 numBundledTxs = 3 insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 4, + 5, 0, - true, + 10, }, { - "multiple bundles in the mempool, no normal txs + ref txs in mempool", + "multiple bundles in the mempool, normal txs + ref txs in mempool", func() { numNormalTxs = 0 numAuctionTxs = 10 numBundledTxs = 3 insertRefTxs = true + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 31, + 32, 30, - true, + 10, }, { "normal txs only", @@ -119,10 +142,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numNormalTxs = 1 numAuctionTxs = 0 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, + 2, 1, - 1, - false, + 0, }, { "many normal txs only", @@ -130,10 +157,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numNormalTxs = 100 numAuctionTxs = 0 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, + 101, 100, - 100, - false, + 0, }, { "single normal tx, single auction tx", @@ -141,10 +172,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numNormalTxs = 1 numAuctionTxs = 1 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 2, + 3, + 1, 1, - true, }, { "single normal tx, single auction tx with ref txs", @@ -152,11 +187,15 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numNormalTxs = 1 numAuctionTxs = 1 numBundledTxs = 3 - insertRefTxs = false + insertRefTxs = true + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 5, + 6, + 4, 1, - true, }, { "single normal tx, single failing auction tx with ref txs", @@ -167,10 +206,14 @@ func (suite *ABCITestSuite) TestPrepareProposal() { insertRefTxs = true suite.auctionBidAmount = sdk.NewCoin("foo", sdk.NewInt(2000)) // this will fail the ante handler reserveFee = sdk.NewCoin("foo", sdk.NewInt(1000000000)) + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = nil }, + 5, 4, - 4, - false, + 0, }, { "many normal tx, single auction tx with no ref txs", @@ -180,57 +223,72 @@ func (suite *ABCITestSuite) TestPrepareProposal() { numNormalTxs = 100 numAuctionTxs = 1 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = nil }, - 101, + 102, 100, - true, + 1, }, { "many normal tx, single auction tx with ref txs", func() { numNormalTxs = 100 - numAuctionTxs = 1 + numAuctionTxs = 100 numBundledTxs = 3 insertRefTxs = true + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) }, - 104, - 103, - true, - }, - { - "many normal tx, single auction tx with ref txs", - func() { - numNormalTxs = 100 - numAuctionTxs = 1 - numBundledTxs = 3 - insertRefTxs = false - }, - 104, + 402, + 400, 100, - true, }, { - "many normal tx, many auction tx with ref txs", + "many normal tx, many auction tx with ref txs but top bid is invalid", func() { numNormalTxs = 100 numAuctionTxs = 100 numBundledTxs = 1 insertRefTxs = true + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + expectedTopAuctionTx = suite.mempool.GetTopAuctionTx(suite.ctx) + + // create a new bid that is greater than the current top bid + bid := sdk.NewCoin("foo", sdk.NewInt(200000000000000000)) + bidTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + bid, + 0, + 0, + []testutils.Account{suite.accounts[0], suite.accounts[1]}, + ) + suite.Require().NoError(err) + + // add the new bid to the mempool + err = suite.mempool.Insert(suite.ctx, bidTx) + suite.Require().NoError(err) + + suite.Require().Equal(suite.mempool.CountAuctionTx(), 101) }, - 201, + 202, 200, - true, + 100, }, } for _, tc := range cases { suite.Run(tc.name, func() { - suite.SetupTest() // reset tc.malleate() - suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) - - // create a new auction + // Create a new auction. params := buildertypes.Params{ MaxBundleSize: maxBundleSize, ReserveFee: reserveFee, @@ -241,59 +299,54 @@ func (suite *ABCITestSuite) TestPrepareProposal() { suite.builderKeeper.SetParams(suite.ctx, params) suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxDecoder(), suite.encodingConfig.TxConfig.TxEncoder(), suite.mempool) - // reset the proposal handler with the new mempool + // Reset the proposal handler with the new mempool. suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder()) + // Create a prepare proposal request based on the current state of the mempool. handler := suite.proposalHandler.PrepareProposalHandler() - res := handler(suite.ctx, abcitypes.RequestPrepareProposal{ - MaxTxBytes: maxTxBytes, - }) + req := suite.createPrepareProposalRequest(maxTxBytes) + res := handler(suite.ctx, req) // -------------------- Check Invariants -------------------- // - // 1. The auction tx must fail if we know it is invalid - suite.Require().Equal(tc.isTopBidValid, suite.isTopBidValid()) + // The first slot in the proposal must be the auction info + auctionInfo := abci.AuctionInfo{} + err := auctionInfo.Unmarshal(res.Txs[abci.AuctionInfoIndex]) + suite.Require().NoError(err) - // 2. total bytes must be less than or equal to maxTxBytes + // Total bytes must be less than or equal to maxTxBytes totalBytes := int64(0) - if suite.isTopBidValid() { - totalBytes += int64(len(res.Txs[0])) - - for _, tx := range res.Txs[1+numBundledTxs:] { - totalBytes += int64(len(tx)) - } - } else { - for _, tx := range res.Txs { - totalBytes += int64(len(tx)) - } + for _, tx := range res.Txs[abci.NumInjectedTxs:] { + totalBytes += int64(len(tx)) } suite.Require().LessOrEqual(totalBytes, maxTxBytes) - // 3. the number of transactions in the response must be equal to the number of expected transactions + // The number of transactions in the response must be equal to the number of expected transactions suite.Require().Equal(tc.expectedNumberProposalTxs, len(res.Txs)) - // 4. if there are auction transactions, the first transaction must be the top bid + // If there are auction transactions, the first transaction must be the top bid // and the rest of the bundle must be in the response - if suite.isTopBidValid() { - auctionTx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[0]) + if expectedTopAuctionTx != nil { + auctionTx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[1]) suite.Require().NoError(err) bidInfo, err := suite.mempool.GetAuctionBidInfo(auctionTx) suite.Require().NoError(err) for index, tx := range bidInfo.Transactions { - suite.Require().Equal(tx, res.Txs[index+1]) + suite.Require().Equal(tx, res.Txs[abci.NumInjectedTxs+index+1]) } } // 5. All of the transactions must be unique uniqueTxs := make(map[string]bool) - for _, tx := range res.Txs { + for _, tx := range res.Txs[abci.NumInjectedTxs:] { suite.Require().False(uniqueTxs[string(tx)]) uniqueTxs[string(tx)] = true } // 6. The number of transactions in the mempool must be correct suite.Require().Equal(tc.expectedNumberTxsInMempool, suite.mempool.CountTx()) + suite.Require().Equal(tc.expectedNumberTxsInAuctionMempool, suite.mempool.CountAuctionTx()) }) } } @@ -301,213 +354,412 @@ 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 + numNormalTxs = 100 + numAuctionTxs = 1 + numBundledTxs = 3 + insertRefTxs = false // auction set up - maxBundleSize uint32 = 10 - reserveFee = sdk.NewCoin("foo", sdk.NewInt(1000)) - minBuyInFee = sdk.NewCoin("foo", sdk.NewInt(1000)) - frontRunningProtection = true + maxBundleSize uint32 = 10 + reserveFee = sdk.NewCoin("foo", sdk.NewInt(1000)) + minBuyInFee = sdk.NewCoin("foo", sdk.NewInt(1000)) ) cases := []struct { - name string - malleate func() - isTopBidValid bool - response abcitypes.ResponseProcessProposal_ProposalStatus + name string + createTxs func() [][]byte + response comettypes.ResponseProcessProposal_ProposalStatus }{ { - "single normal tx, no auction tx", - func() { + "single normal tx, no vote extension info", + func() [][]byte { numNormalTxs = 1 numAuctionTxs = 0 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + txs := suite.exportMempool() + + return txs }, - false, - abcitypes.ResponseProcessProposal_ACCEPT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single auction tx, no normal txs", - func() { + "single auction tx, no vote extension info", + func() [][]byte { numNormalTxs = 0 numAuctionTxs = 1 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + return suite.exportMempool() }, - true, - abcitypes.ResponseProcessProposal_ACCEPT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single auction tx, single auction tx", - func() { + "single auction tx, single auction tx, no vote extension info", + func() [][]byte { numNormalTxs = 1 numAuctionTxs = 1 numBundledTxs = 0 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + return suite.exportMempool() }, - true, - abcitypes.ResponseProcessProposal_ACCEPT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single auction tx, single auction tx with ref txs", - func() { + "single auction tx with ref txs (no unwrapping)", + func() [][]byte { numNormalTxs = 1 numAuctionTxs = 1 numBundledTxs = 4 + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + topAuctionTx := suite.mempool.GetTopAuctionTx(suite.ctx) + suite.Require().NotNil(topAuctionTx) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(topAuctionTx) + suite.Require().NoError(err) + + auctionInfo := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 5) + + proposal := append([][]byte{ + auctionInfo, + txBz, + }, suite.exportMempool()...) + + return proposal }, - true, - abcitypes.ResponseProcessProposal_ACCEPT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single auction tx, single auction tx with no ref txs", - func() { - numNormalTxs = 1 + "single auction tx with ref txs (with unwrapping)", + func() [][]byte { + numNormalTxs = 0 numAuctionTxs = 1 numBundledTxs = 4 insertRefTxs = false - exportRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + topAuctionTx := suite.mempool.GetTopAuctionTx(suite.ctx) + suite.Require().NotNil(topAuctionTx) + + bidInfo, err := suite.mempool.GetAuctionBidInfo(topAuctionTx) + suite.Require().NoError(err) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(topAuctionTx) + suite.Require().NoError(err) + + auctionInfo := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 5) + + proposal := append([][]byte{ + auctionInfo, + txBz, + }, bidInfo.Transactions...) + + return proposal }, - true, - abcitypes.ResponseProcessProposal_REJECT, + comettypes.ResponseProcessProposal_ACCEPT, }, { - "multiple auction txs, single normal tx", - func() { - numNormalTxs = 1 + "single auction tx but no inclusion of ref txs", + func() [][]byte { + numNormalTxs = 0 + numAuctionTxs = 1 + numBundledTxs = 4 + insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + topAuctionTx := suite.mempool.GetTopAuctionTx(suite.ctx) + suite.Require().NotNil(topAuctionTx) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(topAuctionTx) + suite.Require().NoError(err) + + auctionInfo := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 5) + + return [][]byte{ + auctionInfo, + txBz, + } + }, + comettypes.ResponseProcessProposal_REJECT, + }, + { + "single auction tx, but auction tx is not valid", + func() [][]byte { + tx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 1, + 0, // invalid timeout + []testutils.Account{}, + ) + suite.Require().NoError(err) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) + suite.Require().NoError(err) + + auctionInfoBz := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 1) + + return [][]byte{ + auctionInfoBz, + txBz, + } + }, + comettypes.ResponseProcessProposal_REJECT, + }, + { + "single auction tx with ref txs, but auction tx is not valid", + func() [][]byte { + tx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(100)), + 1, + 1, + []testutils.Account{suite.accounts[1], suite.accounts[1], suite.accounts[0]}, + ) + suite.Require().NoError(err) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) + suite.Require().NoError(err) + + auctionInfoBz := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 4) + + bidInfo, err := suite.mempool.GetAuctionBidInfo(tx) + suite.Require().NoError(err) + + return append([][]byte{ + auctionInfoBz, + txBz, + }, bidInfo.Transactions...) + }, + comettypes.ResponseProcessProposal_REJECT, + }, + { + "multiple auction txs but wrong auction tx is at top of block", + func() [][]byte { + numNormalTxs = 0 numAuctionTxs = 2 - numBundledTxs = 4 - insertRefTxs = true - exportRefTxs = true + numBundledTxs = 0 + insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + _, auctionTxBzs := suite.getAllAuctionTxs() + + auctionInfo := suite.createAuctionInfoFromTxBzs(auctionTxBzs, 1) + + proposal := [][]byte{ + auctionInfo, + auctionTxBzs[1], + } + + return proposal }, - true, - abcitypes.ResponseProcessProposal_REJECT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single auction txs, multiple normal tx", - func() { - numNormalTxs = 100 - numAuctionTxs = 1 - numBundledTxs = 4 + "multiple auction txs included in block", + func() [][]byte { + numNormalTxs = 0 + numAuctionTxs = 2 + numBundledTxs = 0 + insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + _, auctionTxBzs := suite.getAllAuctionTxs() + + auctionInfo := suite.createAuctionInfoFromTxBzs(auctionTxBzs, 1) + + proposal := [][]byte{ + auctionInfo, + auctionTxBzs[0], + auctionTxBzs[1], + } + + return proposal }, - true, - abcitypes.ResponseProcessProposal_ACCEPT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single invalid auction tx, multiple normal tx", - func() { - numNormalTxs = 100 - numAuctionTxs = 1 - numBundledTxs = 4 - reserveFee = sdk.NewCoin("foo", sdk.NewInt(100000000000000000)) - insertRefTxs = true - }, - false, - abcitypes.ResponseProcessProposal_REJECT, - }, - { - "single valid auction txs but missing ref txs", - func() { + "single auction tx, but rest of the mempool is invalid", + func() [][]byte { numNormalTxs = 0 numAuctionTxs = 1 - numBundledTxs = 4 - reserveFee = sdk.NewCoin("foo", sdk.NewInt(1000)) + numBundledTxs = 0 insertRefTxs = false - exportRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + topAuctionTx := suite.mempool.GetTopAuctionTx(suite.ctx) + suite.Require().NotNil(topAuctionTx) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(topAuctionTx) + suite.Require().NoError(err) + + auctionInfo := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 1) + + proposal := [][]byte{ + auctionInfo, + txBz, + []byte("invalid tx"), + } + + return proposal }, - true, - abcitypes.ResponseProcessProposal_REJECT, + comettypes.ResponseProcessProposal_REJECT, }, { - "single valid auction txs but missing ref txs, with many normal txs", - func() { + "single auction tx with filled mempool, but rest of the mempool is invalid", + func() [][]byte { numNormalTxs = 100 numAuctionTxs = 1 - numBundledTxs = 4 - reserveFee = sdk.NewCoin("foo", sdk.NewInt(1000)) + numBundledTxs = 0 insertRefTxs = false - exportRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + topAuctionTx := suite.mempool.GetTopAuctionTx(suite.ctx) + suite.Require().NotNil(topAuctionTx) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(topAuctionTx) + suite.Require().NoError(err) + + auctionInfo := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 1) + + proposal := append([][]byte{ + auctionInfo, + txBz, + }, suite.exportMempool()...) + + proposal = append(proposal, []byte("invalid tx")) + + return proposal }, - true, - abcitypes.ResponseProcessProposal_REJECT, + comettypes.ResponseProcessProposal_REJECT, }, { - "auction tx with frontrunning", - func() { - randomAccount := testutils.RandomAccounts(suite.random, 1)[0] - bidder := suite.accounts[0] - bid := sdk.NewCoin("foo", sdk.NewInt(696969696969)) - nonce := suite.nonces[bidder.Address.String()] - frontRunningTx, _ = testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, suite.accounts[0], bid, nonce+1, 1000, []testutils.Account{bidder, randomAccount}) - suite.Require().NotNil(frontRunningTx) - + "multiple auction txs with filled mempool", + func() [][]byte { numNormalTxs = 100 - numAuctionTxs = 1 - numBundledTxs = 4 - insertRefTxs = true - exportRefTxs = true + numAuctionTxs = 10 + numBundledTxs = 0 + insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + _, auctionTxBzs := suite.getAllAuctionTxs() + + auctionInfo := suite.createAuctionInfoFromTxBzs(auctionTxBzs, 1) + + proposal := append([][]byte{ + auctionInfo, + auctionTxBzs[0], + }, suite.exportMempool()...) + + return proposal }, - false, - abcitypes.ResponseProcessProposal_REJECT, + comettypes.ResponseProcessProposal_ACCEPT, }, { - "auction tx with frontrunning, but frontrunning protection disabled", - func() { - randomAccount := testutils.RandomAccounts(suite.random, 1)[0] - bidder := suite.accounts[0] - bid := sdk.NewCoin("foo", sdk.NewInt(696969696969)) - nonce := suite.nonces[bidder.Address.String()] - frontRunningTx, _ = testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, suite.accounts[0], bid, nonce+1, 1000, []testutils.Account{bidder, randomAccount}) - suite.Require().NotNil(frontRunningTx) + "multiple auction txs with ref txs + filled mempool", + func() [][]byte { + numNormalTxs = 100 + numAuctionTxs = 10 + numBundledTxs = 10 + insertRefTxs = false + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + auctionTxs, auctionTxBzs := suite.getAllAuctionTxs() + + auctionInfo := suite.createAuctionInfoFromTxBzs(auctionTxBzs, 11) + + bidInfo, err := suite.mempool.GetAuctionBidInfo(auctionTxs[0]) + suite.Require().NoError(err) + + proposal := append([][]byte{ + auctionInfo, + auctionTxBzs[0], + }, bidInfo.Transactions...) + + proposal = append(proposal, suite.exportMempool()...) + + return proposal + }, + comettypes.ResponseProcessProposal_ACCEPT, + }, + { + "auction tx with front-running", + func() [][]byte { + numNormalTxs = 100 numAuctionTxs = 0 - frontRunningProtection = false + numBundledTxs = 0 + insertRefTxs = false + + suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) + + topAuctionTx, err := testutils.CreateAuctionTxWithSigners( + suite.encodingConfig.TxConfig, + suite.accounts[0], + sdk.NewCoin("foo", sdk.NewInt(1000000)), + 0, + 1, + []testutils.Account{suite.accounts[0], suite.accounts[1]}, // front-running + ) + suite.Require().NoError(err) + + txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(topAuctionTx) + suite.Require().NoError(err) + + bidInfo, err := suite.mempool.GetAuctionBidInfo(topAuctionTx) + suite.Require().NoError(err) + + auctionInfo := suite.createAuctionInfoFromTxBzs([][]byte{txBz}, 3) + + proposal := append([][]byte{ + auctionInfo, + txBz, + }, bidInfo.Transactions...) + + proposal = append(proposal, suite.exportMempool()...) + + return proposal }, - true, - abcitypes.ResponseProcessProposal_ACCEPT, + comettypes.ResponseProcessProposal_REJECT, }, } for _, tc := range cases { suite.Run(tc.name, func() { - suite.SetupTest() // reset - tc.malleate() - - suite.createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs, insertRefTxs) - - // reset the proposal handler with the new mempool - suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder()) - - if frontRunningTx != nil { - suite.Require().NoError(suite.mempool.Insert(suite.ctx, frontRunningTx)) - } - // create a new auction params := buildertypes.Params{ MaxBundleSize: maxBundleSize, ReserveFee: reserveFee, MinBuyInFee: minBuyInFee, - FrontRunningProtection: frontRunningProtection, + FrontRunningProtection: true, MinBidIncrement: suite.minBidIncrement, } suite.builderKeeper.SetParams(suite.ctx, params) suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, 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)) - } + // reset the proposal handler with the new mempool + suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder()) handler := suite.proposalHandler.ProcessProposalHandler() - res := handler(suite.ctx, abcitypes.RequestProcessProposal{ - Txs: txs, + res := handler(suite.ctx, comettypes.RequestProcessProposal{ + Txs: tc.createTxs(), }) // Check if the response is valid @@ -515,16 +767,3 @@ func (suite *ABCITestSuite) TestProcessProposal() { }) } } - -// 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 { - iterator := suite.mempool.AuctionBidSelect(suite.ctx) - if iterator == nil { - return false - } - - // check if the top bid is valid - _, err := suite.anteHandler(suite.ctx, iterator.Tx(), true) - return err == nil -} diff --git a/api/pob/abci/v1/auction.pulsar.go b/api/pob/abci/v1/auction.pulsar.go new file mode 100644 index 0000000..eadfe00 --- /dev/null +++ b/api/pob/abci/v1/auction.pulsar.go @@ -0,0 +1,1414 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package abciv1 + +import ( + fmt "fmt" + runtime "github.com/cosmos/cosmos-proto/runtime" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sort "sort" + sync "sync" +) + +var ( + md_AuctionInfo protoreflect.MessageDescriptor + fd_AuctionInfo_extended_commit_info protoreflect.FieldDescriptor + fd_AuctionInfo_max_tx_bytes protoreflect.FieldDescriptor + fd_AuctionInfo_num_txs protoreflect.FieldDescriptor +) + +func init() { + file_pob_abci_v1_auction_proto_init() + md_AuctionInfo = File_pob_abci_v1_auction_proto.Messages().ByName("AuctionInfo") + fd_AuctionInfo_extended_commit_info = md_AuctionInfo.Fields().ByName("extended_commit_info") + fd_AuctionInfo_max_tx_bytes = md_AuctionInfo.Fields().ByName("max_tx_bytes") + fd_AuctionInfo_num_txs = md_AuctionInfo.Fields().ByName("num_txs") +} + +var _ protoreflect.Message = (*fastReflection_AuctionInfo)(nil) + +type fastReflection_AuctionInfo AuctionInfo + +func (x *AuctionInfo) ProtoReflect() protoreflect.Message { + return (*fastReflection_AuctionInfo)(x) +} + +func (x *AuctionInfo) slowProtoReflect() protoreflect.Message { + mi := &file_pob_abci_v1_auction_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_AuctionInfo_messageType fastReflection_AuctionInfo_messageType +var _ protoreflect.MessageType = fastReflection_AuctionInfo_messageType{} + +type fastReflection_AuctionInfo_messageType struct{} + +func (x fastReflection_AuctionInfo_messageType) Zero() protoreflect.Message { + return (*fastReflection_AuctionInfo)(nil) +} +func (x fastReflection_AuctionInfo_messageType) New() protoreflect.Message { + return new(fastReflection_AuctionInfo) +} +func (x fastReflection_AuctionInfo_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_AuctionInfo +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_AuctionInfo) Descriptor() protoreflect.MessageDescriptor { + return md_AuctionInfo +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_AuctionInfo) Type() protoreflect.MessageType { + return _fastReflection_AuctionInfo_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_AuctionInfo) New() protoreflect.Message { + return new(fastReflection_AuctionInfo) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_AuctionInfo) Interface() protoreflect.ProtoMessage { + return (*AuctionInfo)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_AuctionInfo) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if len(x.ExtendedCommitInfo) != 0 { + value := protoreflect.ValueOfBytes(x.ExtendedCommitInfo) + if !f(fd_AuctionInfo_extended_commit_info, value) { + return + } + } + if x.MaxTxBytes != int64(0) { + value := protoreflect.ValueOfInt64(x.MaxTxBytes) + if !f(fd_AuctionInfo_max_tx_bytes, value) { + return + } + } + if x.NumTxs != uint64(0) { + value := protoreflect.ValueOfUint64(x.NumTxs) + if !f(fd_AuctionInfo_num_txs, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_AuctionInfo) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "pob.abci.v1.AuctionInfo.extended_commit_info": + return len(x.ExtendedCommitInfo) != 0 + case "pob.abci.v1.AuctionInfo.max_tx_bytes": + return x.MaxTxBytes != int64(0) + case "pob.abci.v1.AuctionInfo.num_txs": + return x.NumTxs != uint64(0) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.AuctionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.AuctionInfo does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AuctionInfo) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "pob.abci.v1.AuctionInfo.extended_commit_info": + x.ExtendedCommitInfo = nil + case "pob.abci.v1.AuctionInfo.max_tx_bytes": + x.MaxTxBytes = int64(0) + case "pob.abci.v1.AuctionInfo.num_txs": + x.NumTxs = uint64(0) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.AuctionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.AuctionInfo does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_AuctionInfo) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "pob.abci.v1.AuctionInfo.extended_commit_info": + value := x.ExtendedCommitInfo + return protoreflect.ValueOfBytes(value) + case "pob.abci.v1.AuctionInfo.max_tx_bytes": + value := x.MaxTxBytes + return protoreflect.ValueOfInt64(value) + case "pob.abci.v1.AuctionInfo.num_txs": + value := x.NumTxs + return protoreflect.ValueOfUint64(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.AuctionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.AuctionInfo does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AuctionInfo) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "pob.abci.v1.AuctionInfo.extended_commit_info": + x.ExtendedCommitInfo = value.Bytes() + case "pob.abci.v1.AuctionInfo.max_tx_bytes": + x.MaxTxBytes = value.Int() + case "pob.abci.v1.AuctionInfo.num_txs": + x.NumTxs = value.Uint() + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.AuctionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.AuctionInfo does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AuctionInfo) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "pob.abci.v1.AuctionInfo.extended_commit_info": + panic(fmt.Errorf("field extended_commit_info of message pob.abci.v1.AuctionInfo is not mutable")) + case "pob.abci.v1.AuctionInfo.max_tx_bytes": + panic(fmt.Errorf("field max_tx_bytes of message pob.abci.v1.AuctionInfo is not mutable")) + case "pob.abci.v1.AuctionInfo.num_txs": + panic(fmt.Errorf("field num_txs of message pob.abci.v1.AuctionInfo is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.AuctionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.AuctionInfo does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_AuctionInfo) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "pob.abci.v1.AuctionInfo.extended_commit_info": + return protoreflect.ValueOfBytes(nil) + case "pob.abci.v1.AuctionInfo.max_tx_bytes": + return protoreflect.ValueOfInt64(int64(0)) + case "pob.abci.v1.AuctionInfo.num_txs": + return protoreflect.ValueOfUint64(uint64(0)) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.AuctionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.AuctionInfo does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_AuctionInfo) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in pob.abci.v1.AuctionInfo", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_AuctionInfo) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AuctionInfo) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_AuctionInfo) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_AuctionInfo) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*AuctionInfo) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.ExtendedCommitInfo) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.MaxTxBytes != 0 { + n += 1 + runtime.Sov(uint64(x.MaxTxBytes)) + } + if x.NumTxs != 0 { + n += 1 + runtime.Sov(uint64(x.NumTxs)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*AuctionInfo) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.NumTxs != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.NumTxs)) + i-- + dAtA[i] = 0x18 + } + if x.MaxTxBytes != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.MaxTxBytes)) + i-- + dAtA[i] = 0x10 + } + if len(x.ExtendedCommitInfo) > 0 { + i -= len(x.ExtendedCommitInfo) + copy(dAtA[i:], x.ExtendedCommitInfo) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.ExtendedCommitInfo))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*AuctionInfo) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: AuctionInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: AuctionInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ExtendedCommitInfo", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.ExtendedCommitInfo = append(x.ExtendedCommitInfo[:0], dAtA[iNdEx:postIndex]...) + if x.ExtendedCommitInfo == nil { + x.ExtendedCommitInfo = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field MaxTxBytes", wireType) + } + x.MaxTxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.MaxTxBytes |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field NumTxs", wireType) + } + x.NumTxs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.NumTxs |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var _ protoreflect.Map = (*_VoteExtensionInfo_2_map)(nil) + +type _VoteExtensionInfo_2_map struct { + m *map[string][]byte +} + +func (x *_VoteExtensionInfo_2_map) Len() int { + if x.m == nil { + return 0 + } + return len(*x.m) +} + +func (x *_VoteExtensionInfo_2_map) Range(f func(protoreflect.MapKey, protoreflect.Value) bool) { + if x.m == nil { + return + } + for k, v := range *x.m { + mapKey := (protoreflect.MapKey)(protoreflect.ValueOfString(k)) + mapValue := protoreflect.ValueOfBytes(v) + if !f(mapKey, mapValue) { + break + } + } +} + +func (x *_VoteExtensionInfo_2_map) Has(key protoreflect.MapKey) bool { + if x.m == nil { + return false + } + keyUnwrapped := key.String() + concreteValue := keyUnwrapped + _, ok := (*x.m)[concreteValue] + return ok +} + +func (x *_VoteExtensionInfo_2_map) Clear(key protoreflect.MapKey) { + if x.m == nil { + return + } + keyUnwrapped := key.String() + concreteKey := keyUnwrapped + delete(*x.m, concreteKey) +} + +func (x *_VoteExtensionInfo_2_map) Get(key protoreflect.MapKey) protoreflect.Value { + if x.m == nil { + return protoreflect.Value{} + } + keyUnwrapped := key.String() + concreteKey := keyUnwrapped + v, ok := (*x.m)[concreteKey] + if !ok { + return protoreflect.Value{} + } + return protoreflect.ValueOfBytes(v) +} + +func (x *_VoteExtensionInfo_2_map) Set(key protoreflect.MapKey, value protoreflect.Value) { + if !key.IsValid() || !value.IsValid() { + panic("invalid key or value provided") + } + keyUnwrapped := key.String() + concreteKey := keyUnwrapped + valueUnwrapped := value.Bytes() + concreteValue := valueUnwrapped + (*x.m)[concreteKey] = concreteValue +} + +func (x *_VoteExtensionInfo_2_map) Mutable(key protoreflect.MapKey) protoreflect.Value { + panic("should not call Mutable on protoreflect.Map whose value is not of type protoreflect.Message") +} + +func (x *_VoteExtensionInfo_2_map) NewValue() protoreflect.Value { + var v []byte + return protoreflect.ValueOfBytes(v) +} + +func (x *_VoteExtensionInfo_2_map) IsValid() bool { + return x.m != nil +} + +var ( + md_VoteExtensionInfo protoreflect.MessageDescriptor + fd_VoteExtensionInfo_registry protoreflect.FieldDescriptor +) + +func init() { + file_pob_abci_v1_auction_proto_init() + md_VoteExtensionInfo = File_pob_abci_v1_auction_proto.Messages().ByName("VoteExtensionInfo") + fd_VoteExtensionInfo_registry = md_VoteExtensionInfo.Fields().ByName("registry") +} + +var _ protoreflect.Message = (*fastReflection_VoteExtensionInfo)(nil) + +type fastReflection_VoteExtensionInfo VoteExtensionInfo + +func (x *VoteExtensionInfo) ProtoReflect() protoreflect.Message { + return (*fastReflection_VoteExtensionInfo)(x) +} + +func (x *VoteExtensionInfo) slowProtoReflect() protoreflect.Message { + mi := &file_pob_abci_v1_auction_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_VoteExtensionInfo_messageType fastReflection_VoteExtensionInfo_messageType +var _ protoreflect.MessageType = fastReflection_VoteExtensionInfo_messageType{} + +type fastReflection_VoteExtensionInfo_messageType struct{} + +func (x fastReflection_VoteExtensionInfo_messageType) Zero() protoreflect.Message { + return (*fastReflection_VoteExtensionInfo)(nil) +} +func (x fastReflection_VoteExtensionInfo_messageType) New() protoreflect.Message { + return new(fastReflection_VoteExtensionInfo) +} +func (x fastReflection_VoteExtensionInfo_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_VoteExtensionInfo +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_VoteExtensionInfo) Descriptor() protoreflect.MessageDescriptor { + return md_VoteExtensionInfo +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_VoteExtensionInfo) Type() protoreflect.MessageType { + return _fastReflection_VoteExtensionInfo_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_VoteExtensionInfo) New() protoreflect.Message { + return new(fastReflection_VoteExtensionInfo) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_VoteExtensionInfo) Interface() protoreflect.ProtoMessage { + return (*VoteExtensionInfo)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_VoteExtensionInfo) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if len(x.Registry) != 0 { + value := protoreflect.ValueOfMap(&_VoteExtensionInfo_2_map{m: &x.Registry}) + if !f(fd_VoteExtensionInfo_registry, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_VoteExtensionInfo) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "pob.abci.v1.VoteExtensionInfo.registry": + return len(x.Registry) != 0 + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.VoteExtensionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.VoteExtensionInfo does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_VoteExtensionInfo) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "pob.abci.v1.VoteExtensionInfo.registry": + x.Registry = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.VoteExtensionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.VoteExtensionInfo does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_VoteExtensionInfo) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "pob.abci.v1.VoteExtensionInfo.registry": + if len(x.Registry) == 0 { + return protoreflect.ValueOfMap(&_VoteExtensionInfo_2_map{}) + } + mapValue := &_VoteExtensionInfo_2_map{m: &x.Registry} + return protoreflect.ValueOfMap(mapValue) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.VoteExtensionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.VoteExtensionInfo does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_VoteExtensionInfo) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "pob.abci.v1.VoteExtensionInfo.registry": + mv := value.Map() + cmv := mv.(*_VoteExtensionInfo_2_map) + x.Registry = *cmv.m + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.VoteExtensionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.VoteExtensionInfo does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_VoteExtensionInfo) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "pob.abci.v1.VoteExtensionInfo.registry": + if x.Registry == nil { + x.Registry = make(map[string][]byte) + } + value := &_VoteExtensionInfo_2_map{m: &x.Registry} + return protoreflect.ValueOfMap(value) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.VoteExtensionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.VoteExtensionInfo does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_VoteExtensionInfo) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "pob.abci.v1.VoteExtensionInfo.registry": + m := make(map[string][]byte) + return protoreflect.ValueOfMap(&_VoteExtensionInfo_2_map{m: &m}) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: pob.abci.v1.VoteExtensionInfo")) + } + panic(fmt.Errorf("message pob.abci.v1.VoteExtensionInfo does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_VoteExtensionInfo) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in pob.abci.v1.VoteExtensionInfo", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_VoteExtensionInfo) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_VoteExtensionInfo) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_VoteExtensionInfo) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_VoteExtensionInfo) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*VoteExtensionInfo) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if len(x.Registry) > 0 { + SiZeMaP := func(k string, v []byte) { + l = 1 + len(v) + runtime.Sov(uint64(len(v))) + mapEntrySize := 1 + len(k) + runtime.Sov(uint64(len(k))) + l + n += mapEntrySize + 1 + runtime.Sov(uint64(mapEntrySize)) + } + if options.Deterministic { + sortme := make([]string, 0, len(x.Registry)) + for k := range x.Registry { + sortme = append(sortme, k) + } + sort.Strings(sortme) + for _, k := range sortme { + v := x.Registry[k] + SiZeMaP(k, v) + } + } else { + for k, v := range x.Registry { + SiZeMaP(k, v) + } + } + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*VoteExtensionInfo) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.Registry) > 0 { + MaRsHaLmAp := func(k string, v []byte) (protoiface.MarshalOutput, error) { + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = runtime.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = runtime.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = runtime.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + return protoiface.MarshalOutput{}, nil + } + if options.Deterministic { + keysForRegistry := make([]string, 0, len(x.Registry)) + for k := range x.Registry { + keysForRegistry = append(keysForRegistry, string(k)) + } + sort.Slice(keysForRegistry, func(i, j int) bool { + return keysForRegistry[i] < keysForRegistry[j] + }) + for iNdEx := len(keysForRegistry) - 1; iNdEx >= 0; iNdEx-- { + v := x.Registry[string(keysForRegistry[iNdEx])] + out, err := MaRsHaLmAp(keysForRegistry[iNdEx], v) + if err != nil { + return out, err + } + } + } else { + for k := range x.Registry { + v := x.Registry[k] + out, err := MaRsHaLmAp(k, v) + if err != nil { + return out, err + } + } + } + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*VoteExtensionInfo) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: VoteExtensionInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: VoteExtensionInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Registry", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Registry == nil { + x.Registry = make(map[string][]byte) + } + var mapkey string + var mapvalue []byte + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postStringIndexmapkey > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapbyteLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapbyteLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intMapbyteLen := int(mapbyteLen) + if intMapbyteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postbytesIndex := iNdEx + intMapbyteLen + if postbytesIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postbytesIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + mapvalue = make([]byte, mapbyteLen) + copy(mapvalue, dAtA[iNdEx:postbytesIndex]) + iNdEx = postbytesIndex + } else { + iNdEx = entryPreIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + x.Registry[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: pob/abci/v1/auction.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// AuctionInfo contains information about the top of block auction +// that was run in PrepareProposal using vote extensions. +type AuctionInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // extended_commit_info contains the vote extensions that were used to run the auction. + ExtendedCommitInfo []byte `protobuf:"bytes,1,opt,name=extended_commit_info,json=extendedCommitInfo,proto3" json:"extended_commit_info,omitempty"` + // max_tx_bytes is the maximum number of bytes that were allowed for the proposal. + MaxTxBytes int64 `protobuf:"varint,2,opt,name=max_tx_bytes,json=maxTxBytes,proto3" json:"max_tx_bytes,omitempty"` + // num_txs is the number of transactions that were included in the proposal. + NumTxs uint64 `protobuf:"varint,3,opt,name=num_txs,json=numTxs,proto3" json:"num_txs,omitempty"` +} + +func (x *AuctionInfo) Reset() { + *x = AuctionInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_pob_abci_v1_auction_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuctionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuctionInfo) ProtoMessage() {} + +// Deprecated: Use AuctionInfo.ProtoReflect.Descriptor instead. +func (*AuctionInfo) Descriptor() ([]byte, []int) { + return file_pob_abci_v1_auction_proto_rawDescGZIP(), []int{0} +} + +func (x *AuctionInfo) GetExtendedCommitInfo() []byte { + if x != nil { + return x.ExtendedCommitInfo + } + return nil +} + +func (x *AuctionInfo) GetMaxTxBytes() int64 { + if x != nil { + return x.MaxTxBytes + } + return 0 +} + +func (x *AuctionInfo) GetNumTxs() uint64 { + if x != nil { + return x.NumTxs + } + return 0 +} + +// VoteExtensionInfo wraps all vote extension data into a registry which allows applications to define +// multiple different vote extensions that can be applied to state. +type VoteExtensionInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // registry is the data of the vote extension. + Registry map[string][]byte `protobuf:"bytes,2,rep,name=registry,proto3" json:"registry,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *VoteExtensionInfo) Reset() { + *x = VoteExtensionInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_pob_abci_v1_auction_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VoteExtensionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VoteExtensionInfo) ProtoMessage() {} + +// Deprecated: Use VoteExtensionInfo.ProtoReflect.Descriptor instead. +func (*VoteExtensionInfo) Descriptor() ([]byte, []int) { + return file_pob_abci_v1_auction_proto_rawDescGZIP(), []int{1} +} + +func (x *VoteExtensionInfo) GetRegistry() map[string][]byte { + if x != nil { + return x.Registry + } + return nil +} + +var File_pob_abci_v1_auction_proto protoreflect.FileDescriptor + +var file_pob_abci_v1_auction_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x70, 0x6f, 0x62, 0x2f, 0x61, 0x62, 0x63, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x70, 0x6f, 0x62, + 0x2e, 0x61, 0x62, 0x63, 0x69, 0x2e, 0x76, 0x31, 0x22, 0x7a, 0x0a, 0x0b, 0x41, 0x75, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x30, 0x0a, 0x14, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x61, 0x78, + 0x5f, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x6d, 0x61, 0x78, 0x54, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, + 0x75, 0x6d, 0x5f, 0x74, 0x78, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, + 0x6d, 0x54, 0x78, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x11, 0x56, 0x6f, 0x74, 0x65, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x08, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, + 0x6f, 0x62, 0x2e, 0x61, 0x62, 0x63, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x74, 0x65, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x1a, 0x3b, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x62, 0x2e, 0x61, 0x62, + 0x63, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x41, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x23, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, + 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x62, 0x2f, 0x61, 0x62, 0x63, 0x69, + 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x62, 0x63, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x41, 0x58, + 0xaa, 0x02, 0x0b, 0x50, 0x6f, 0x62, 0x2e, 0x41, 0x62, 0x63, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, + 0x0b, 0x50, 0x6f, 0x62, 0x5c, 0x41, 0x62, 0x63, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x17, 0x50, + 0x6f, 0x62, 0x5c, 0x41, 0x62, 0x63, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6f, 0x62, 0x3a, 0x3a, 0x41, 0x62, + 0x63, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pob_abci_v1_auction_proto_rawDescOnce sync.Once + file_pob_abci_v1_auction_proto_rawDescData = file_pob_abci_v1_auction_proto_rawDesc +) + +func file_pob_abci_v1_auction_proto_rawDescGZIP() []byte { + file_pob_abci_v1_auction_proto_rawDescOnce.Do(func() { + file_pob_abci_v1_auction_proto_rawDescData = protoimpl.X.CompressGZIP(file_pob_abci_v1_auction_proto_rawDescData) + }) + return file_pob_abci_v1_auction_proto_rawDescData +} + +var file_pob_abci_v1_auction_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_pob_abci_v1_auction_proto_goTypes = []interface{}{ + (*AuctionInfo)(nil), // 0: pob.abci.v1.AuctionInfo + (*VoteExtensionInfo)(nil), // 1: pob.abci.v1.VoteExtensionInfo + nil, // 2: pob.abci.v1.VoteExtensionInfo.RegistryEntry +} +var file_pob_abci_v1_auction_proto_depIdxs = []int32{ + 2, // 0: pob.abci.v1.VoteExtensionInfo.registry:type_name -> pob.abci.v1.VoteExtensionInfo.RegistryEntry + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_pob_abci_v1_auction_proto_init() } +func file_pob_abci_v1_auction_proto_init() { + if File_pob_abci_v1_auction_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pob_abci_v1_auction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AuctionInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pob_abci_v1_auction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VoteExtensionInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pob_abci_v1_auction_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_pob_abci_v1_auction_proto_goTypes, + DependencyIndexes: file_pob_abci_v1_auction_proto_depIdxs, + MessageInfos: file_pob_abci_v1_auction_proto_msgTypes, + }.Build() + File_pob_abci_v1_auction_proto = out.File + file_pob_abci_v1_auction_proto_rawDesc = nil + file_pob_abci_v1_auction_proto_goTypes = nil + file_pob_abci_v1_auction_proto_depIdxs = nil +} diff --git a/proto/pob/abci/v1/auction.proto b/proto/pob/abci/v1/auction.proto new file mode 100644 index 0000000..0c9af8d --- /dev/null +++ b/proto/pob/abci/v1/auction.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package pob.abci.v1; + +option go_package = "github.com/skip-mev/pob/abci"; + +// AuctionInfo contains information about the top of block auction +// that was run in PrepareProposal using vote extensions. +message AuctionInfo { + // extended_commit_info contains the vote extensions that were used to run the auction. + bytes extended_commit_info = 1; + + // max_tx_bytes is the maximum number of bytes that were allowed for the proposal. + int64 max_tx_bytes = 2; + + // num_txs is the number of transactions that were included in the proposal. + uint64 num_txs = 3; +} \ No newline at end of file diff --git a/testutils/utils.go b/testutils/utils.go index 446f8f4..3a97516 100644 --- a/testutils/utils.go +++ b/testutils/utils.go @@ -125,6 +125,15 @@ func CreateRandomTx(txCfg client.TxConfig, account Account, nonce, numberMsgs, t return txBuilder.GetTx(), nil } +func CreateRandomTxBz(txCfg client.TxConfig, account Account, nonce, numberMsgs, timeout uint64) ([]byte, error) { + tx, err := CreateRandomTx(txCfg, account, nonce, numberMsgs, timeout) + if err != nil { + return nil, err + } + + return txCfg.TxEncoder()(tx) +} + func CreateTxWithSigners(txCfg client.TxConfig, nonce, timeout uint64, signers []Account) (authsigning.Tx, error) { msgs := []sdk.Msg{} for _, signer := range signers { @@ -199,6 +208,20 @@ func CreateAuctionTxWithSigners(txCfg client.TxConfig, bidder Account, bid sdk.C return txBuilder.GetTx(), nil } +func CreateAuctionTxWithSignerBz(txCfg client.TxConfig, bidder Account, bid sdk.Coin, nonce, timeout uint64, signers []Account) ([]byte, error) { + bidTx, err := CreateAuctionTxWithSigners(txCfg, bidder, bid, nonce, timeout, signers) + if err != nil { + return nil, err + } + + bz, err := txCfg.TxEncoder()(bidTx) + if err != nil { + return nil, err + } + + return bz, nil +} + func CreateRandomMsgs(acc sdk.AccAddress, numberMsgs int) []sdk.Msg { msgs := make([]sdk.Msg, numberMsgs) for i := 0; i < numberMsgs; i++ {