Skip to content

Commit

Permalink
chore(verifytx): Updating VerifyTx to Cache between Transactions (#137)
Browse files Browse the repository at this point in the history
* updating mev lane with cleaner impl

* nit

* lint

* updating anteverifytx to verify tx

* nit

* ignoring first height

* tidy

(cherry picked from commit cbc0483)

# Conflicts:
#	abci/abci.go
#	abci/abci_test.go
#	lanes/base/base_test.go
#	lanes/mev/abci_test.go
#	lanes/mev/mev_test.go
  • Loading branch information
davidterpay authored and mergify[bot] committed Oct 5, 2023
1 parent 3976e73 commit 975341a
Show file tree
Hide file tree
Showing 12 changed files with 945 additions and 152 deletions.
140 changes: 32 additions & 108 deletions abci/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
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"
)

const (
Expand Down Expand Up @@ -52,7 +51,15 @@ func NewProposalHandler(
// a boundary on the number of bytes that can be included in the proposal and will include all
// valid transactions in the proposal (up to MaxBlockSize, MaxGasLimit).
func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
<<<<<<< HEAD

Check failure on line 54 in abci/abci.go

View workflow job for this annotation

GitHub Actions / golangci-lint

expected statement, found '<<' (typecheck)
return func(ctx sdk.Context, req abci.RequestPrepareProposal) (resp abci.ResponsePrepareProposal) {
=======

Check failure on line 56 in abci/abci.go

View workflow job for this annotation

GitHub Actions / golangci-lint

expected statement, found '==' (typecheck)
return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (resp *abci.ResponsePrepareProposal, err error) {
if req.Height <= 1 {
return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil
}

>>>>>>> cbc0483 (chore(verifytx): Updating VerifyTx to Cache between Transactions (#137))

Check failure on line 62 in abci/abci.go

View workflow job for this annotation

GitHub Actions / golangci-lint

expected statement, found '>>' (typecheck)
// In the case where there is a panic, we recover here and return an empty proposal.
defer func() {
if rec := recover(); rec != nil {
Expand All @@ -63,14 +70,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)}
Expand All @@ -87,13 +94,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,
Expand All @@ -108,7 +119,15 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
// lane will iteratively verify the transactions that it belong to it. If any lane fails to verify the
// transactions, then the proposal is rejected.
func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
<<<<<<< HEAD
return func(ctx sdk.Context, req abci.RequestProcessProposal) (resp abci.ResponseProcessProposal) {
=======

Check failure on line 124 in abci/abci.go

View workflow job for this annotation

GitHub Actions / golangci-lint

expected statement, found '==' (typecheck)
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
}

>>>>>>> cbc0483 (chore(verifytx): Updating VerifyTx to Cache between Transactions (#137))

Check failure on line 130 in abci/abci.go

View workflow job for this annotation

GitHub Actions / golangci-lint

expected statement, found '>>' (typecheck)
// In the case where any of the lanes panic, we recover here and return a reject status.
defer func() {
if rec := recover(); rec != nil {
Expand All @@ -127,13 +146,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}
Expand All @@ -149,101 +162,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}
}
}

// 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
}
Loading

0 comments on commit 975341a

Please sign in to comment.