feat: x/blocksdk types and keeper outline (#187)

* module proto

* lane proto

* proto-gen

* proto-format

* regenerate

* genesis

* stub

* test

* comment

* new keeper functions

* finalize

* lint fix

* proto format

* encounteredLanes
This commit is contained in:
Alex Johnson 2023-11-03 13:42:23 -04:00 committed by GitHub
parent d451d7a167
commit ef654f5cbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 917 additions and 5 deletions

View File

@ -27,10 +27,11 @@ message Lane {
(gogoproto.nullable) = false
];
// order is the priority ordering of the Lane when processed in PrepareProposal
// and ProcessProposal. Lane orders should be set in order of priority starting from 0,
// monotonically increasing and non-overlapping. A lane with a lower order value
// will have a higher priority over a lane with a higher order value. For example,
// if LaneA has priority of 0 and LaneB has a priority of 1, LaneA has priority over LaneB.
// order is the priority ordering of the Lane when processed in
// PrepareProposal and ProcessProposal. Lane orders should be set in order of
// priority starting from 0, monotonically increasing and non-overlapping. A
// lane with a lower order value will have a higher priority over a lane with
// a higher order value. For example, if LaneA has priority of 0 and LaneB
// has a priority of 1, LaneA has priority over LaneB.
uint64 order = 3;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
package sdk.blocksdk.v1;
option go_package = "github.com/skip-mev/block-sdk/x/blocksdk/types";
import "gogoproto/gogo.proto";
import "sdk/blocksdk/v1/blocksdk.proto";
// GenesisState defines the genesis state of the x/blocksdk module.
message GenesisState {
// lanes is the list of all configured lanes at genesis time.
repeated Lane lanes = 1 [ (gogoproto.nullable) = false ];
}

View File

@ -0,0 +1,21 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/block-sdk/x/blocksdk/types"
)
// InitGenesis initializes the blocksdk module's state from a given genesis state.
func (k *Keeper) InitGenesis(ctx sdk.Context, gs types.GenesisState) {
for _, lane := range gs.Lanes {
k.setLane(ctx, lane)
}
}
// ExportGenesis returns a GenesisState for a given context.
func (k *Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
lanes := k.GetLanes(ctx)
return &types.GenesisState{Lanes: lanes}
}

104
x/blocksdk/keeper/keeper.go Normal file
View File

@ -0,0 +1,104 @@
package keeper
import (
"fmt"
"cosmossdk.io/log"
"cosmossdk.io/store/prefix"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/block-sdk/x/blocksdk/types"
)
type Keeper struct {
cdc codec.BinaryCodec
storeKey storetypes.StoreKey
// The address that is capable of executing a message.
// Typically, this will be the governance module's address.
authority string
}
// NewKeeper creates a new x/blocksdk keeper.
func NewKeeper(
cdc codec.BinaryCodec,
storeKey storetypes.StoreKey,
authority string,
) Keeper {
return Keeper{
cdc: cdc,
storeKey: storeKey,
authority: authority,
}
}
// Logger returns a blocksdk module-specific logger.
func (k *Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
}
// GetAuthority returns the address that is capable of executing a MsgUpdateParams message.
func (k *Keeper) GetAuthority() string {
return k.authority
}
// AddLane calls SetLane and provides additional stateful checks that the new
// set of lanes will be valid.
func (k *Keeper) AddLane(ctx sdk.Context, lane types.Lane) error {
currentLanes := k.GetLanes(ctx)
currentLanes = append(currentLanes, lane)
// validate new set of lanes
if err := types.Lanes(currentLanes).ValidateBasic(); err != nil {
return fmt.Errorf("new lane creates invalid lane configuration: %w", err)
}
k.setLane(ctx, lane)
return nil
}
// setLane sets a lane in the store.
func (k *Keeper) setLane(ctx sdk.Context, lane types.Lane) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyLanes)
b := k.cdc.MustMarshal(&lane)
store.Set([]byte(lane.Id), b)
}
// GetLane returns a lane by its id.
func (k *Keeper) GetLane(ctx sdk.Context, id string) (lane types.Lane, err error) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyLanes)
b := store.Get([]byte(id))
if b == nil {
return lane, fmt.Errorf("lane not found for ID %s", id)
}
k.cdc.MustUnmarshal(b, &lane)
return lane, nil
}
// GetLanes returns all lanes.
func (k *Keeper) GetLanes(ctx sdk.Context) (lanes []types.Lane) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyLanes)
iterator := storetypes.KVStorePrefixIterator(store, []byte{})
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var lane types.Lane
k.cdc.MustUnmarshal(iterator.Value(), &lane)
lanes = append(lanes, lane)
}
return
}
// DeleteLane deletes an Lane.
func (k *Keeper) DeleteLane(ctx sdk.Context, id string) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyLanes)
// Delete the Lane
store.Delete([]byte(id))
}

