diff --git a/abci/abci.go b/abci/abci.go index f868706..5a7e2c2 100644 --- a/abci/abci.go +++ b/abci/abci.go @@ -9,7 +9,6 @@ import ( "github.com/skip-mev/block-sdk/block" "github.com/skip-mev/block-sdk/block/proposals" - "github.com/skip-mev/block-sdk/block/proposals/types" ) const ( @@ -54,6 +53,10 @@ func NewProposalHandler( // valid transactions in the proposal (up to MaxBlockSize, MaxGasLimit). func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (resp *abci.ResponsePrepareProposal, err error) { + if req.Height <= 1 { + return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil + } + // In the case where there is a panic, we recover here and return an empty proposal. defer func() { if rec := recover(); rec != nil { @@ -65,14 +68,14 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { } }() - h.logger.Info("mempool distribution before proposal creation", "distribution", h.mempool.GetTxDistribution()) - - // Build an empty placeholder proposal with the maximum block size and gas limit. - maxBlockSize, maxGasLimit := proposals.GetBlockLimits(ctx) - emptyProposal := proposals.NewProposal(h.txEncoder, maxBlockSize, maxGasLimit) + h.logger.Info( + "mempool distribution before proposal creation", + "distribution", h.mempool.GetTxDistribution(), + "height", req.Height, + ) // Fill the proposal with transactions from each lane. - finalProposal, err := h.prepareLanesHandler(ctx, emptyProposal) + finalProposal, err := h.prepareLanesHandler(ctx, proposals.NewProposalWithContext(ctx, h.txEncoder)) if err != nil { h.logger.Error("failed to prepare proposal", "err", err) return &abci.ResponsePrepareProposal{Txs: make([][]byte, 0)}, err @@ -89,13 +92,17 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { "prepared proposal", "num_txs", len(txs), "total_tx_bytes", finalProposal.Info.BlockSize, - "max_tx_bytes", maxBlockSize, + "max_tx_bytes", finalProposal.Info.MaxBlockSize, "total_gas_limit", finalProposal.Info.GasLimit, - "max_gas_limit", maxGasLimit, + "max_gas_limit", finalProposal.Info.MaxGasLimit, "height", req.Height, ) - h.logger.Info("mempool distribution after proposal creation", "distribution", h.mempool.GetTxDistribution()) + h.logger.Info( + "mempool distribution after proposal creation", + "distribution", h.mempool.GetTxDistribution(), + "height", req.Height, + ) return &abci.ResponsePrepareProposal{ Txs: txs, @@ -111,6 +118,10 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { // transactions, then the proposal is rejected. func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { return func(ctx sdk.Context, req *abci.RequestProcessProposal) (resp *abci.ResponseProcessProposal, err error) { + if req.Height <= 1 { + return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil + } + // In the case where any of the lanes panic, we recover here and return a reject status. defer func() { if rec := recover(); rec != nil { @@ -130,13 +141,7 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { // Build handler that will verify the partial proposals according to each lane's verification logic. processLanesHandler := ChainProcessLanes(partialProposals, h.mempool.Registry()) - - // Build an empty placeholder proposal. - maxBlockSize, maxGasLimit := proposals.GetBlockLimits(ctx) - emptyProposal := proposals.NewProposal(h.txEncoder, maxBlockSize, maxGasLimit) - - // Verify the proposal according to the verification logic from each lane. - finalProposal, err := processLanesHandler(ctx, emptyProposal) + finalProposal, err := processLanesHandler(ctx, proposals.NewProposalWithContext(ctx, h.txEncoder)) if err != nil { h.logger.Error("failed to validate the proposal", "err", err) return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, err @@ -152,101 +157,12 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { "processed proposal", "num_txs", len(req.Txs), "total_tx_bytes", finalProposal.Info.BlockSize, - "max_tx_bytes", maxBlockSize, + "max_tx_bytes", finalProposal.Info.MaxBlockSize, "total_gas_limit", finalProposal.Info.GasLimit, - "max_gas_limit", maxGasLimit, + "max_gas_limit", finalProposal.Info.MaxGasLimit, "height", req.Height, ) return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil } } - -// ExtractLanes validates the proposal against the basic invariants that are required -// for the proposal to be valid. This includes: -// 1. The proposal must contain the proposal information and must be valid. -// 2. The proposal must contain the correct number of transactions for each lane. -func (h *ProposalHandler) ExtractLanes(proposal [][]byte) (types.ProposalInfo, [][][]byte, error) { - // If the proposal is empty, then the metadata was not included. - if len(proposal) == 0 { - return types.ProposalInfo{}, nil, fmt.Errorf("proposal does not contain proposal metadata") - } - - metaDataBz, txs := proposal[ProposalInfoIndex], proposal[ProposalInfoIndex+1:] - - // Retrieve the metadata from the proposal. - var metaData types.ProposalInfo - if err := metaData.Unmarshal(metaDataBz); err != nil { - return types.ProposalInfo{}, nil, fmt.Errorf("failed to unmarshal proposal metadata: %w", err) - } - - lanes := h.mempool.Registry() - partialProposals := make([][][]byte, len(lanes)) - - if metaData.TxsByLane == nil { - if len(txs) > 0 { - return types.ProposalInfo{}, nil, fmt.Errorf("proposal contains invalid number of transactions") - } - - return types.ProposalInfo{}, partialProposals, nil - } - - h.logger.Info( - "received proposal with metadata", - "max_block_size", metaData.MaxBlockSize, - "max_gas_limit", metaData.MaxGasLimit, - "gas_limit", metaData.GasLimit, - "block_size", metaData.BlockSize, - "lanes_with_txs", metaData.TxsByLane, - ) - - // Iterate through all of the lanes and match the corresponding transactions to the lane. - for index, lane := range lanes { - numTxs := metaData.TxsByLane[lane.Name()] - if numTxs > uint64(len(txs)) { - return types.ProposalInfo{}, nil, fmt.Errorf( - "proposal metadata contains invalid number of transactions for lane %s; got %d, expected %d", - lane.Name(), - len(txs), - numTxs, - ) - } - - partialProposals[index] = txs[:numTxs] - txs = txs[numTxs:] - } - - // If there are any transactions remaining in the proposal, then the proposal is invalid. - if len(txs) > 0 { - return types.ProposalInfo{}, nil, fmt.Errorf("proposal contains invalid number of transactions") - } - - return metaData, partialProposals, nil -} - -// ValidateBlockLimits validates the block limits of the proposal against the block limits -// of the chain. -func (h *ProposalHandler) ValidateBlockLimits(finalProposal proposals.Proposal, proposalInfo types.ProposalInfo) error { - // Conduct final checks on block size and gas limit. - if finalProposal.Info.BlockSize != proposalInfo.BlockSize { - h.logger.Error( - "proposal block size does not match", - "expected", proposalInfo.BlockSize, - "got", finalProposal.Info.BlockSize, - ) - - return fmt.Errorf("proposal block size does not match") - } - - if finalProposal.Info.GasLimit != proposalInfo.GasLimit { - h.logger.Error( - "proposal gas limit does not match", - "expected", proposalInfo.GasLimit, - "got", finalProposal.Info.GasLimit, - ) - - return fmt.Errorf("proposal gas limit does not match") - } - - return nil -} diff --git a/abci/abci_test.go b/abci/abci_test.go index 5a84a29..a257ee7 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -45,6 +45,7 @@ func (s *ProposalsTestSuite) SetupTest() { s.key = storetypes.NewKVStoreKey("test") testCtx := testutil.DefaultContextWithDB(s.T(), s.key, storetypes.NewTransientStoreKey("transient_test")) s.ctx = testCtx.Ctx.WithIsCheckTx(true) + s.ctx = s.ctx.WithBlockHeight(1) } func (s *ProposalsTestSuite) SetupSubTest() { @@ -58,7 +59,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) s.Require().Equal(1, len(resp.Txs)) @@ -93,7 +94,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx)) proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NotNil(resp) s.Require().NoError(err) @@ -145,7 +146,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NotNil(resp) s.Require().NoError(err) @@ -197,7 +198,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2)) proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NotNil(resp) s.Require().NoError(err) @@ -224,7 +225,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -266,7 +267,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -317,7 +318,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -369,7 +370,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -424,7 +425,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { size := int64(len(proposal[0]) - 1) s.setBlockParams(10000000, size) - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -472,7 +473,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposal := s.getTxBytes(freeTx) - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -551,7 +552,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler() proposal := s.getTxBytes(tx, bundleTxs[0], bundleTxs[1], bundleTxs[2], bundleTxs[3], freeTx, normalTx) - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -617,7 +618,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() { proposal := s.getTxBytes(tx, bundleTxs[0], normalTx) // Should be theoretically sufficient to fit the bid tx and the bundled tx + normal tx - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -667,7 +668,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { mempool, ).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -716,7 +717,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { mempool, ).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -766,7 +767,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { mempool, ).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -816,7 +817,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() { mempool, ).PrepareProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) @@ -855,7 +856,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { ) proposal := [][]byte{info} - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_ACCEPT}, resp) @@ -883,7 +884,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 1}, tx) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_ACCEPT}, resp) @@ -941,7 +942,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 1, mevLane.Name(): 2, freeLane.Name(): 1}, bidTx, bundleTxs[0], freeTx, tx) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NoError(err) s.Require().NotNil(resp) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_ACCEPT}, resp) @@ -974,7 +975,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal[0] = infoBz proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().Error(err) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) }) @@ -1006,7 +1007,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal[0] = infoBz proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().Error(err) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) }) @@ -1031,7 +1032,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { ) proposal := [][]byte{info, {0x01, 0x02, 0x03}} - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().Error(err) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) }) @@ -1063,7 +1064,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { ) proposal := [][]byte{info, txbz} - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().Error(err) s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp) }) @@ -1102,7 +1103,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 1, mevLane.Name(): 1}, tx2, tx) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NotNil(resp) s.Require().Error(err) }) @@ -1157,7 +1158,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 2, mevLane.Name(): 3}, bidTx, bundle[0], bundle[1], normalTx, normalTx2) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NotNil(resp) s.Require().Error(err) }) @@ -1207,7 +1208,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 2, mevLane.Name(): 1}, bidTx, normalTx, normalTx2) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NotNil(resp) s.Require().Error(err) }) @@ -1264,7 +1265,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() { proposal := s.createProposal(map[string]uint64{defaultLane.Name(): 2, mevLane.Name(): 1}, bidTx, normalTx, normalTx2) proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler() - resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal}) + resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: proposal, Height: 2}) s.Require().NotNil(resp) s.Require().Error(err) }) diff --git a/abci/utils.go b/abci/utils.go index 2923ff0..e6950f4 100644 --- a/abci/utils.go +++ b/abci/utils.go @@ -1,15 +1,107 @@ package abci import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/skip-mev/block-sdk/block" "github.com/skip-mev/block-sdk/block/proposals" + "github.com/skip-mev/block-sdk/block/proposals/types" "github.com/skip-mev/block-sdk/lanes/terminator" ) +// ExtractLanes validates the proposal against the basic invariants that are required +// for the proposal to be valid. This includes: +// 1. The proposal must contain the proposal information and must be valid. +// 2. The proposal must contain the correct number of transactions for each lane. +func (h *ProposalHandler) ExtractLanes(proposal [][]byte) (types.ProposalInfo, [][][]byte, error) { + // If the proposal is empty, then the metadata was not included. + if len(proposal) == 0 { + return types.ProposalInfo{}, nil, fmt.Errorf("proposal does not contain proposal metadata") + } + + metaDataBz, txs := proposal[ProposalInfoIndex], proposal[ProposalInfoIndex+1:] + + // Retrieve the metadata from the proposal. + var metaData types.ProposalInfo + if err := metaData.Unmarshal(metaDataBz); err != nil { + return types.ProposalInfo{}, nil, fmt.Errorf("failed to unmarshal proposal metadata: %w", err) + } + + lanes := h.mempool.Registry() + partialProposals := make([][][]byte, len(lanes)) + + if metaData.TxsByLane == nil { + if len(txs) > 0 { + return types.ProposalInfo{}, nil, fmt.Errorf("proposal contains invalid number of transactions") + } + + return types.ProposalInfo{}, partialProposals, nil + } + + h.logger.Info( + "received proposal with metadata", + "max_block_size", metaData.MaxBlockSize, + "max_gas_limit", metaData.MaxGasLimit, + "gas_limit", metaData.GasLimit, + "block_size", metaData.BlockSize, + "lanes_with_txs", metaData.TxsByLane, + ) + + // Iterate through all of the lanes and match the corresponding transactions to the lane. + for index, lane := range lanes { + numTxs := metaData.TxsByLane[lane.Name()] + if numTxs > uint64(len(txs)) { + return types.ProposalInfo{}, nil, fmt.Errorf( + "proposal metadata contains invalid number of transactions for lane %s; got %d, expected %d", + lane.Name(), + len(txs), + numTxs, + ) + } + + partialProposals[index] = txs[:numTxs] + txs = txs[numTxs:] + } + + // If there are any transactions remaining in the proposal, then the proposal is invalid. + if len(txs) > 0 { + return types.ProposalInfo{}, nil, fmt.Errorf("proposal contains invalid number of transactions") + } + + return metaData, partialProposals, nil +} + +// ValidateBlockLimits validates the block limits of the proposal against the block limits +// of the chain. +func (h *ProposalHandler) ValidateBlockLimits(finalProposal proposals.Proposal, proposalInfo types.ProposalInfo) error { + // Conduct final checks on block size and gas limit. + if finalProposal.Info.BlockSize != proposalInfo.BlockSize { + h.logger.Error( + "proposal block size does not match", + "expected", proposalInfo.BlockSize, + "got", finalProposal.Info.BlockSize, + ) + + return fmt.Errorf("proposal block size does not match") + } + + if finalProposal.Info.GasLimit != proposalInfo.GasLimit { + h.logger.Error( + "proposal gas limit does not match", + "expected", proposalInfo.GasLimit, + "got", finalProposal.Info.GasLimit, + ) + + return fmt.Errorf("proposal gas limit does not match") + } + + return nil +} + // ChainPrepareLanes chains together the proposal preparation logic from each lane into a // single function. The first lane in the chain is the first lane to be prepared and the -// last lane in the chain is the last lane to be prepared.In the case where any of the lanes +// last lane in the chain is the last lane to be prepared. In the case where any of the lanes // fail to prepare the partial proposal, the lane that failed will be skipped and the next // lane in the chain will be called to prepare the proposal. func ChainPrepareLanes(chain []block.Lane) block.PrepareLanesHandler { diff --git a/block/base/abci.go b/block/base/abci.go index 886360b..59b0b64 100644 --- a/block/base/abci.go +++ b/block/base/abci.go @@ -51,6 +51,8 @@ func (l *BaseLane) PrepareLane( "err", err, "num_txs_to_add", len(txsToInclude), "num_txs_to_remove", len(txsToRemove), + "lane_max_block_size", limit.MaxTxBytes, + "lane_max_gas_limit", limit.MaxGasLimit, ) return proposal, err @@ -61,6 +63,8 @@ func (l *BaseLane) PrepareLane( "lane", l.Name(), "num_txs_added", len(txsToInclude), "num_txs_removed", len(txsToRemove), + "lane_max_block_size", limit.MaxTxBytes, + "lane_max_gas_limit", limit.MaxGasLimit, ) return next(ctx, proposal) @@ -122,12 +126,20 @@ func (l *BaseLane) ProcessLane( return next(ctx, proposal) } -// AnteVerifyTx verifies that the transaction is valid respecting the ante verification logic of +// VerifyTx verifies that the transaction is valid respecting the ante verification logic of // of the antehandler chain. -func (l *BaseLane) AnteVerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { +func (l *BaseLane) VerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) error { if l.cfg.AnteHandler != nil { - return l.cfg.AnteHandler(ctx, tx, simulate) + // Only write to the context if the tx does not fail. + catchCtx, write := ctx.CacheContext() + if _, err := l.cfg.AnteHandler(catchCtx, tx, simulate); err != nil { + return err + } + + write() + + return nil } - return ctx, nil + return nil } diff --git a/block/base/handlers.go b/block/base/handlers.go index aa00829..e072057 100644 --- a/block/base/handlers.go +++ b/block/base/handlers.go @@ -89,7 +89,7 @@ func (l *BaseLane) DefaultPrepareLaneHandler() PrepareLaneHandler { } // Verify the transaction. - if ctx, err = l.AnteVerifyTx(ctx, tx, false); err != nil { + if err = l.VerifyTx(ctx, tx, false); err != nil { l.Logger().Info( "failed to verify tx", "tx_hash", txInfo.Hash, @@ -128,7 +128,7 @@ func (l *BaseLane) DefaultProcessLaneHandler() ProcessLaneHandler { return fmt.Errorf("transaction at index %d has a higher priority than %d", index, index-1) } - if _, err := l.AnteVerifyTx(ctx, tx, false); err != nil { + if err := l.VerifyTx(ctx, tx, false); err != nil { return fmt.Errorf("failed to verify tx: %w", err) } } diff --git a/block/mempool.go b/block/mempool.go index 4029c09..6845956 100644 --- a/block/mempool.go +++ b/block/mempool.go @@ -51,7 +51,7 @@ type ( // attempt to insert, remove transactions from all lanes it belongs to. It is recommended, // that mutex is set to true when creating the mempool. This will ensure that each // transaction cannot be inserted into the lanes before it. -func NewLanedMempool(logger log.Logger, mutex bool, lanes ...Lane) *LanedMempool { +func NewLanedMempool(logger log.Logger, mutex bool, lanes ...Lane) Mempool { mempool := &LanedMempool{ logger: logger, registry: lanes, diff --git a/block/proposals/proposals.go b/block/proposals/proposals.go index 4b3f5bf..0701ffb 100644 --- a/block/proposals/proposals.go +++ b/block/proposals/proposals.go @@ -20,6 +20,12 @@ type ( } ) +// NewProposalWithContext returns a new empty proposal. +func NewProposalWithContext(ctx sdk.Context, txEncoder sdk.TxEncoder) Proposal { + maxBlockSize, maxGasLimit := GetBlockLimits(ctx) + return NewProposal(txEncoder, maxBlockSize, maxGasLimit) +} + // NewProposal returns a new empty proposal. Any transactions added to the proposal // will be subject to the given max block size and max gas limit. func NewProposal(txEncoder sdk.TxEncoder, maxBlockSize int64, maxGasLimit uint64) Proposal { diff --git a/lanes/base/abci_test.go b/lanes/base/abci_test.go index cae064b..020b4ee 100644 --- a/lanes/base/abci_test.go +++ b/lanes/base/abci_test.go @@ -37,7 +37,7 @@ func (s *BaseTestSuite) TestPrepareLane() { tx: true, } lane := s.initLane(math.LegacyMustNewDecFromStr("0.5"), expectedExecution) - s.Require().NoError(lane.Insert(sdk.Context{}, tx)) + s.Require().NoError(lane.Insert(s.ctx, tx)) txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) s.Require().NoError(err) @@ -48,7 +48,7 @@ func (s *BaseTestSuite) TestPrepareLane() { 1, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is empty @@ -76,7 +76,7 @@ func (s *BaseTestSuite) TestPrepareLane() { lane := s.initLane(math.LegacyMustNewDecFromStr("0.5"), expectedExecution) // Insert the transaction into the lane - s.Require().NoError(lane.Insert(sdk.Context{}, tx)) + s.Require().NoError(lane.Insert(s.ctx, tx)) txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) s.Require().NoError(err) @@ -91,7 +91,7 @@ func (s *BaseTestSuite) TestPrepareLane() { limit.MaxGasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is empty @@ -119,7 +119,7 @@ func (s *BaseTestSuite) TestPrepareLane() { lane := s.initLane(math.LegacyMustNewDecFromStr("0.5"), expectedExecution) // Insert the transaction into the lane - s.Require().NoError(lane.Insert(sdk.Context{}, tx)) + s.Require().NoError(lane.Insert(s.ctx, tx)) txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) s.Require().NoError(err) @@ -135,7 +135,7 @@ func (s *BaseTestSuite) TestPrepareLane() { limit.MaxGasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is empty @@ -163,7 +163,7 @@ func (s *BaseTestSuite) TestPrepareLane() { } lane := s.initLane(math.LegacyOneDec(), expectedExecution) - s.Require().NoError(lane.Insert(sdk.Context{}, tx)) + s.Require().NoError(lane.Insert(s.ctx, tx)) txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) s.Require().NoError(err) @@ -178,7 +178,7 @@ func (s *BaseTestSuite) TestPrepareLane() { limit.MaxGasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is not empty and contains the transaction @@ -207,7 +207,7 @@ func (s *BaseTestSuite) TestPrepareLane() { } lane := s.initLane(math.LegacyOneDec(), expectedExecution) - s.Require().NoError(lane.Insert(sdk.Context{}, tx)) + s.Require().NoError(lane.Insert(s.ctx, tx)) txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) s.Require().NoError(err) @@ -218,7 +218,7 @@ func (s *BaseTestSuite) TestPrepareLane() { 10, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is empty @@ -260,8 +260,8 @@ func (s *BaseTestSuite) TestPrepareLane() { } lane := s.initLane(math.LegacyOneDec(), expectedExecution) - s.Require().NoError(lane.Insert(sdk.Context{}, tx1)) - s.Require().NoError(lane.Insert(sdk.Context{}, tx2)) + s.Require().NoError(lane.Insert(s.ctx, tx1)) + s.Require().NoError(lane.Insert(s.ctx, tx2)) txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1) s.Require().NoError(err) @@ -277,7 +277,7 @@ func (s *BaseTestSuite) TestPrepareLane() { gasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -316,8 +316,8 @@ func (s *BaseTestSuite) TestPrepareLane() { } lane := s.initLane(math.LegacyOneDec(), expectedExecution) - s.Require().NoError(lane.Insert(sdk.Context{}, tx1)) - s.Require().NoError(lane.Insert(sdk.Context{}, tx2)) + s.Require().NoError(lane.Insert(s.ctx, tx1)) + s.Require().NoError(lane.Insert(s.ctx, tx2)) txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1) s.Require().NoError(err) @@ -333,7 +333,7 @@ func (s *BaseTestSuite) TestPrepareLane() { gasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -375,8 +375,8 @@ func (s *BaseTestSuite) TestPrepareLane() { lane := s.initLane(math.LegacyOneDec(), expectedExecution) // Insert the transaction into the lane - s.Require().NoError(lane.Insert(sdk.Context{}.WithPriority(10), tx1)) - s.Require().NoError(lane.Insert(sdk.Context{}.WithPriority(5), tx2)) + s.Require().NoError(lane.Insert(s.ctx.WithPriority(10), tx1)) + s.Require().NoError(lane.Insert(s.ctx.WithPriority(5), tx2)) txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1) s.Require().NoError(err) @@ -392,7 +392,7 @@ func (s *BaseTestSuite) TestPrepareLane() { gasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -434,8 +434,8 @@ func (s *BaseTestSuite) TestPrepareLane() { lane := s.initLane(math.LegacyOneDec(), expectedExecution) // Insert the transaction into the lane - s.Require().NoError(lane.Insert(sdk.Context{}, tx1)) - s.Require().NoError(lane.Insert(sdk.Context{}, tx2)) + s.Require().NoError(lane.Insert(s.ctx, tx1)) + s.Require().NoError(lane.Insert(s.ctx, tx2)) txBz1, err := s.encodingConfig.TxConfig.TxEncoder()(tx1) s.Require().NoError(err) @@ -451,7 +451,7 @@ func (s *BaseTestSuite) TestPrepareLane() { gasLimit, ) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -481,7 +481,7 @@ func (s *BaseTestSuite) TestPrepareLane() { lane := s.initLane(math.LegacyOneDec(), expectedExecution) // Insert the transaction into the lane - s.Require().NoError(lane.Insert(sdk.Context{}, tx)) + s.Require().NoError(lane.Insert(s.ctx, tx)) txBz, err := s.encodingConfig.TxConfig.TxEncoder()(tx) s.Require().NoError(err) @@ -500,7 +500,7 @@ func (s *BaseTestSuite) TestPrepareLane() { err = emptyProposal.UpdateProposal(mockLane, []sdk.Tx{tx}) s.Require().NoError(err) - finalProposal, err := lane.PrepareLane(sdk.Context{}, emptyProposal, block.NoOpPrepareLanesHandler()) + finalProposal, err := lane.PrepareLane(s.ctx, emptyProposal, block.NoOpPrepareLanesHandler()) s.Require().NoError(err) // Ensure the proposal is ordered correctly @@ -543,7 +543,7 @@ func (s *BaseTestSuite) TestProcessLane() { 100000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().NoError(err) }) @@ -578,7 +578,7 @@ func (s *BaseTestSuite) TestProcessLane() { 100000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -636,7 +636,7 @@ func (s *BaseTestSuite) TestProcessLane() { 100000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -684,7 +684,7 @@ func (s *BaseTestSuite) TestProcessLane() { 100000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().NoError(err) }) @@ -732,7 +732,7 @@ func (s *BaseTestSuite) TestProcessLane() { 100000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -783,7 +783,7 @@ func (s *BaseTestSuite) TestProcessLane() { 100000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -818,7 +818,7 @@ func (s *BaseTestSuite) TestProcessLane() { 1000000, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -854,7 +854,7 @@ func (s *BaseTestSuite) TestProcessLane() { 9, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -901,7 +901,7 @@ func (s *BaseTestSuite) TestProcessLane() { 19, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) @@ -949,7 +949,7 @@ func (s *BaseTestSuite) TestProcessLane() { 20, ) - _, err = lane.ProcessLane(sdk.Context{}, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) + _, err = lane.ProcessLane(s.ctx, emptyProposal, partialProposal, block.NoOpProcessLanesHandler()) s.Require().Error(err) }) } diff --git a/lanes/base/base_test.go b/lanes/base/base_test.go index f4aad61..cf6c2ef 100644 --- a/lanes/base/base_test.go +++ b/lanes/base/base_test.go @@ -4,14 +4,19 @@ import ( "math/rand" "testing" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" testutils "github.com/skip-mev/block-sdk/testutils" + "github.com/skip-mev/block-sdk/x/auction/types" ) type BaseTestSuite struct { suite.Suite + ctx sdk.Context encodingConfig testutils.EncodingConfig random *rand.Rand accounts []testutils.Account @@ -30,4 +35,7 @@ func (s *BaseTestSuite) SetupTest() { s.random = rand.New(rand.NewSource(1)) s.accounts = testutils.RandomAccounts(s.random, 5) s.gasTokenDenom = "stake" + + key := storetypes.NewKVStoreKey(types.StoreKey) + s.ctx = testutil.DefaultContext(key, storetypes.NewTransientStoreKey("transient_key")) } diff --git a/lanes/mev/abci.go b/lanes/mev/abci.go index ecea336..574f01e 100644 --- a/lanes/mev/abci.go +++ b/lanes/mev/abci.go @@ -231,7 +231,7 @@ func (l *MEVLane) VerifyBidTx(ctx sdk.Context, bidTx sdk.Tx, bundle []sdk.Tx) er } // verify the top-level bid transaction - if ctx, err = l.AnteVerifyTx(ctx, bidTx, false); err != nil { + if err = l.VerifyTx(ctx, bidTx, false); err != nil { return fmt.Errorf("invalid bid tx; failed to execute ante handler: %w", err) } @@ -241,7 +241,7 @@ func (l *MEVLane) VerifyBidTx(ctx sdk.Context, bidTx sdk.Tx, bundle []sdk.Tx) er return fmt.Errorf("invalid bid tx; bundled tx is another bid transaction") } - if ctx, err = l.AnteVerifyTx(ctx, bundledTx, false); err != nil { + if err = l.VerifyTx(ctx, bundledTx, false); err != nil { return fmt.Errorf("invalid bid tx; failed to execute bundled transaction: %w", err) } } diff --git a/lanes/mev/abci_test.go b/lanes/mev/abci_test.go index 29dd86a..d1c8bec 100644 --- a/lanes/mev/abci_test.go +++ b/lanes/mev/abci_test.go @@ -10,6 +10,8 @@ import ( ) func (s *MEVTestSuite) TestPrepareLane() { + s.ctx = s.ctx.WithExecMode(sdk.ExecModePrepareProposal) + s.Run("can prepare a lane with no txs in mempool", func() { lane := s.initLane(math.LegacyOneDec(), nil) proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) @@ -220,6 +222,8 @@ func (s *MEVTestSuite) TestPrepareLane() { } func (s *MEVTestSuite) TestProcessLane() { + s.ctx = s.ctx.WithExecMode(sdk.ExecModeProcessProposal) + s.Run("can process an empty proposal", func() { lane := s.initLane(math.LegacyOneDec(), nil) proposal := proposals.NewProposal(s.encCfg.TxConfig.TxEncoder(), 200, 100) diff --git a/lanes/mev/mev_test.go b/lanes/mev/mev_test.go index 707fe78..effa9b5 100644 --- a/lanes/mev/mev_test.go +++ b/lanes/mev/mev_test.go @@ -41,7 +41,8 @@ func (s *MEVTestSuite) SetupTest() { s.encCfg = testutils.CreateTestEncodingConfig() s.config = mev.NewDefaultAuctionFactory(s.encCfg.TxConfig.TxDecoder(), signer_extraction.NewDefaultAdapter()) testCtx := testutil.DefaultContextWithDB(s.T(), storetypes.NewKVStoreKey("test"), storetypes.NewTransientStoreKey("transient_test")) - s.ctx = testCtx.Ctx.WithIsCheckTx(true) + s.ctx = testCtx.Ctx.WithExecMode(sdk.ExecModePrepareProposal) + s.ctx = s.ctx.WithBlockHeight(1) // Init accounts random := rand.New(rand.NewSource(time.Now().Unix()))