Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor fi retriever instantiation to get correct start height
  • Loading branch information
julienrbrt committed Dec 19, 2025
commit 1f8f9630b5d0b3db67acd7e014b8e362e3168fbd
11 changes: 3 additions & 8 deletions apps/evm/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ var RunCmd = &cobra.Command{
}

// Create sequencer based on configuration
sequencer, err := createSequencer(context.Background(), logger, datastore, nodeConfig, genesis, daClient)
sequencer, err := createSequencer(logger, datastore, nodeConfig, genesis, daClient)
if err != nil {
return err
}
Expand Down Expand Up @@ -154,22 +154,20 @@ func init() {
// If BasedSequencer is enabled, it creates a based sequencer that fetches transactions from DA.
// Otherwise, it creates a single (traditional) sequencer.
func createSequencer(
ctx context.Context,
logger zerolog.Logger,
datastore datastore.Batching,
nodeConfig config.Config,
genesis genesis.Genesis,
daClient block.FullDAClient,
) (coresequencer.Sequencer, error) {
fiRetriever := block.NewForcedInclusionRetriever(daClient, genesis, datastore, logger)

if nodeConfig.Node.BasedSequencer {
// Based sequencer mode - fetch transactions only from DA
if !nodeConfig.Node.Aggregator {
return nil, fmt.Errorf("based sequencer mode requires aggregator mode to be enabled")
}

basedSeq, err := based.NewBasedSequencer(ctx, fiRetriever, datastore, genesis, logger)
fiRetriever := block.NewForcedInclusionRetriever(daClient, logger, genesis.DAStartHeight, genesis.DAEpochForcedInclusion)
basedSeq, err := based.NewBasedSequencer(fiRetriever, datastore, genesis, logger)
if err != nil {
return nil, fmt.Errorf("failed to create based sequencer: %w", err)
}
Expand All @@ -183,15 +181,12 @@ func createSequencer(
}

sequencer, err := single.NewSequencer(
ctx,
logger,
datastore,
daClient,
[]byte(genesis.ChainID),
nodeConfig.Node.BlockTime.Duration,
nodeConfig.Node.Aggregator,
1000,
fiRetriever,
genesis,
)
if err != nil {
Expand Down
7 changes: 2 additions & 5 deletions apps/grpc/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ func createSequencer(
}

daClient := block.NewDAClient(blobClient, nodeConfig, logger)
fiRetriever := block.NewForcedInclusionRetriever(daClient, genesis, datastore, logger)

if nodeConfig.Node.BasedSequencer {
// Based sequencer mode - fetch transactions only from DA
if !nodeConfig.Node.Aggregator {
return nil, fmt.Errorf("based sequencer mode requires aggregator mode to be enabled")
}

basedSeq, err := based.NewBasedSequencer(ctx, fiRetriever, datastore, genesis, logger)
fiRetriever := block.NewForcedInclusionRetriever(daClient, logger, genesis.DAStartHeight, genesis.DAEpochForcedInclusion)
basedSeq, err := based.NewBasedSequencer(fiRetriever, datastore, genesis, logger)
if err != nil {
return nil, fmt.Errorf("failed to create based sequencer: %w", err)
}
Expand All @@ -142,15 +142,12 @@ func createSequencer(
}

sequencer, err := single.NewSequencer(
ctx,
logger,
datastore,
daClient,
[]byte(genesis.ChainID),
nodeConfig.Node.BlockTime.Duration,
nodeConfig.Node.Aggregator,
1000,
fiRetriever,
genesis,
)
if err != nil {
Expand Down
7 changes: 2 additions & 5 deletions apps/testapp/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ func createSequencer(
}

daClient := block.NewDAClient(blobClient, nodeConfig, logger)
fiRetriever := block.NewForcedInclusionRetriever(daClient, genesis, datastore, logger)

if nodeConfig.Node.BasedSequencer {
// Based sequencer mode - fetch transactions only from DA
if !nodeConfig.Node.Aggregator {
return nil, fmt.Errorf("based sequencer mode requires aggregator mode to be enabled")
}

basedSeq, err := based.NewBasedSequencer(ctx, fiRetriever, datastore, genesis, logger)
fiRetriever := block.NewForcedInclusionRetriever(daClient, logger, genesis.DAStartHeight, genesis.DAEpochForcedInclusion)
basedSeq, err := based.NewBasedSequencer(fiRetriever, datastore, genesis, logger)
if err != nil {
return nil, fmt.Errorf("failed to create based sequencer: %w", err)
}
Expand All @@ -143,15 +143,12 @@ func createSequencer(
}

sequencer, err := single.NewSequencer(
ctx,
logger,
datastore,
daClient,
[]byte(genesis.ChainID),
nodeConfig.Node.BlockTime.Duration,
nodeConfig.Node.Aggregator,
1000,
fiRetriever,
genesis,
)
if err != nil {
Expand Down
35 changes: 8 additions & 27 deletions block/internal/da/forced_inclusion_retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ package da

import (
"context"
"encoding/binary"
"errors"
"fmt"
"time"

"github.com/rs/zerolog"

datypes "github.com/evstack/ev-node/pkg/da/types"
"github.com/evstack/ev-node/pkg/genesis"
"github.com/evstack/ev-node/pkg/store"
"github.com/evstack/ev-node/types"
)

Expand All @@ -22,7 +19,6 @@ var ErrForceInclusionNotConfigured = errors.New("forced inclusion namespace not
type ForcedInclusionRetriever struct {
client Client
logger zerolog.Logger
store store.Store
daEpochSize uint64
daStartHeight uint64
}
Expand All @@ -38,32 +34,27 @@ type ForcedInclusionEvent struct {
// NewForcedInclusionRetriever creates a new forced inclusion retriever.
func NewForcedInclusionRetriever(
client Client,
genesis genesis.Genesis,
store store.Store,
logger zerolog.Logger,
daStartHeight, daEpochSize uint64,
) *ForcedInclusionRetriever {
r := &ForcedInclusionRetriever{
client: client,
store: store,
logger: logger.With().Str("component", "forced_inclusion_retriever").Logger(),
daEpochSize: genesis.DAEpochForcedInclusion,
return &ForcedInclusionRetriever{
client: client,
logger: logger.With().Str("component", "forced_inclusion_retriever").Logger(),
daStartHeight: daStartHeight,
daEpochSize: daEpochSize,
}

// check for inclusion da height on store (sequencer) or genesis da height (full nodes)
r.daStartHeight = max(r.getInitialDAStartHeight(context.Background()), genesis.DAStartHeight)
return r
}

// RetrieveForcedIncludedTxs retrieves forced inclusion transactions at the given DA height.
// It respects epoch boundaries and only fetches at epoch start.
func (r *ForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) {
// when daStartHeight is not set or no namespace is configured, we retrieve nothing.
if !r.client.HasForcedInclusionNamespace() || r.daStartHeight == 0 {
if !r.client.HasForcedInclusionNamespace() {
return nil, ErrForceInclusionNotConfigured
}

if daHeight < r.daStartHeight {
return nil, ErrForceInclusionNotConfigured
return nil, fmt.Errorf("DA height %d is before the configured start height %d", daHeight, r.daStartHeight)
}

epochStart, epochEnd, currentEpochNumber := types.CalculateEpochBoundaries(daHeight, r.daStartHeight, r.daEpochSize)
Expand Down Expand Up @@ -184,13 +175,3 @@ func (r *ForcedInclusionRetriever) processForcedInclusionBlobs(

return nil
}

// getInitialDAStartHeight retrieves the DA height of the first included chain height from store.
func (r *ForcedInclusionRetriever) getInitialDAStartHeight(ctx context.Context) uint64 {
daIncludedHeightBytes, err := r.store.GetMetadata(ctx, store.GenesisDAHeightKey)
if err != nil || len(daIncludedHeightBytes) != 8 {
return 0
}

return binary.LittleEndian.Uint64(daIncludedHeightBytes)
}
43 changes: 8 additions & 35 deletions block/internal/da/forced_inclusion_retriever_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import (
"testing"
"time"

"github.com/ipfs/go-datastore"
dssync "github.com/ipfs/go-datastore/sync"
"github.com/rs/zerolog"
"github.com/stretchr/testify/mock"
"gotest.tools/v3/assert"

datypes "github.com/evstack/ev-node/pkg/da/types"
"github.com/evstack/ev-node/pkg/genesis"
"github.com/evstack/ev-node/pkg/store"
"github.com/evstack/ev-node/test/mocks"
)

Expand All @@ -27,10 +24,7 @@ func TestNewForcedInclusionRetriever(t *testing.T) {
DAEpochForcedInclusion: 10,
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
assert.Assert(t, retriever != nil)
}

Expand All @@ -43,10 +37,7 @@ func TestForcedInclusionRetriever_RetrieveForcedIncludedTxs_NoNamespace(t *testi
DAEpochForcedInclusion: 10,
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
ctx := context.Background()

_, err := retriever.RetrieveForcedIncludedTxs(ctx, 100)
Expand All @@ -65,10 +56,7 @@ func TestForcedInclusionRetriever_RetrieveForcedIncludedTxs_NotAtEpochStart(t *t
DAEpochForcedInclusion: 10,
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
ctx := context.Background()

// Height 105 is not an epoch start (100, 110, 120, etc. are epoch starts)
Expand Down Expand Up @@ -101,10 +89,7 @@ func TestForcedInclusionRetriever_RetrieveForcedIncludedTxs_EpochStartSuccess(t
DAEpochForcedInclusion: 1, // Single height epoch
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
ctx := context.Background()

// Height 100 is an epoch start
Expand All @@ -131,10 +116,7 @@ func TestForcedInclusionRetriever_RetrieveForcedIncludedTxs_EpochStartNotAvailab
DAEpochForcedInclusion: 10,
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
ctx := context.Background()

// Epoch boundaries: [100, 109] - retrieval happens at epoch end (109)
Expand All @@ -157,10 +139,7 @@ func TestForcedInclusionRetriever_RetrieveForcedIncludedTxs_NoBlobsAtHeight(t *t
DAEpochForcedInclusion: 1, // Single height epoch
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
ctx := context.Background()

event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100)
Expand Down Expand Up @@ -198,10 +177,7 @@ func TestForcedInclusionRetriever_RetrieveForcedIncludedTxs_MultiHeightEpoch(t *
DAEpochForcedInclusion: 3, // Epoch: 100-102
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)
ctx := context.Background()

// Epoch boundaries: [100, 102] - retrieval happens at epoch end (102)
Expand All @@ -225,10 +201,7 @@ func TestForcedInclusionRetriever_processForcedInclusionBlobs(t *testing.T) {
DAEpochForcedInclusion: 10,
}

ds := dssync.MutexWrap(datastore.NewMapDatastore())
st := store.New(ds)

retriever := NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
retriever := NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

tests := []struct {
name string
Expand Down
2 changes: 1 addition & 1 deletion block/internal/syncing/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (s *Syncer) Start(ctx context.Context) error {

// Initialize handlers
s.daRetriever = NewDARetriever(s.daClient, s.cache, s.genesis, s.logger)
s.fiRetriever = da.NewForcedInclusionRetriever(s.daClient, s.genesis, s.store, s.logger)
s.fiRetriever = da.NewForcedInclusionRetriever(s.daClient, s.logger, s.genesis.DAStartHeight, s.genesis.DAEpochForcedInclusion)
s.p2pHandler = NewP2PHandler(s.headerStore.Store(), s.dataStore.Store(), s.cache, s.genesis, s.logger)
if currentHeight, err := s.store.Height(s.ctx); err != nil {
s.logger.Error().Err(err).Msg("failed to set initial processed height for p2p handler")
Expand Down
16 changes: 8 additions & 8 deletions block/internal/syncing/syncer_forced_inclusion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ func TestVerifyForcedInclusionTxs_AllTransactionsIncluded(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(cfg.DA.ForcedInclusionNamespace)).Maybe()
client.On("HasForcedInclusionNamespace").Return(true).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -443,7 +443,7 @@ func TestVerifyForcedInclusionTxs_MissingTransactions(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(cfg.DA.ForcedInclusionNamespace)).Maybe()
client.On("HasForcedInclusionNamespace").Return(true).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -546,7 +546,7 @@ func TestVerifyForcedInclusionTxs_PartiallyIncluded(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(cfg.DA.ForcedInclusionNamespace)).Maybe()
client.On("HasForcedInclusionNamespace").Return(true).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -653,7 +653,7 @@ func TestVerifyForcedInclusionTxs_NoForcedTransactions(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(cfg.DA.ForcedInclusionNamespace)).Maybe()
client.On("HasForcedInclusionNamespace").Return(true).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -719,7 +719,7 @@ func TestVerifyForcedInclusionTxs_NamespaceNotConfigured(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(nil)).Maybe()
client.On("HasForcedInclusionNamespace").Return(false).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -784,7 +784,7 @@ func TestVerifyForcedInclusionTxs_DeferralWithinEpoch(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(cfg.DA.ForcedInclusionNamespace)).Maybe()
client.On("HasForcedInclusionNamespace").Return(true).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -907,7 +907,7 @@ func TestVerifyForcedInclusionTxs_MaliciousAfterEpochEnd(t *testing.T) {
client.On("GetForcedInclusionNamespace").Return([]byte(cfg.DA.ForcedInclusionNamespace)).Maybe()
client.On("HasForcedInclusionNamespace").Return(true).Maybe()
daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down Expand Up @@ -996,7 +996,7 @@ func TestVerifyForcedInclusionTxs_SmoothingExceedsEpoch(t *testing.T) {
client.On("HasForcedInclusionNamespace").Return(true).Maybe()

daRetriever := NewDARetriever(client, cm, gen, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, gen, st, zerolog.Nop())
fiRetriever := da.NewForcedInclusionRetriever(client, zerolog.Nop(), gen.DAStartHeight, gen.DAEpochForcedInclusion)

s := NewSyncer(
st,
Expand Down
Loading