View File

@ -0,0 +1,129 @@
package keeper_test
import (
"testing"
"cosmossdk.io/math"
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/blocksdk/keeper"
"github.com/skip-mev/block-sdk/x/blocksdk/types"
)
type KeeperTestSuite struct {
suite.Suite
blocksdKeeper keeper.Keeper
encCfg testutils.EncodingConfig
ctx sdk.Context
// msgServer types.MsgServer
key *storetypes.KVStoreKey
authorityAccount sdk.AccAddress
}
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
func (s *KeeperTestSuite) SetupTest() {
s.encCfg = testutils.CreateTestEncodingConfig()
s.key = storetypes.NewKVStoreKey(types.StoreKey)
testCtx := testutil.DefaultContextWithDB(s.T(), s.key, storetypes.NewTransientStoreKey("transient_test"))
s.ctx = testCtx.Ctx
s.authorityAccount = []byte("authority")
s.blocksdKeeper = keeper.NewKeeper(
s.encCfg.Codec,
s.key,
s.authorityAccount.String(),
)
// s.msgServer = keeper.NewMsgServerImpl(s.blocksdKeeper)
}
func (s *KeeperTestSuite) TestLane() {
const (
validLaneID1 = "test1"
validLaneID2 = "test2"
validLaneID3 = "test3"
invalidLaneID = "invalid"
)
lanes := []types.Lane{
{
Id: validLaneID1,
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.10"),
Order: 0,
},
{
Id: validLaneID2,
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.10"),
Order: 1,
},
}
for _, lane := range lanes {
err := s.blocksdKeeper.AddLane(s.ctx, lane)
s.Require().NoError(err)
}
s.Run("get lane valid", func() {
gotLane, err := s.blocksdKeeper.GetLane(s.ctx, validLaneID1)
s.Require().NoError(err)
s.Require().Equal(lanes[0], gotLane)
})
s.Run("get lane invalid", func() {
_, err := s.blocksdKeeper.GetLane(s.ctx, invalidLaneID)
s.Require().Error(err)
})
s.Run("get lanes", func() {
gotLanes := s.blocksdKeeper.GetLanes(s.ctx)
s.Require().Equal(lanes, gotLanes)
})
s.Run("add invalid duplicate lane order", func() {
invalidLane := types.Lane{
Id: validLaneID3,
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.10"),
Order: 0,
}
err := s.blocksdKeeper.AddLane(s.ctx, invalidLane)
s.Require().Error(err)
})
s.Run("add invalid duplicate lane ID", func() {
invalidLane := types.Lane{
Id: validLaneID1,
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.10"),
Order: 2,
}
err := s.blocksdKeeper.AddLane(s.ctx, invalidLane)
s.Require().Error(err)
})
s.Run("add invalid non-monotonic order", func() {
invalidLane := types.Lane{
Id: validLaneID3,
MaxBlockSpace: math.LegacyMustNewDecFromStr("0.10"),
Order: 3,
}
err := s.blocksdKeeper.AddLane(s.ctx, invalidLane)
s.Require().Error(err)
})
s.Run("delete valid", func() {
s.blocksdKeeper.DeleteLane(s.ctx, validLaneID1)
_, err := s.blocksdKeeper.GetLane(s.ctx, validLaneID1)
s.Require().Error(err)
})
}

View File

@ -0,0 +1,34 @@
package types
import (
"encoding/json"
"github.com/cosmos/cosmos-sdk/codec"
)
// NewGenesisState creates a new GenesisState instance.
func NewGenesisState() *GenesisState {
return &GenesisState{}
}
// DefaultGenesisState returns the default GenesisState instance.
func DefaultGenesisState() *GenesisState {
return &GenesisState{}
}
// Validate performs basic validation of the blocksdk module genesis state.
func (gs GenesisState) Validate() error {
return Lanes(gs.Lanes).ValidateBasic()
}
// GetGenesisStateFromAppState returns x/blocksdk GenesisState given raw application
// genesis state.
func GetGenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessage) GenesisState {
var genesisState GenesisState
if appState[ModuleName] != nil {
cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState)
}
return genesisState
}

View File

