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
This commit is contained in:
@@ -1,43 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type (
|
||||
// Lane defines the required API dependencies for the IgnoreDecorator. The ignore decorator
|
||||
// will check if a transaction belongs to a lane by calling the Match function.
|
||||
Lane interface {
|
||||
Match(ctx sdk.Context, tx sdk.Tx) bool
|
||||
}
|
||||
|
||||
// IgnoreDecorator is an AnteDecorator that wraps an existing AnteDecorator. It allows
|
||||
// for the AnteDecorator to be ignored for specified lanes.
|
||||
IgnoreDecorator struct {
|
||||
decorator sdk.AnteDecorator
|
||||
lanes []Lane
|
||||
}
|
||||
)
|
||||
|
||||
// NewIgnoreDecorator returns a new IgnoreDecorator instance.
|
||||
func NewIgnoreDecorator(decorator sdk.AnteDecorator, lanes ...Lane) *IgnoreDecorator {
|
||||
return &IgnoreDecorator{
|
||||
decorator: decorator,
|
||||
lanes: lanes,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle implements the sdk.AnteDecorator interface. If the transaction belongs to
|
||||
// one of the lanes, the next AnteHandler is called. Otherwise, the decorator's AnteHandler
|
||||
// is called.
|
||||
func (sd IgnoreDecorator) AnteHandle(
|
||||
ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler,
|
||||
) (sdk.Context, error) {
|
||||
for _, lane := range sd.lanes {
|
||||
if lane.Match(ctx, tx) {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
}
|
||||
|
||||
return sd.decorator.AnteHandle(ctx, tx, simulate, next)
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
block "github.com/skip-mev/block-sdk/block"
|
||||
|
||||
log "cosmossdk.io/log"
|
||||
|
||||
math "cosmossdk.io/math"
|
||||
|
||||
mempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Lane is an autogenerated mock type for the Lane type
|
||||
type Lane struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CheckOrder provides a mock function with given fields: ctx, txs
|
||||
func (_m *Lane) CheckOrder(ctx types.Context, txs []types.Tx) error {
|
||||
ret := _m.Called(ctx, txs)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx) error); ok {
|
||||
r0 = rf(ctx, txs)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Compare provides a mock function with given fields: ctx, this, other
|
||||
func (_m *Lane) Compare(ctx types.Context, this types.Tx, other types.Tx) int {
|
||||
ret := _m.Called(ctx, this, other)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(types.Context, types.Tx, types.Tx) int); ok {
|
||||
r0 = rf(ctx, this, other)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Contains provides a mock function with given fields: tx
|
||||
func (_m *Lane) Contains(tx types.Tx) bool {
|
||||
ret := _m.Called(tx)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) bool); ok {
|
||||
r0 = rf(tx)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CountTx provides a mock function with given fields:
|
||||
func (_m *Lane) CountTx() int {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func() int); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetMaxBlockSpace provides a mock function with given fields:
|
||||
func (_m *Lane) GetMaxBlockSpace() math.LegacyDec {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 math.LegacyDec
|
||||
if rf, ok := ret.Get(0).(func() math.LegacyDec); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(math.LegacyDec)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Insert provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Lane) Insert(_a0 context.Context, _a1 types.Tx) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Logger provides a mock function with given fields:
|
||||
func (_m *Lane) Logger() log.Logger {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 log.Logger
|
||||
if rf, ok := ret.Get(0).(func() log.Logger); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(log.Logger)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Match provides a mock function with given fields: ctx, tx
|
||||
func (_m *Lane) Match(ctx types.Context, tx types.Tx) bool {
|
||||
ret := _m.Called(ctx, tx)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Context, types.Tx) bool); ok {
|
||||
r0 = rf(ctx, tx)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *Lane) Name() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PrepareLane provides a mock function with given fields: ctx, proposal, maxTxBytes, next
|
||||
func (_m *Lane) PrepareLane(ctx types.Context, proposal block.BlockProposal, maxTxBytes int64, next block.PrepareLanesHandler) (block.BlockProposal, error) {
|
||||
ret := _m.Called(ctx, proposal, maxTxBytes, next)
|
||||
|
||||
var r0 block.BlockProposal
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) (block.BlockProposal, error)); ok {
|
||||
return rf(ctx, proposal, maxTxBytes, next)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) block.BlockProposal); ok {
|
||||
r0 = rf(ctx, proposal, maxTxBytes, next)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(block.BlockProposal)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) error); ok {
|
||||
r1 = rf(ctx, proposal, maxTxBytes, next)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ProcessLane provides a mock function with given fields: ctx, proposalTxs, next
|
||||
func (_m *Lane) ProcessLane(ctx types.Context, proposalTxs []types.Tx, next block.ProcessLanesHandler) (types.Context, error) {
|
||||
ret := _m.Called(ctx, proposalTxs, next)
|
||||
|
||||
var r0 types.Context
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, block.ProcessLanesHandler) (types.Context, error)); ok {
|
||||
return rf(ctx, proposalTxs, next)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, block.ProcessLanesHandler) types.Context); ok {
|
||||
r0 = rf(ctx, proposalTxs, next)
|
||||
} else {
|
||||
r0 = ret.Get(0).(types.Context)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(types.Context, []types.Tx, block.ProcessLanesHandler) error); ok {
|
||||
r1 = rf(ctx, proposalTxs, next)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Remove provides a mock function with given fields: _a0
|
||||
func (_m *Lane) Remove(_a0 types.Tx) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Select provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Lane) Select(_a0 context.Context, _a1 [][]byte) mempool.Iterator {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 mempool.Iterator
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [][]byte) mempool.Iterator); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(mempool.Iterator)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetAnteHandler provides a mock function with given fields: antehander
|
||||
func (_m *Lane) SetAnteHandler(antehander types.AnteHandler) {
|
||||
_m.Called(antehander)
|
||||
}
|
||||
|
||||
// SetIgnoreList provides a mock function with given fields: ignoreList
|
||||
func (_m *Lane) SetIgnoreList(ignoreList []block.Lane) {
|
||||
_m.Called(ignoreList)
|
||||
}
|
||||
|
||||
// NewLane creates a new instance of Lane. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewLane(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
},
|
||||
) *Lane {
|
||||
mock := &Lane{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
// Code generated by mockery v2.30.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// LaneMempool is an autogenerated mock type for the LaneMempool type
|
||||
type LaneMempool struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Compare provides a mock function with given fields: ctx, this, other
|
||||
func (_m *LaneMempool) Compare(ctx types.Context, this types.Tx, other types.Tx) int {
|
||||
ret := _m.Called(ctx, this, other)
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func(types.Context, types.Tx, types.Tx) int); ok {
|
||||
r0 = rf(ctx, this, other)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Contains provides a mock function with given fields: tx
|
||||
func (_m *LaneMempool) Contains(tx types.Tx) bool {
|
||||
ret := _m.Called(tx)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) bool); ok {
|
||||
r0 = rf(tx)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CountTx provides a mock function with given fields:
|
||||
func (_m *LaneMempool) CountTx() int {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 int
|
||||
if rf, ok := ret.Get(0).(func() int); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(int)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Insert provides a mock function with given fields: _a0, _a1
|
||||
func (_m *LaneMempool) Insert(_a0 context.Context, _a1 types.Tx) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Remove provides a mock function with given fields: _a0
|
||||
func (_m *LaneMempool) Remove(_a0 types.Tx) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(types.Tx) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Select provides a mock function with given fields: _a0, _a1
|
||||
func (_m *LaneMempool) Select(_a0 context.Context, _a1 [][]byte) mempool.Iterator {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 mempool.Iterator
|
||||
if rf, ok := ret.Get(0).(func(context.Context, [][]byte) mempool.Iterator); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(mempool.Iterator)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewLaneMempool creates a new instance of LaneMempool. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewLaneMempool(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
},
|
||||
) *LaneMempool {
|
||||
mock := &LaneMempool{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
+45
-26
@@ -5,23 +5,47 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
|
||||
)
|
||||
|
||||
// GetTxHashStr returns the hex-encoded hash of the transaction alongside the
|
||||
// transaction bytes.
|
||||
func GetTxHashStr(txEncoder sdk.TxEncoder, tx sdk.Tx) ([]byte, string, error) {
|
||||
type (
|
||||
// TxInfo contains the information required for a transaction to be
|
||||
// included in a proposal.
|
||||
TxInfo struct {
|
||||
// Hash is the hex-encoded hash of the transaction.
|
||||
Hash string
|
||||
// Size is the size of the transaction in bytes.
|
||||
Size int64
|
||||
// GasLimit is the gas limit of the transaction.
|
||||
GasLimit uint64
|
||||
// TxBytes is the bytes of the transaction.
|
||||
TxBytes []byte
|
||||
}
|
||||
)
|
||||
|
||||
// GetTxHashStr returns the TxInfo of a given transaction.
|
||||
func GetTxInfo(txEncoder sdk.TxEncoder, tx sdk.Tx) (TxInfo, error) {
|
||||
txBz, err := txEncoder(tx)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to encode transaction: %w", err)
|
||||
return TxInfo{}, fmt.Errorf("failed to encode transaction: %w", err)
|
||||
}
|
||||
|
||||
txHash := sha256.Sum256(txBz)
|
||||
txHashStr := hex.EncodeToString(txHash[:])
|
||||
|
||||
return txBz, txHashStr, nil
|
||||
// TODO: Add an adapter to lanes so that this can be flexible to support EVM, etc.
|
||||
gasTx, ok := tx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return TxInfo{}, fmt.Errorf("failed to cast transaction to GasTx")
|
||||
}
|
||||
|
||||
return TxInfo{
|
||||
Hash: txHashStr,
|
||||
Size: int64(len(txBz)),
|
||||
GasLimit: gasTx.GetGas(),
|
||||
TxBytes: txBz,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetDecodedTxs returns the decoded transactions from the given bytes.
|
||||
@@ -39,6 +63,21 @@ func GetDecodedTxs(txDecoder sdk.TxDecoder, txs [][]byte) ([]sdk.Tx, error) {
|
||||
return decodedTxs, nil
|
||||
}
|
||||
|
||||
// GetEncodedTxs returns the encoded transactions from the given bytes.
|
||||
func GetEncodedTxs(txEncoder sdk.TxEncoder, txs []sdk.Tx) ([][]byte, error) {
|
||||
var encodedTxs [][]byte
|
||||
for _, tx := range txs {
|
||||
txBz, err := txEncoder(tx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode transaction: %w", err)
|
||||
}
|
||||
|
||||
encodedTxs = append(encodedTxs, txBz)
|
||||
}
|
||||
|
||||
return encodedTxs, nil
|
||||
}
|
||||
|
||||
// RemoveTxsFromLane removes the transactions from the given lane's mempool.
|
||||
func RemoveTxsFromLane(txs []sdk.Tx, mempool sdkmempool.Mempool) error {
|
||||
for _, tx := range txs {
|
||||
@@ -49,23 +88,3 @@ func RemoveTxsFromLane(txs []sdk.Tx, mempool sdkmempool.Mempool) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMaxTxBytesForLane returns the maximum number of bytes that can be included in the proposal
|
||||
// for the given lane.
|
||||
func GetMaxTxBytesForLane(maxTxBytes, totalTxBytes int64, ratio math.LegacyDec) int64 {
|
||||
// In the case where the ratio is zero, we return the max tx bytes remaining. Note, the only
|
||||
// lane that should have a ratio of zero is the default lane. This means the default lane
|
||||
// will have no limit on the number of transactions it can include in a block and is only
|
||||
// limited by the maxTxBytes included in the PrepareProposalRequest.
|
||||
if ratio.IsZero() {
|
||||
remainder := maxTxBytes - totalTxBytes
|
||||
if remainder < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return remainder
|
||||
}
|
||||
|
||||
// Otherwise, we calculate the max tx bytes for the lane based on the ratio.
|
||||
return ratio.MulInt64(maxTxBytes).TruncateInt().Int64()
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
|
||||
"github.com/skip-mev/block-sdk/block/utils"
|
||||
)
|
||||
|
||||
func TestGetMaxTxBytesForLane(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
maxTxBytes int64
|
||||
totalTxBytes int64
|
||||
ratio math.LegacyDec
|
||||
expected int64
|
||||
}{
|
||||
{
|
||||
"ratio is zero",
|
||||
100,
|
||||
50,
|
||||
math.LegacyZeroDec(),
|
||||
50,
|
||||
},
|
||||
{
|
||||
"ratio is zero",
|
||||
100,
|
||||
100,
|
||||
math.LegacyZeroDec(),
|
||||
0,
|
||||
},
|
||||
{
|
||||
"ratio is zero",
|
||||
100,
|
||||
150,
|
||||
math.LegacyZeroDec(),
|
||||
0,
|
||||
},
|
||||
{
|
||||
"ratio is 1",
|
||||
100,
|
||||
50,
|
||||
math.LegacyOneDec(),
|
||||
100,
|
||||
},
|
||||
{
|
||||
"ratio is 10%",
|
||||
100,
|
||||
50,
|
||||
math.LegacyMustNewDecFromStr("0.1"),
|
||||
10,
|
||||
},
|
||||
{
|
||||
"ratio is 25%",
|
||||
100,
|
||||
50,
|
||||
math.LegacyMustNewDecFromStr("0.25"),
|
||||
25,
|
||||
},
|
||||
{
|
||||
"ratio is 50%",
|
||||
101,
|
||||
50,
|
||||
math.LegacyMustNewDecFromStr("0.5"),
|
||||
50,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := utils.GetMaxTxBytesForLane(tc.maxTxBytes, tc.totalTxBytes, tc.ratio)
|
||||
if actual != tc.expected {
|
||||
t.Errorf("expected %d, got %d", tc.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user