Skip to content

Commit 554a195

Browse files
authored
feat: solo sequencer, testapp max blob size ldflags (#3235)
* feat: solo sequencer * refactor: set max blob size as ldflag * refactor: wire solo sequencer in testapp * updates * fix ldflag * cleanup api * better defaults * cleanup readme * fixes * nit * fix typo
1 parent 25892d7 commit 554a195

16 files changed

Lines changed: 504 additions & 49 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Changes
1313

14+
- Make it easier to override `DefaultMaxBlobSize` by ldflags [#3235](https://github.com/evstack/ev-node/pull/3235)
15+
- Add solo sequencer (simple in memory single sequencer without force inclusion) [#3235](https://github.com/evstack/ev-node/pull/3235)
1416
- Improve reaper to sustain txs burst better [#3236](https://github.com/evstack/ev-node/pull/3236)
1517

1618
## v1.1.0

apps/testapp/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ RUN go mod download && (cd apps/testapp && go mod download)
2727
COPY . .
2828

2929
WORKDIR /ev-node/apps/testapp
30-
RUN go build -o /go/bin/testapp .
30+
31+
# 125829120 = 120 MB
32+
RUN go build -ldflags "-X github.com/evstack/ev-node/block/internal/common.defaultMaxBlobSizeStr=125829120" -o /go/bin/testapp .
3133

3234
## prep the final image.
3335
#

apps/testapp/cmd/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func InitCmd() *cobra.Command {
3535
// we use load in order to parse all the flags
3636
cfg, _ := rollconf.Load(cmd)
3737
cfg.Node.Aggregator = aggregator
38-
cfg.Node.BlockTime = rollconf.DurationWrapper{Duration: 10 * time.Millisecond}
38+
cfg.Node.BlockTime = rollconf.DurationWrapper{Duration: 100 * time.Millisecond}
3939
if err := cfg.Validate(); err != nil {
4040
return fmt.Errorf("error validating config: %w", err)
4141
}

apps/testapp/cmd/root.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ const (
1212

1313
// flagKVEndpoint is the flag for the KV endpoint
1414
flagKVEndpoint = "kv-endpoint"
15+
// flagSoloSequencer is the flag to enable a solo sequencer
16+
flagSoloSequencer = "solo-sequencer"
1517
)
1618

1719
func init() {
1820
config.AddGlobalFlags(RootCmd, AppName)
1921
config.AddFlags(RunCmd)
20-
// Add the KV endpoint flag specifically to the RunCmd
22+
23+
// add more flags to RunCmd
2124
RunCmd.Flags().String(flagKVEndpoint, "", "Address and port for the KV executor HTTP server")
25+
RunCmd.Flags().Bool(flagSoloSequencer, false, "Enable Solo sequencer (instead of based sequencer or single sequencer)")
2226
}
2327

2428
// RootCmd is the root command for Evolve

apps/testapp/cmd/run.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/evstack/ev-node/pkg/p2p/key"
2323
"github.com/evstack/ev-node/pkg/sequencers/based"
2424
"github.com/evstack/ev-node/pkg/sequencers/single"
25+
"github.com/evstack/ev-node/pkg/sequencers/solo"
2526
"github.com/evstack/ev-node/pkg/store"
2627
)
2728

@@ -91,7 +92,7 @@ var RunCmd = &cobra.Command{
9192
}
9293

9394
// Create sequencer based on configuration
94-
sequencer, err := createSequencer(ctx, logger, datastore, nodeConfig, genesis, executor)
95+
sequencer, err := createSequencer(ctx, command, logger, datastore, nodeConfig, genesis, executor)
9596
if err != nil {
9697
return err
9798
}
@@ -105,12 +106,21 @@ var RunCmd = &cobra.Command{
105106
// Otherwise, it creates a single (traditional) sequencer.
106107
func createSequencer(
107108
ctx context.Context,
109+
cmd *cobra.Command,
108110
logger zerolog.Logger,
109111
datastore datastore.Batching,
110112
nodeConfig config.Config,
111113
genesis genesis.Genesis,
112114
executor execution.Executor,
113115
) (coresequencer.Sequencer, error) {
116+
if enabled, _ := cmd.Flags().GetBool(flagSoloSequencer); enabled {
117+
if nodeConfig.Node.BasedSequencer {
118+
return nil, fmt.Errorf("solo sequencer cannot be used with based")
119+
}
120+
121+
return solo.NewSoloSequencer(logger, []byte(genesis.ChainID), executor), nil
122+
}
123+
114124
blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "")
115125
if err != nil {
116126
return nil, fmt.Errorf("failed to create blob client: %w", err)

block/internal/common/consts.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
11
package common
22

3-
const DefaultMaxBlobSize = 5 * 1024 * 1024 // 5MB fallback blob size limit
3+
import "strconv"
4+
5+
// defaultMaxBlobSizeStr holds the string representation of the default blob
6+
// size limit. Override at link time via:
7+
//
8+
// go build -ldflags "-X github.com/evstack/ev-node/block/internal/common.defaultMaxBlobSizeStr=125829120"
9+
var defaultMaxBlobSizeStr = "5242880" // 5 MB
10+
11+
// DefaultMaxBlobSize is the max blob size limit used for blob submission.
12+
var DefaultMaxBlobSize uint64
13+
14+
func init() {
15+
v, err := strconv.ParseUint(defaultMaxBlobSizeStr, 10, 64)
16+
if err != nil || v == 0 {
17+
DefaultMaxBlobSize = 5 * 1024 * 1024 // 5 MB fallback
18+
return
19+
}
20+
DefaultMaxBlobSize = v
21+
}

block/internal/da/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ func extractBlobData(resp *blobrpc.SubscriptionResponse) [][]byte {
559559
continue
560560
}
561561
data := blob.Data()
562-
if len(data) == 0 || len(data) > common.DefaultMaxBlobSize {
562+
if len(data) == 0 || uint64(len(data)) > common.DefaultMaxBlobSize {
563563
continue
564564
}
565565
blobs = append(blobs, data)

block/internal/submitting/batching_strategy.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
type BatchingStrategy interface {
1313
// ShouldSubmit determines if a batch should be submitted based on the strategy
1414
// Returns true if submission should happen now
15-
ShouldSubmit(pendingCount uint64, totalSizeBeforeSig int, maxBlobSize int, timeSinceLastSubmit time.Duration) bool
15+
ShouldSubmit(pendingCount uint64, totalSizeBeforeSig uint64, maxBlobSize uint64, timeSinceLastSubmit time.Duration) bool
1616
}
1717

1818
// NewBatchingStrategy creates a batching strategy based on configuration
@@ -34,7 +34,7 @@ func NewBatchingStrategy(cfg config.DAConfig) (BatchingStrategy, error) {
3434
// ImmediateStrategy submits as soon as any items are available
3535
type ImmediateStrategy struct{}
3636

37-
func (s *ImmediateStrategy) ShouldSubmit(pendingCount uint64, totalSize int, maxBlobSize int, timeSinceLastSubmit time.Duration) bool {
37+
func (s *ImmediateStrategy) ShouldSubmit(pendingCount uint64, totalSize uint64, maxBlobSize uint64, timeSinceLastSubmit time.Duration) bool {
3838
return pendingCount > 0
3939
}
4040

@@ -57,12 +57,12 @@ func NewSizeBasedStrategy(sizeThreshold float64, minItems uint64) *SizeBasedStra
5757
}
5858
}
5959

60-
func (s *SizeBasedStrategy) ShouldSubmit(pendingCount uint64, totalSize int, maxBlobSize int, timeSinceLastSubmit time.Duration) bool {
60+
func (s *SizeBasedStrategy) ShouldSubmit(pendingCount uint64, totalSize uint64, maxBlobSize uint64, timeSinceLastSubmit time.Duration) bool {
6161
if pendingCount < s.minItems {
6262
return false
6363
}
6464

65-
threshold := int(float64(maxBlobSize) * s.sizeThreshold)
65+
threshold := uint64(float64(maxBlobSize) * s.sizeThreshold)
6666
return totalSize >= threshold
6767
}
6868

@@ -85,7 +85,7 @@ func NewTimeBasedStrategy(daBlockTime time.Duration, maxDelay time.Duration, min
8585
}
8686
}
8787

88-
func (s *TimeBasedStrategy) ShouldSubmit(pendingCount uint64, totalSize int, maxBlobSize int, timeSinceLastSubmit time.Duration) bool {
88+
func (s *TimeBasedStrategy) ShouldSubmit(pendingCount uint64, totalSize uint64, maxBlobSize uint64, timeSinceLastSubmit time.Duration) bool {
8989
if pendingCount < s.minItems {
9090
return false
9191
}
@@ -120,18 +120,16 @@ func NewAdaptiveStrategy(daBlockTime time.Duration, sizeThreshold float64, maxDe
120120
}
121121
}
122122

123-
func (s *AdaptiveStrategy) ShouldSubmit(pendingCount uint64, totalSize int, maxBlobSize int, timeSinceLastSubmit time.Duration) bool {
123+
func (s *AdaptiveStrategy) ShouldSubmit(pendingCount uint64, totalSize uint64, maxBlobSize uint64, timeSinceLastSubmit time.Duration) bool {
124124
if pendingCount < s.minItems {
125125
return false
126126
}
127127

128-
// Submit if we've reached the size threshold
129-
threshold := int(float64(maxBlobSize) * s.sizeThreshold)
128+
threshold := uint64(float64(maxBlobSize) * s.sizeThreshold)
130129
if totalSize >= threshold {
131130
return true
132131
}
133132

134-
// Submit if max delay has been reached
135133
if timeSinceLastSubmit >= s.maxDelay {
136134
return true
137135
}

block/internal/submitting/batching_strategy_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import (
1313

1414
func TestImmediateStrategy(t *testing.T) {
1515
strategy := &ImmediateStrategy{}
16+
maxBlobSize := common.DefaultMaxBlobSize
1617

1718
tests := []struct {
1819
name string
1920
pendingCount uint64
20-
totalSize int
21+
totalSize uint64
2122
expected bool
2223
}{
2324
{
@@ -42,7 +43,7 @@ func TestImmediateStrategy(t *testing.T) {
4243

4344
for _, tt := range tests {
4445
t.Run(tt.name, func(t *testing.T) {
45-
result := strategy.ShouldSubmit(tt.pendingCount, tt.totalSize, common.DefaultMaxBlobSize, 0)
46+
result := strategy.ShouldSubmit(tt.pendingCount, tt.totalSize, maxBlobSize, 0)
4647
assert.Equal(t, tt.expected, result)
4748
})
4849
}
@@ -56,7 +57,7 @@ func TestSizeBasedStrategy(t *testing.T) {
5657
sizeThreshold float64
5758
minItems uint64
5859
pendingCount uint64
59-
totalSize int
60+
totalSize uint64
6061
expectedSubmit bool
6162
}{
6263
{
@@ -80,15 +81,15 @@ func TestSizeBasedStrategy(t *testing.T) {
8081
sizeThreshold: 0.8,
8182
minItems: 1,
8283
pendingCount: 10,
83-
totalSize: int(float64(maxBlobSize) * 0.8), // 80% of max
84+
totalSize: uint64(float64(maxBlobSize) * 0.8), // 80% of max
8485
expectedSubmit: true,
8586
},
8687
{
8788
name: "above threshold",
8889
sizeThreshold: 0.8,
8990
minItems: 1,
9091
pendingCount: 20,
91-
totalSize: int(float64(maxBlobSize) * 0.875), // 87.5%
92+
totalSize: uint64(float64(maxBlobSize) * 0.875), // 87.5%
9293
expectedSubmit: true,
9394
},
9495
{
@@ -125,39 +126,39 @@ func TestTimeBasedStrategy(t *testing.T) {
125126
name string
126127
minItems uint64
127128
pendingCount uint64
128-
totalSize int
129+
totalSize uint64
129130
timeSinceLastSubmit time.Duration
130131
expectedSubmit bool
131132
}{
132133
{
133134
name: "below min items",
134135
minItems: 2,
135136
pendingCount: 1,
136-
totalSize: int(float64(maxBlobSize) * 0.2),
137+
totalSize: uint64(float64(maxBlobSize) * 0.2),
137138
timeSinceLastSubmit: 10 * time.Second,
138139
expectedSubmit: false,
139140
},
140141
{
141142
name: "before max delay",
142143
minItems: 1,
143144
pendingCount: 5,
144-
totalSize: int(float64(maxBlobSize) * 0.5),
145+
totalSize: uint64(float64(maxBlobSize) * 0.5),
145146
timeSinceLastSubmit: 3 * time.Second,
146147
expectedSubmit: false,
147148
},
148149
{
149150
name: "at max delay",
150151
minItems: 1,
151152
pendingCount: 3,
152-
totalSize: int(float64(maxBlobSize) * 0.4),
153+
totalSize: uint64(float64(maxBlobSize) * 0.4),
153154
timeSinceLastSubmit: 6 * time.Second,
154155
expectedSubmit: true,
155156
},
156157
{
157158
name: "after max delay",
158159
minItems: 1,
159160
pendingCount: 2,
160-
totalSize: int(float64(maxBlobSize) * 0.2),
161+
totalSize: uint64(float64(maxBlobSize) * 0.2),
161162
timeSinceLastSubmit: 10 * time.Second,
162163
expectedSubmit: true,
163164
},
@@ -181,7 +182,7 @@ func TestAdaptiveStrategy(t *testing.T) {
181182
name string
182183
minItems uint64
183184
pendingCount uint64
184-
totalSize int
185+
totalSize uint64
185186
timeSinceLastSubmit time.Duration
186187
expectedSubmit bool
187188
reason string
@@ -190,7 +191,7 @@ func TestAdaptiveStrategy(t *testing.T) {
190191
name: "below min items",
191192
minItems: 3,
192193
pendingCount: 2,
193-
totalSize: int(float64(maxBlobSize) * 0.875),
194+
totalSize: uint64(float64(maxBlobSize) * 0.875),
194195
timeSinceLastSubmit: 10 * time.Second,
195196
expectedSubmit: false,
196197
reason: "not enough items",
@@ -199,7 +200,7 @@ func TestAdaptiveStrategy(t *testing.T) {
199200
name: "size threshold reached",
200201
minItems: 1,
201202
pendingCount: 10,
202-
totalSize: int(float64(maxBlobSize) * 0.85), // 85%
203+
totalSize: uint64(float64(maxBlobSize) * 0.85), // 85%
203204
timeSinceLastSubmit: 1 * time.Second,
204205
expectedSubmit: true,
205206
reason: "size threshold met",
@@ -208,7 +209,7 @@ func TestAdaptiveStrategy(t *testing.T) {
208209
name: "time threshold reached",
209210
minItems: 1,
210211
pendingCount: 2,
211-
totalSize: int(float64(maxBlobSize) * 0.2), // Only 20%
212+
totalSize: uint64(float64(maxBlobSize) * 0.2), // Only 20%
212213
timeSinceLastSubmit: 7 * time.Second,
213214
expectedSubmit: true,
214215
reason: "time threshold met",
@@ -217,7 +218,7 @@ func TestAdaptiveStrategy(t *testing.T) {
217218
name: "neither threshold reached",
218219
minItems: 1,
219220
pendingCount: 5,
220-
totalSize: int(float64(maxBlobSize) * 0.5), // 50%
221+
totalSize: uint64(float64(maxBlobSize) * 0.5), // 50%
221222
timeSinceLastSubmit: 3 * time.Second,
222223
expectedSubmit: false,
223224
reason: "waiting for threshold",
@@ -226,7 +227,7 @@ func TestAdaptiveStrategy(t *testing.T) {
226227
name: "both thresholds reached",
227228
minItems: 1,
228229
pendingCount: 20,
229-
totalSize: int(float64(maxBlobSize) * 0.875), // 87.5%
230+
totalSize: uint64(float64(maxBlobSize) * 0.875), // 87.5%
230231
timeSinceLastSubmit: 10 * time.Second,
231232
expectedSubmit: true,
232233
reason: "both thresholds met",
@@ -305,10 +306,9 @@ func TestNewBatchingStrategy(t *testing.T) {
305306
}
306307

307308
func TestBatchingStrategiesComparison(t *testing.T) {
308-
// This test demonstrates how different strategies behave with the same input
309309
maxBlobSize := common.DefaultMaxBlobSize
310310
pendingCount := uint64(10)
311-
totalSize := maxBlobSize / 2 // 50% full
311+
totalSize := maxBlobSize / 2
312312
timeSinceLastSubmit := 3 * time.Second
313313

314314
immediate := &ImmediateStrategy{}

block/internal/submitting/da_submitter.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ type retryPolicy struct {
4242
MaxAttempts int
4343
MinBackoff time.Duration
4444
MaxBackoff time.Duration
45-
MaxBlobBytes int
45+
MaxBlobBytes uint64
4646
}
4747

4848
func defaultRetryPolicy(maxAttempts int, maxDuration time.Duration) retryPolicy {
@@ -581,7 +581,7 @@ func submitToDA[T any](
581581
if err != nil {
582582
s.logger.Error().
583583
Str("itemType", itemType).
584-
Int("maxBlobBytes", pol.MaxBlobBytes).
584+
Uint64("maxBlobBytes", pol.MaxBlobBytes).
585585
Err(err).
586586
Msg("CRITICAL: Unrecoverable error - item exceeds maximum blob size")
587587
return fmt.Errorf("unrecoverable error: no %s items fit within max blob size: %w", itemType, err)
@@ -644,7 +644,7 @@ func submitToDA[T any](
644644
if len(items) == 1 {
645645
s.logger.Error().
646646
Str("itemType", itemType).
647-
Int("maxBlobBytes", pol.MaxBlobBytes).
647+
Uint64("maxBlobBytes", pol.MaxBlobBytes).
648648
Msg("CRITICAL: Unrecoverable error - single item exceeds DA blob size limit")
649649
return fmt.Errorf("unrecoverable error: %w: single %s item exceeds DA blob size limit", common.ErrOversizedItem, itemType)
650650
}
@@ -690,11 +690,11 @@ func submitToDA[T any](
690690

691691
// limitBatchBySize returns a prefix of items whose total marshaled size does not exceed maxBytes.
692692
// If the first item exceeds maxBytes, it returns ErrOversizedItem which is unrecoverable.
693-
func limitBatchBySize[T any](items []T, marshaled [][]byte, maxBytes int) ([]T, [][]byte, error) {
694-
total := 0
693+
func limitBatchBySize[T any](items []T, marshaled [][]byte, maxBytes uint64) ([]T, [][]byte, error) {
694+
total := uint64(0)
695695
count := 0
696696
for i := range items {
697-
sz := len(marshaled[i])
697+
sz := uint64(len(marshaled[i]))
698698
if sz > maxBytes {
699699
if i == 0 {
700700
return nil, nil, fmt.Errorf("%w: item size %d exceeds max %d", common.ErrOversizedItem, sz, maxBytes)

0 commit comments

Comments
 (0)