@ -0,0 +1,331 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: sdk/blocksdk/v1/genesis.proto
package types
import (
fmt "fmt"
_ "github.com/cosmos/gogoproto/gogoproto"
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
// GenesisState defines the genesis state of the x/blocksdk module.
type GenesisState struct {
// lanes is the list of all configured lanes at genesis time.
Lanes []Lane `protobuf:"bytes,1,rep,name=lanes,proto3" json:"lanes"`
}
func (m *GenesisState) Reset() { *m = GenesisState{} }
func (m *GenesisState) String() string { return proto.CompactTextString(m) }
func (*GenesisState) ProtoMessage() {}
func (*GenesisState) Descriptor() ([]byte, []int) {
return fileDescriptor_b356885c34672c5f, []int{0}
}
func (m *GenesisState) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) {
xxx_messageInfo_GenesisState.Merge(m, src)
}
func (m *GenesisState) XXX_Size() int {
return m.Size()
}
func (m *GenesisState) XXX_DiscardUnknown() {
xxx_messageInfo_GenesisState.DiscardUnknown(m)
}
var xxx_messageInfo_GenesisState proto.InternalMessageInfo
func (m *GenesisState) GetLanes() []Lane {
if m != nil {
return m.Lanes
}
return nil
}
func init() {
proto.RegisterType((*GenesisState)(nil), "sdk.blocksdk.v1.GenesisState")
}
func init() { proto.RegisterFile("sdk/blocksdk/v1/genesis.proto", fileDescriptor_b356885c34672c5f) }
var fileDescriptor_b356885c34672c5f = []byte{
// 199 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2d, 0x4e, 0xc9, 0xd6,
0x4f, 0xca, 0xc9, 0x4f, 0xce, 0x06, 0x31, 0xca, 0x0c, 0xf5, 0xd3, 0x53, 0xf3, 0x52, 0x8b, 0x33,
0x8b, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0xf8, 0x8b, 0x53, 0xb2, 0xf5, 0x60, 0xd2, 0x7a,
0x65, 0x86, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x39, 0x7d, 0x10, 0x0b, 0xa2, 0x4c, 0x4a,
0x0e, 0xdd, 0x14, 0xb8, 0x16, 0xb0, 0xbc, 0x92, 0x23, 0x17, 0x8f, 0x3b, 0xc4, 0xdc, 0xe0, 0x92,
0xc4, 0x92, 0x54, 0x21, 0x43, 0x2e, 0xd6, 0x9c, 0xc4, 0xbc, 0xd4, 0x62, 0x09, 0x46, 0x05, 0x66,
0x0d, 0x6e, 0x23, 0x51, 0x3d, 0x34, 0x6b, 0xf4, 0x7c, 0x12, 0xf3, 0x52, 0x9d, 0x58, 0x4e, 0xdc,
0x93, 0x67, 0x08, 0x82, 0xa8, 0x74, 0xf2, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6,
0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39,
0x86, 0x28, 0xbd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xe2, 0xec,
0xcc, 0x02, 0xdd, 0xdc, 0xd4, 0x32, 0x88, 0x03, 0x74, 0x41, 0xae, 0xa9, 0x40, 0x38, 0xac, 0xa4,
0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0xec, 0x26, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x68,
0xfa, 0x64, 0x9d, 0xfb, 0x00, 0x00, 0x00,
}
func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Lanes) > 0 {
for iNdEx := len(m.Lanes) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Lanes[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenesis(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
offset -= sovGenesis(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *GenesisState) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Lanes) > 0 {
for _, e := range m.Lanes {
l = e.Size()
n += 1 + l + sovGenesis(uint64(l))
}
}
return n
}
func sovGenesis(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozGenesis(x uint64) (n int) {
return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *GenesisState) 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 ErrIntOverflowGenesis
}
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: GenesisState: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Lanes", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenesis
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenesis
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenesis
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Lanes = append(m.Lanes, Lane{})
if err := m.Lanes[len(m.Lanes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenesis(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenesis
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenesis(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, ErrIntOverflowGenesis
}
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, ErrIntOverflowGenesis
}
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, ErrIntOverflowGenesis
}
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, ErrInvalidLengthGenesis
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupGenesis
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthGenesis
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,4 @@
package types_test
// TODO add tests if genesis expands.
// Currently is fully covered by TestLanes_ValidateBasic

22
x/blocksdk/types/keys.go Normal file
View File

@ -0,0 +1,22 @@
package types
const (
// ModuleName is the name of the blocksdk module
ModuleName = "blocksdk"
// StoreKey is the default store key for the blocksdk module
StoreKey = ModuleName
// RouterKey is the message route for the blocksdk module
RouterKey = ModuleName
// QuerierRoute is the querier route for the blocksdk module
QuerierRoute = ModuleName
)
const (
prefixLanes = iota
)
// KeyLanes is the store key for the lanes.
var KeyLanes = []byte{prefixLanes}

66
x/blocksdk/types/lane.go Normal file
View File

@ -0,0 +1,66 @@
package types
import (
"fmt"
"cosmossdk.io/math"
)
// Lanes is a type alias for a slice of Lane objects.
type Lanes []Lane
// ValidateBasic performs stateless validation of a Lane.
func (l *Lane) ValidateBasic() error {
if l.Id == "" {
return fmt.Errorf("lane must have an id specified")
}
if l.MaxBlockSpace.IsNil() || l.MaxBlockSpace.IsNegative() || l.MaxBlockSpace.GT(math.LegacyOneDec()) {
return fmt.Errorf("max block space must be set to a value between 0 and 1")
}
return nil
}
// ValidateBasic performs stateless validation all Lanes. Performs lane.ValidateBasic()
// for each lane, verifies that order is monotonically increasing for all lanes, and
// checks that IDs are unique.
func (ls Lanes) ValidateBasic() error {
encounteredIDs := make(map[string]struct{})
encounteredOrders := make(map[uint64]struct{})
// validate each lane and check that ID and order fields are unique
for _, l := range ls {
if err := l.ValidateBasic(); err != nil {
return err
}
_, found := encounteredIDs[l.Id]
if found {
return fmt.Errorf("duplicate lane ID found: %s", l.Id)
}
encounteredIDs[l.Id] = struct{}{}
_, found = encounteredOrders[l.Order]
if found {
return fmt.Errorf("duplicate lane order found: %d", l.Order)
}
encounteredOrders[l.Order] = struct{}{}
}
// check if an order value is outside the expected range
// since all order values are already unique, if a value
// is greater than the total number of lanes it must break
// the rule of monotonicity
numLanes := uint64(len(ls))
for order := range encounteredOrders {
// subtract 1 since orders starts at 0
if order > numLanes-1 {
return fmt.Errorf("orders are not set in a monotonically increasing order: order value: %d", order)
}
}
return nil
}

View File

@ -0,0 +1,187 @@
package types_test
import (
"testing"
"cosmossdk.io/math"
"github.com/skip-mev/block-sdk/x/blocksdk/types"
)
func TestLane_ValidateBasic(t *testing.T) {
tests := []struct {
name string
lane types.Lane
wantErr bool
}{
{
name: "invalid no id",
lane: types.Lane{
Id: "",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
wantErr: true,
},
{
name: "invalid maxblockspace nil",
lane: types.Lane{
Id: "free",
Order: 0,
},
wantErr: true,
},
{
name: "invalid maxblockspace negative",
lane: types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyMustNewDecFromStr("-1.0"),
Order: 0,
},
wantErr: true,
},
{
name: "invalid maxblockspace greater than 1",
lane: types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyMustNewDecFromStr("1.1"),
Order: 0,
},
wantErr: true,
},
{
name: "valid",
lane: types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
wantErr: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if err := tc.lane.ValidateBasic(); (err != nil) != tc.wantErr {
t.Errorf("ValidateBasic() error = %v, wantErr %v", err, tc.wantErr)
}
})
}
}
func TestLanes_ValidateBasic(t *testing.T) {
tests := []struct {
name string
lanes types.Lanes
wantErr bool
}{
{
name: "invalid validate basic (no id)",
lanes: types.Lanes{
types.Lane{
Id: "",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
},
wantErr: true,
},
{
name: "invalid duplicate IDs",
lanes: types.Lanes{
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 1,
},
},
wantErr: true,
},
{
name: "invalid duplicate orders",
lanes: types.Lanes{
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
types.Lane{
Id: "mev",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
},
wantErr: true,
},
{
name: "invalid duplicate orders",
lanes: types.Lanes{
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
types.Lane{
Id: "mev",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
},
wantErr: true,
},
{
name: "invalid orders non-monotonic",
lanes: types.Lanes{
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
types.Lane{
Id: "mev",
MaxBlockSpace: math.LegacyOneDec(),
Order: 2,
},
},
wantErr: true,
},
{
name: "valid single",
lanes: types.Lanes{
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
},
wantErr: false,
},
{
name: "valid multiple",
lanes: types.Lanes{
types.Lane{
Id: "free",
MaxBlockSpace: math.LegacyOneDec(),
Order: 0,
},
types.Lane{
Id: "mev",
MaxBlockSpace: math.LegacyOneDec(),
Order: 1,
},
},
wantErr: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if err := tc.lanes.ValidateBasic(); (err != nil) != tc.wantErr {
t.Errorf("ValidateBasic() error = %v, wantErr %v", err, tc.wantErr)
}
})
}
}