block-sdk/block/proposals/update.go
David Terpay b9d6761776
feat(ABCI): New Proposal Struct with Associated Metadata (#126)
* new proto types for proposal info

* new proposal type

* nits

* lane input

* lint

* feat(ABCI): Deprecating `CheckOrderHandler` with new Proposal MetaData (#127)

* refactor without checkorder

* nits

* more nits

* lint

* nits

* feat(ABCI): Updating MEV lane to have no `CheckOrder` handler + testing (#128)

* updating mev lane

* nits

* preventing adding multiple bid txs in prepare

* update
2023-09-28 11:10:13 -04:00

114 lines
3.5 KiB
Go

package proposals
import (
"fmt"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/block-sdk/block/utils"
)
// Lane defines the contract interface for a lane.
type Lane interface {
GetMaxBlockSpace() math.LegacyDec
Name() string
}
// UpdateProposal updates the proposal with the given transactions and lane limits. There are a
// few invariants that are checked:
// 1. The total size of the proposal must be less than the maximum number of bytes allowed.
// 2. The total size of the partial proposal must be less than the maximum number of bytes allowed for
// the lane.
// 3. The total gas limit of the proposal must be less than the maximum gas limit allowed.
// 4. The total gas limit of the partial proposal must be less than the maximum gas limit allowed for
// the lane.
// 5. The lane must not have already prepared a partial proposal.
// 6. The transaction must not already be in the proposal.
func (p *Proposal) UpdateProposal(lane Lane, partialProposal []sdk.Tx) error {
if len(partialProposal) == 0 {
return nil
}
// invariant check: Ensure we have not already prepared a partial proposal for this lane.
if _, ok := p.Info.TxsByLane[lane.Name()]; ok {
return fmt.Errorf("lane %s already prepared a partial proposal", lane)
}
// Aggregate info from the transactions.
hashes := make(map[string]struct{})
txs := make([][]byte, len(partialProposal))
partialProposalSize := int64(0)
partialProposalGasLimit := uint64(0)
for index, tx := range partialProposal {
txInfo, err := utils.GetTxInfo(p.TxEncoder, tx)
if err != nil {
return fmt.Errorf("err retrieving transaction info: %s", err)
}
// invariant check: Ensure that the transaction is not already in the proposal.
if _, ok := p.Cache[txInfo.Hash]; ok {
return fmt.Errorf("transaction %s is already in the proposal", txInfo.Hash)
}
hashes[txInfo.Hash] = struct{}{}
partialProposalSize += txInfo.Size
partialProposalGasLimit += txInfo.GasLimit
txs[index] = txInfo.TxBytes
}
// invariant check: Ensure that the partial proposal is not too large.
limit := p.GetLaneLimits(lane.GetMaxBlockSpace())
if partialProposalSize > limit.MaxTxBytes {
return fmt.Errorf(
"partial proposal is too large: %d > %d",
partialProposalSize,
limit.MaxTxBytes,
)
}
// invariant check: Ensure that the partial proposal does not consume too much gas.
if partialProposalGasLimit > limit.MaxGasLimit {
return fmt.Errorf(
"partial proposal consumes too much gas: %d > %d",
partialProposalGasLimit,
limit.MaxGasLimit,
)
}
// invariant check: Ensure that the lane did not prepare a block proposal that is too large.
updatedSize := p.Info.BlockSize + partialProposalSize
if updatedSize > p.Info.MaxBlockSize {
return fmt.Errorf(
"block proposal is too large: %d > %d",
updatedSize,
p.Info.MaxBlockSize,
)
}
// invariant check: Ensure that the lane did not prepare a block proposal that consumes too much gas.
updatedGasLimit := p.Info.GasLimit + partialProposalGasLimit
if updatedGasLimit > p.Info.MaxGasLimit {
return fmt.Errorf(
"block proposal consumes too much gas: %d > %d",
updatedGasLimit,
p.Info.MaxGasLimit,
)
}
// Update the proposal.
p.Info.BlockSize = updatedSize
p.Info.GasLimit = updatedGasLimit
// Update the lane info.
p.Info.TxsByLane[lane.Name()] = uint64(len(partialProposal))
// Update the proposal.
p.Txs = append(p.Txs, txs...)
for hash := range hashes {
p.Cache[hash] = struct{}{}
}
return nil
}