From dc52948a516a7c07764fba47871db516f1feddeb Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Wed, 19 Nov 2025 20:33:30 +0100 Subject: [PATCH 1/6] add exchange wrapper to first check da store for height before requesting from p2p --- node/full.go | 10 +++--- node/light.go | 6 ++-- pkg/sync/exchange_wrapper.go | 58 +++++++++++++++++++++++++++++++++++ pkg/sync/sync_service.go | 30 +++++++++++++----- pkg/sync/sync_service_test.go | 7 +++-- 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 pkg/sync/exchange_wrapper.go diff --git a/node/full.go b/node/full.go index 2097c24b59..39000fd6f0 100644 --- a/node/full.go +++ b/node/full.go @@ -87,12 +87,12 @@ func newFullNode( mainKV := newPrefixKV(database, EvPrefix) rktStore := store.New(mainKV) - headerSyncService, err := initHeaderSyncService(mainKV, nodeConfig, genesis, p2pClient, logger) + headerSyncService, err := initHeaderSyncService(mainKV, rktStore, nodeConfig, genesis, p2pClient, logger) if err != nil { return nil, err } - dataSyncService, err := initDataSyncService(mainKV, nodeConfig, genesis, p2pClient, logger) + dataSyncService, err := initDataSyncService(mainKV, rktStore, nodeConfig, genesis, p2pClient, logger) if err != nil { return nil, err } @@ -149,6 +149,7 @@ func newFullNode( func initHeaderSyncService( mainKV ds.Batching, + daStore store.Store, nodeConfig config.Config, genesis genesispkg.Genesis, p2pClient *p2p.Client, @@ -156,7 +157,7 @@ func initHeaderSyncService( ) (*evsync.HeaderSyncService, error) { componentLogger := logger.With().Str("component", "HeaderSyncService").Logger() - headerSyncService, err := evsync.NewHeaderSyncService(mainKV, nodeConfig, genesis, p2pClient, componentLogger) + headerSyncService, err := evsync.NewHeaderSyncService(mainKV, daStore, nodeConfig, genesis, p2pClient, componentLogger) if err != nil { return nil, fmt.Errorf("error while initializing HeaderSyncService: %w", err) } @@ -165,6 +166,7 @@ func initHeaderSyncService( func initDataSyncService( mainKV ds.Batching, + daStore store.Store, nodeConfig config.Config, genesis genesispkg.Genesis, p2pClient *p2p.Client, @@ -172,7 +174,7 @@ func initDataSyncService( ) (*evsync.DataSyncService, error) { componentLogger := logger.With().Str("component", "DataSyncService").Logger() - dataSyncService, err := evsync.NewDataSyncService(mainKV, nodeConfig, genesis, p2pClient, componentLogger) + dataSyncService, err := evsync.NewDataSyncService(mainKV, daStore, nodeConfig, genesis, p2pClient, componentLogger) if err != nil { return nil, fmt.Errorf("error while initializing DataSyncService: %w", err) } diff --git a/node/light.go b/node/light.go index 4b1d1cc7ed..dfb966014f 100644 --- a/node/light.go +++ b/node/light.go @@ -43,13 +43,13 @@ func newLightNode( logger zerolog.Logger, ) (ln *LightNode, err error) { componentLogger := logger.With().Str("component", "HeaderSyncService").Logger() - headerSyncService, err := sync.NewHeaderSyncService(database, conf, genesis, p2pClient, componentLogger) + store := store.New(database) + + headerSyncService, err := sync.NewHeaderSyncService(database, store, conf, genesis, p2pClient, componentLogger) if err != nil { return nil, fmt.Errorf("error while initializing HeaderSyncService: %w", err) } - store := store.New(database) - node := &LightNode{ P2P: p2pClient, hSyncService: headerSyncService, diff --git a/pkg/sync/exchange_wrapper.go b/pkg/sync/exchange_wrapper.go new file mode 100644 index 0000000000..7c52a836f8 --- /dev/null +++ b/pkg/sync/exchange_wrapper.go @@ -0,0 +1,58 @@ +package sync + +import ( + "context" + + "github.com/celestiaorg/go-header" + "github.com/evstack/ev-node/pkg/store" + "github.com/evstack/ev-node/types" +) + +type exchangeWrapper[H header.Header[H]] struct { + header.Exchange[H] + daStore store.Store +} + +func (ew *exchangeWrapper[H]) Get(ctx context.Context, hash header.Hash) (H, error) { + // Check DA store first + var zero H + if ew.daStore != nil { + switch any(zero).(type) { + case *types.SignedHeader: + h, _, err := ew.daStore.GetBlockByHash(ctx, hash) + if err == nil && h != nil { + return any(h).(H), nil + } + case *types.Data: + _, d, err := ew.daStore.GetBlockByHash(ctx, hash) + if err == nil && d != nil { + return any(d).(H), nil + } + } + } + + // Fallback to network exchange + return ew.Exchange.Get(ctx, hash) +} + +func (ew *exchangeWrapper[H]) GetByHeight(ctx context.Context, height uint64) (H, error) { + // Check DA store first + var zero H + if ew.daStore != nil { + switch any(zero).(type) { + case *types.SignedHeader: + h, _, err := ew.daStore.GetBlockData(ctx, height) + if err == nil && h != nil { + return any(h).(H), nil + } + case *types.Data: + _, d, err := ew.daStore.GetBlockData(ctx, height) + if err == nil && d != nil { + return any(d).(H), nil + } + } + } + + // Fallback to network exchange + return ew.Exchange.GetByHeight(ctx, height) +} diff --git a/pkg/sync/sync_service.go b/pkg/sync/sync_service.go index ba808e366c..8276e67eb3 100644 --- a/pkg/sync/sync_service.go +++ b/pkg/sync/sync_service.go @@ -23,6 +23,7 @@ import ( "github.com/evstack/ev-node/pkg/config" "github.com/evstack/ev-node/pkg/genesis" "github.com/evstack/ev-node/pkg/p2p" + "github.com/evstack/ev-node/pkg/store" "github.com/evstack/ev-node/types" ) @@ -48,10 +49,12 @@ type SyncService[H header.Header[H]] struct { p2p *p2p.Client - ex *goheaderp2p.Exchange[H] + ex header.Exchange[H] + p2pExchange *goheaderp2p.Exchange[H] sub *goheaderp2p.Subscriber[H] p2pServer *goheaderp2p.ExchangeServer[H] store *goheaderstore.Store[H] + daStore store.Store syncer *goheadersync.Syncer[H] syncerStatus *SyncerStatus topicSubscription header.Subscription[H] @@ -66,27 +69,30 @@ type HeaderSyncService = SyncService[*types.SignedHeader] // NewDataSyncService returns a new DataSyncService. func NewDataSyncService( store ds.Batching, + daStore store.Store, conf config.Config, genesis genesis.Genesis, p2p *p2p.Client, logger zerolog.Logger, ) (*DataSyncService, error) { - return newSyncService[*types.Data](store, dataSync, conf, genesis, p2p, logger) + return newSyncService[*types.Data](store, daStore, dataSync, conf, genesis, p2p, logger) } // NewHeaderSyncService returns a new HeaderSyncService. func NewHeaderSyncService( store ds.Batching, + daStore store.Store, conf config.Config, genesis genesis.Genesis, p2p *p2p.Client, logger zerolog.Logger, ) (*HeaderSyncService, error) { - return newSyncService[*types.SignedHeader](store, headerSync, conf, genesis, p2p, logger) + return newSyncService[*types.SignedHeader](store, daStore, headerSync, conf, genesis, p2p, logger) } func newSyncService[H header.Header[H]]( - store ds.Batching, + dsStore ds.Batching, + daStore store.Store, syncType syncType, conf config.Config, genesis genesis.Genesis, @@ -98,7 +104,7 @@ func newSyncService[H header.Header[H]]( } ss, err := goheaderstore.NewStore[H]( - store, + dsStore, goheaderstore.WithStorePrefix(string(syncType)), goheaderstore.WithMetrics(), ) @@ -111,6 +117,7 @@ func newSyncService[H header.Header[H]]( genesis: genesis, p2p: p2p, store: ss, + daStore: daStore, syncType: syncType, logger: logger, syncerStatus: new(SyncerStatus), @@ -276,12 +283,19 @@ func (syncService *SyncService[H]) setupP2PInfrastructure(ctx context.Context) ( peerIDs := syncService.getPeerIDs() - if syncService.ex, err = newP2PExchange[H](syncService.p2p.Host(), peerIDs, networkID, syncService.genesis.ChainID, syncService.p2p.ConnectionGater()); err != nil { + if syncService.p2pExchange, err = newP2PExchange[H](syncService.p2p.Host(), peerIDs, networkID, syncService.genesis.ChainID, syncService.p2p.ConnectionGater()); err != nil { return nil, fmt.Errorf("error while creating exchange: %w", err) } - if err := syncService.ex.Start(ctx); err != nil { + if err := syncService.p2pExchange.Start(ctx); err != nil { return nil, fmt.Errorf("error while starting exchange: %w", err) } + + // Wrap the exchange with the DA store check + syncService.ex = &exchangeWrapper[H]{ + Exchange: syncService.p2pExchange, + daStore: syncService.daStore, + } + return peerIDs, nil } @@ -375,7 +389,7 @@ func (syncService *SyncService[H]) Stop(ctx context.Context) error { syncService.topicSubscription.Cancel() err := errors.Join( syncService.p2pServer.Stop(ctx), - syncService.ex.Stop(ctx), + syncService.p2pExchange.Stop(ctx), syncService.sub.Stop(ctx), ) if syncService.syncerStatus.isStarted() { diff --git a/pkg/sync/sync_service_test.go b/pkg/sync/sync_service_test.go index 8086c0849e..50753cbb12 100644 --- a/pkg/sync/sync_service_test.go +++ b/pkg/sync/sync_service_test.go @@ -21,6 +21,8 @@ import ( mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + + "github.com/evstack/ev-node/pkg/store" ) func TestHeaderSyncServiceRestart(t *testing.T) { @@ -58,7 +60,8 @@ func TestHeaderSyncServiceRestart(t *testing.T) { defer cancel() require.NoError(t, p2pClient.Start(ctx)) - svc, err := NewHeaderSyncService(mainKV, conf, genesisDoc, p2pClient, logger) + rktStore := store.New(mainKV) + svc, err := NewHeaderSyncService(mainKV, rktStore, conf, genesisDoc, p2pClient, logger) require.NoError(t, err) err = svc.Start(ctx) require.NoError(t, err) @@ -98,7 +101,7 @@ func TestHeaderSyncServiceRestart(t *testing.T) { require.NoError(t, err) t.Cleanup(func() { _ = p2pClient.Close() }) - svc, err = NewHeaderSyncService(mainKV, conf, genesisDoc, p2pClient, logger) + svc, err = NewHeaderSyncService(mainKV, rktStore, conf, genesisDoc, p2pClient, logger) require.NoError(t, err) err = svc.Start(ctx) require.NoError(t, err) From 630e51f89f3a229858993d196ea807823835ef7b Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Thu, 20 Nov 2025 12:27:03 +0100 Subject: [PATCH 2/6] add tests and remove type switching --- .mockery.yaml | 5 + pkg/sync/exchange_wrapper.go | 40 ++-- pkg/sync/exchange_wrapper_test.go | 116 +++++++++++ pkg/sync/sync_service.go | 52 +++-- test/mocks/external/hexchange.go | 330 ++++++++++++++++++++++++++++++ 5 files changed, 501 insertions(+), 42 deletions(-) create mode 100644 pkg/sync/exchange_wrapper_test.go create mode 100644 test/mocks/external/hexchange.go diff --git a/.mockery.yaml b/.mockery.yaml index 2694096012..878a98bf4b 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -41,6 +41,11 @@ packages: filename: store.go github.com/celestiaorg/go-header: interfaces: + Exchange: + config: + dir: ./test/mocks + pkgname: mocks + filename: external/hexchange.go Store: config: dir: ./test/mocks diff --git a/pkg/sync/exchange_wrapper.go b/pkg/sync/exchange_wrapper.go index 7c52a836f8..ec574b627d 100644 --- a/pkg/sync/exchange_wrapper.go +++ b/pkg/sync/exchange_wrapper.go @@ -5,29 +5,23 @@ import ( "github.com/celestiaorg/go-header" "github.com/evstack/ev-node/pkg/store" - "github.com/evstack/ev-node/types" ) +type storeGetter[H header.Header[H]] func(context.Context, store.Store, header.Hash) (H, error) +type storeGetterByHeight[H header.Header[H]] func(context.Context, store.Store, uint64) (H, error) + type exchangeWrapper[H header.Header[H]] struct { header.Exchange[H] - daStore store.Store + daStore store.Store + getter storeGetter[H] + getterByHeight storeGetterByHeight[H] } func (ew *exchangeWrapper[H]) Get(ctx context.Context, hash header.Hash) (H, error) { // Check DA store first - var zero H - if ew.daStore != nil { - switch any(zero).(type) { - case *types.SignedHeader: - h, _, err := ew.daStore.GetBlockByHash(ctx, hash) - if err == nil && h != nil { - return any(h).(H), nil - } - case *types.Data: - _, d, err := ew.daStore.GetBlockByHash(ctx, hash) - if err == nil && d != nil { - return any(d).(H), nil - } + if ew.daStore != nil && ew.getter != nil { + if h, err := ew.getter(ctx, ew.daStore, hash); err == nil && !h.IsZero() { + return h, nil } } @@ -37,19 +31,9 @@ func (ew *exchangeWrapper[H]) Get(ctx context.Context, hash header.Hash) (H, err func (ew *exchangeWrapper[H]) GetByHeight(ctx context.Context, height uint64) (H, error) { // Check DA store first - var zero H - if ew.daStore != nil { - switch any(zero).(type) { - case *types.SignedHeader: - h, _, err := ew.daStore.GetBlockData(ctx, height) - if err == nil && h != nil { - return any(h).(H), nil - } - case *types.Data: - _, d, err := ew.daStore.GetBlockData(ctx, height) - if err == nil && d != nil { - return any(d).(H), nil - } + if ew.daStore != nil && ew.getterByHeight != nil { + if h, err := ew.getterByHeight(ctx, ew.daStore, height); err == nil && !h.IsZero() { + return h, nil } } diff --git a/pkg/sync/exchange_wrapper_test.go b/pkg/sync/exchange_wrapper_test.go new file mode 100644 index 0000000000..f6efc8bf24 --- /dev/null +++ b/pkg/sync/exchange_wrapper_test.go @@ -0,0 +1,116 @@ +package sync + +import ( + "context" + "errors" + "testing" + + "github.com/celestiaorg/go-header" + "github.com/evstack/ev-node/pkg/store" + "github.com/evstack/ev-node/test/mocks" + extmocks "github.com/evstack/ev-node/test/mocks/external" + "github.com/evstack/ev-node/types" + "github.com/stretchr/testify/assert" +) + +func TestExchangeWrapper_Get(t *testing.T) { + ctx := context.Background() + hash := header.Hash([]byte("test-hash")) + expectedHeader := &types.SignedHeader{} // Just a dummy + + t.Run("Hit in Store", func(t *testing.T) { + mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + // Exchange should NOT be called + + getter := func(ctx context.Context, s store.Store, h header.Hash) (*types.SignedHeader, error) { + return expectedHeader, nil + } + + ew := &exchangeWrapper[*types.SignedHeader]{ + Exchange: mockEx, + daStore: mocks.NewMockStore(t), + getter: getter, + } + + h, err := ew.Get(ctx, hash) + assert.NoError(t, err) + assert.Equal(t, expectedHeader, h) + }) + + t.Run("Miss in Store", func(t *testing.T) { + mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + mockEx.On("Get", ctx, hash).Return(expectedHeader, nil) + + getter := func(ctx context.Context, s store.Store, h header.Hash) (*types.SignedHeader, error) { + return nil, errors.New("not found") + } + + ew := &exchangeWrapper[*types.SignedHeader]{ + Exchange: mockEx, + daStore: mocks.NewMockStore(t), + getter: getter, + } + + h, err := ew.Get(ctx, hash) + assert.NoError(t, err) + assert.Equal(t, expectedHeader, h) + }) + + t.Run("Store Not Configured", func(t *testing.T) { + mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + mockEx.On("Get", ctx, hash).Return(expectedHeader, nil) + + ew := &exchangeWrapper[*types.SignedHeader]{ + Exchange: mockEx, + daStore: nil, // No store + getter: nil, + } + + h, err := ew.Get(ctx, hash) + assert.NoError(t, err) + assert.Equal(t, expectedHeader, h) + }) +} + +func TestExchangeWrapper_GetByHeight(t *testing.T) { + ctx := context.Background() + height := uint64(10) + expectedHeader := &types.SignedHeader{} + + t.Run("Hit in Store", func(t *testing.T) { + mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + + getterByHeight := func(ctx context.Context, s store.Store, h uint64) (*types.SignedHeader, error) { + return expectedHeader, nil + } + + ew := &exchangeWrapper[*types.SignedHeader]{ + Exchange: mockEx, + daStore: mocks.NewMockStore(t), + getterByHeight: getterByHeight, + } + + h, err := ew.GetByHeight(ctx, height) + assert.NoError(t, err) + assert.Equal(t, expectedHeader, h) + }) + + t.Run("Miss in Store", func(t *testing.T) { + mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + mockEx.On("GetByHeight", ctx, height).Return(expectedHeader, nil) + + getterByHeight := func(ctx context.Context, s store.Store, h uint64) (*types.SignedHeader, error) { + return nil, errors.New("not found") + } + + ew := &exchangeWrapper[*types.SignedHeader]{ + Exchange: mockEx, + daStore: mocks.NewMockStore(t), + getterByHeight: getterByHeight, + } + + h, err := ew.GetByHeight(ctx, height) + assert.NoError(t, err) + assert.Equal(t, expectedHeader, h) + }) +} diff --git a/pkg/sync/sync_service.go b/pkg/sync/sync_service.go index 8276e67eb3..c4cd716949 100644 --- a/pkg/sync/sync_service.go +++ b/pkg/sync/sync_service.go @@ -58,6 +58,9 @@ type SyncService[H header.Header[H]] struct { syncer *goheadersync.Syncer[H] syncerStatus *SyncerStatus topicSubscription header.Subscription[H] + + getter storeGetter[H] + getterByHeight storeGetterByHeight[H] } // DataSyncService is the P2P Sync Service for blocks. @@ -68,31 +71,48 @@ type HeaderSyncService = SyncService[*types.SignedHeader] // NewDataSyncService returns a new DataSyncService. func NewDataSyncService( - store ds.Batching, + dsStore ds.Batching, daStore store.Store, conf config.Config, genesis genesis.Genesis, p2p *p2p.Client, logger zerolog.Logger, ) (*DataSyncService, error) { - return newSyncService[*types.Data](store, daStore, dataSync, conf, genesis, p2p, logger) + getter := func(ctx context.Context, s store.Store, hash header.Hash) (*types.Data, error) { + _, d, err := s.GetBlockByHash(ctx, hash) + return d, err + } + getterByHeight := func(ctx context.Context, s store.Store, height uint64) (*types.Data, error) { + _, d, err := s.GetBlockData(ctx, height) + return d, err + } + return newSyncService[*types.Data](dsStore, daStore, getter, getterByHeight, dataSync, conf, genesis, p2p, logger) } // NewHeaderSyncService returns a new HeaderSyncService. func NewHeaderSyncService( - store ds.Batching, + dsStore ds.Batching, daStore store.Store, conf config.Config, genesis genesis.Genesis, p2p *p2p.Client, logger zerolog.Logger, ) (*HeaderSyncService, error) { - return newSyncService[*types.SignedHeader](store, daStore, headerSync, conf, genesis, p2p, logger) + getter := func(ctx context.Context, s store.Store, hash header.Hash) (*types.SignedHeader, error) { + h, _, err := s.GetBlockByHash(ctx, hash) + return h, err + } + getterByHeight := func(ctx context.Context, s store.Store, height uint64) (*types.SignedHeader, error) { + return s.GetHeader(ctx, height) + } + return newSyncService[*types.SignedHeader](dsStore, daStore, getter, getterByHeight, headerSync, conf, genesis, p2p, logger) } func newSyncService[H header.Header[H]]( dsStore ds.Batching, daStore store.Store, + getter storeGetter[H], + getterByHeight storeGetterByHeight[H], syncType syncType, conf config.Config, genesis genesis.Genesis, @@ -113,14 +133,16 @@ func newSyncService[H header.Header[H]]( } svc := &SyncService[H]{ - conf: conf, - genesis: genesis, - p2p: p2p, - store: ss, - daStore: daStore, - syncType: syncType, - logger: logger, - syncerStatus: new(SyncerStatus), + conf: conf, + genesis: genesis, + p2p: p2p, + store: ss, + daStore: daStore, + getter: getter, + getterByHeight: getterByHeight, + syncType: syncType, + logger: logger, + syncerStatus: new(SyncerStatus), } return svc, nil @@ -292,8 +314,10 @@ func (syncService *SyncService[H]) setupP2PInfrastructure(ctx context.Context) ( // Wrap the exchange with the DA store check syncService.ex = &exchangeWrapper[H]{ - Exchange: syncService.p2pExchange, - daStore: syncService.daStore, + Exchange: syncService.p2pExchange, + daStore: syncService.daStore, + getter: syncService.getter, + getterByHeight: syncService.getterByHeight, } return peerIDs, nil diff --git a/test/mocks/external/hexchange.go b/test/mocks/external/hexchange.go new file mode 100644 index 0000000000..cfa5729e5d --- /dev/null +++ b/test/mocks/external/hexchange.go @@ -0,0 +1,330 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + "github.com/celestiaorg/go-header" + mock "github.com/stretchr/testify/mock" +) + +// NewMockExchange creates a new instance of MockExchange. 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 NewMockExchange[H header.Header[H]](t interface { + mock.TestingT + Cleanup(func()) +}) *MockExchange[H] { + mock := &MockExchange[H]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockExchange is an autogenerated mock type for the Exchange type +type MockExchange[H header.Header[H]] struct { + mock.Mock +} + +type MockExchange_Expecter[H header.Header[H]] struct { + mock *mock.Mock +} + +func (_m *MockExchange[H]) EXPECT() *MockExchange_Expecter[H] { + return &MockExchange_Expecter[H]{mock: &_m.Mock} +} + +// Get provides a mock function for the type MockExchange +func (_mock *MockExchange[H]) Get(context1 context.Context, hash header.Hash) (H, error) { + ret := _mock.Called(context1, hash) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, header.Hash) (H, error)); ok { + return returnFunc(context1, hash) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, header.Hash) H); ok { + r0 = returnFunc(context1, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, header.Hash) error); ok { + r1 = returnFunc(context1, hash) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockExchange_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockExchange_Get_Call[H header.Header[H]] struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - context1 context.Context +// - hash header.Hash +func (_e *MockExchange_Expecter[H]) Get(context1 interface{}, hash interface{}) *MockExchange_Get_Call[H] { + return &MockExchange_Get_Call[H]{Call: _e.mock.On("Get", context1, hash)} +} + +func (_c *MockExchange_Get_Call[H]) Run(run func(context1 context.Context, hash header.Hash)) *MockExchange_Get_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 header.Hash + if args[1] != nil { + arg1 = args[1].(header.Hash) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockExchange_Get_Call[H]) Return(v H, err error) *MockExchange_Get_Call[H] { + _c.Call.Return(v, err) + return _c +} + +func (_c *MockExchange_Get_Call[H]) RunAndReturn(run func(context1 context.Context, hash header.Hash) (H, error)) *MockExchange_Get_Call[H] { + _c.Call.Return(run) + return _c +} + +// GetByHeight provides a mock function for the type MockExchange +func (_mock *MockExchange[H]) GetByHeight(context1 context.Context, v uint64) (H, error) { + ret := _mock.Called(context1, v) + + if len(ret) == 0 { + panic("no return value specified for GetByHeight") + } + + var r0 H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uint64) (H, error)); ok { + return returnFunc(context1, v) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, uint64) H); ok { + r0 = returnFunc(context1, v) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, uint64) error); ok { + r1 = returnFunc(context1, v) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockExchange_GetByHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByHeight' +type MockExchange_GetByHeight_Call[H header.Header[H]] struct { + *mock.Call +} + +// GetByHeight is a helper method to define mock.On call +// - context1 context.Context +// - v uint64 +func (_e *MockExchange_Expecter[H]) GetByHeight(context1 interface{}, v interface{}) *MockExchange_GetByHeight_Call[H] { + return &MockExchange_GetByHeight_Call[H]{Call: _e.mock.On("GetByHeight", context1, v)} +} + +func (_c *MockExchange_GetByHeight_Call[H]) Run(run func(context1 context.Context, v uint64)) *MockExchange_GetByHeight_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uint64 + if args[1] != nil { + arg1 = args[1].(uint64) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockExchange_GetByHeight_Call[H]) Return(v1 H, err error) *MockExchange_GetByHeight_Call[H] { + _c.Call.Return(v1, err) + return _c +} + +func (_c *MockExchange_GetByHeight_Call[H]) RunAndReturn(run func(context1 context.Context, v uint64) (H, error)) *MockExchange_GetByHeight_Call[H] { + _c.Call.Return(run) + return _c +} + +// GetRangeByHeight provides a mock function for the type MockExchange +func (_mock *MockExchange[H]) GetRangeByHeight(ctx context.Context, from H, to uint64) ([]H, error) { + ret := _mock.Called(ctx, from, to) + + if len(ret) == 0 { + panic("no return value specified for GetRangeByHeight") + } + + var r0 []H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, H, uint64) ([]H, error)); ok { + return returnFunc(ctx, from, to) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, H, uint64) []H); ok { + r0 = returnFunc(ctx, from, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, H, uint64) error); ok { + r1 = returnFunc(ctx, from, to) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockExchange_GetRangeByHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRangeByHeight' +type MockExchange_GetRangeByHeight_Call[H header.Header[H]] struct { + *mock.Call +} + +// GetRangeByHeight is a helper method to define mock.On call +// - ctx context.Context +// - from H +// - to uint64 +func (_e *MockExchange_Expecter[H]) GetRangeByHeight(ctx interface{}, from interface{}, to interface{}) *MockExchange_GetRangeByHeight_Call[H] { + return &MockExchange_GetRangeByHeight_Call[H]{Call: _e.mock.On("GetRangeByHeight", ctx, from, to)} +} + +func (_c *MockExchange_GetRangeByHeight_Call[H]) Run(run func(ctx context.Context, from H, to uint64)) *MockExchange_GetRangeByHeight_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 H + if args[1] != nil { + arg1 = args[1].(H) + } + var arg2 uint64 + if args[2] != nil { + arg2 = args[2].(uint64) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MockExchange_GetRangeByHeight_Call[H]) Return(vs []H, err error) *MockExchange_GetRangeByHeight_Call[H] { + _c.Call.Return(vs, err) + return _c +} + +func (_c *MockExchange_GetRangeByHeight_Call[H]) RunAndReturn(run func(ctx context.Context, from H, to uint64) ([]H, error)) *MockExchange_GetRangeByHeight_Call[H] { + _c.Call.Return(run) + return _c +} + +// Head provides a mock function for the type MockExchange +func (_mock *MockExchange[H]) Head(context1 context.Context, headOptions ...header.HeadOption[H]) (H, error) { + // header.HeadOption[H] + _va := make([]interface{}, len(headOptions)) + for _i := range headOptions { + _va[_i] = headOptions[_i] + } + var _ca []interface{} + _ca = append(_ca, context1) + _ca = append(_ca, _va...) + ret := _mock.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Head") + } + + var r0 H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, ...header.HeadOption[H]) (H, error)); ok { + return returnFunc(context1, headOptions...) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, ...header.HeadOption[H]) H); ok { + r0 = returnFunc(context1, headOptions...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, ...header.HeadOption[H]) error); ok { + r1 = returnFunc(context1, headOptions...) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockExchange_Head_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Head' +type MockExchange_Head_Call[H header.Header[H]] struct { + *mock.Call +} + +// Head is a helper method to define mock.On call +// - context1 context.Context +// - headOptions ...header.HeadOption[H] +func (_e *MockExchange_Expecter[H]) Head(context1 interface{}, headOptions ...interface{}) *MockExchange_Head_Call[H] { + return &MockExchange_Head_Call[H]{Call: _e.mock.On("Head", + append([]interface{}{context1}, headOptions...)...)} +} + +func (_c *MockExchange_Head_Call[H]) Run(run func(context1 context.Context, headOptions ...header.HeadOption[H])) *MockExchange_Head_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 []header.HeadOption[H] + variadicArgs := make([]header.HeadOption[H], len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(header.HeadOption[H]) + } + } + arg1 = variadicArgs + run( + arg0, + arg1..., + ) + }) + return _c +} + +func (_c *MockExchange_Head_Call[H]) Return(v H, err error) *MockExchange_Head_Call[H] { + _c.Call.Return(v, err) + return _c +} + +func (_c *MockExchange_Head_Call[H]) RunAndReturn(run func(context1 context.Context, headOptions ...header.HeadOption[H]) (H, error)) *MockExchange_Head_Call[H] { + _c.Call.Return(run) + return _c +} From ae8e0a2292222a9d79d0da6396b25bdd84661b03 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Thu, 20 Nov 2025 12:52:51 +0100 Subject: [PATCH 3/6] fix tests --- pkg/sync/sync_service_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/sync/sync_service_test.go b/pkg/sync/sync_service_test.go index a3a52c361d..cd434bc7b9 100644 --- a/pkg/sync/sync_service_test.go +++ b/pkg/sync/sync_service_test.go @@ -152,7 +152,8 @@ func TestHeaderSyncServiceInitFromHigherHeight(t *testing.T) { require.NoError(t, p2pClient.Start(ctx)) t.Cleanup(func() { _ = p2pClient.Close() }) - svc, err := NewHeaderSyncService(mainKV, conf, genesisDoc, p2pClient, logger) + rktStore := store.New(mainKV) + svc, err := NewHeaderSyncService(mainKV, rktStore, conf, genesisDoc, p2pClient, logger) require.NoError(t, err) require.NoError(t, svc.Start(ctx)) t.Cleanup(func() { _ = svc.Stop(context.Background()) }) From 210ffcc3758e33e229a110dd797d4c14baeb8fc3 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Thu, 20 Nov 2025 13:21:07 +0100 Subject: [PATCH 4/6] lint --- pkg/sync/sync_service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/sync/sync_service.go b/pkg/sync/sync_service.go index 54893eb25d..52905c52db 100644 --- a/pkg/sync/sync_service.go +++ b/pkg/sync/sync_service.go @@ -59,9 +59,9 @@ type SyncService[H header.Header[H]] struct { syncerStatus *SyncerStatus topicSubscription header.Subscription[H] - getter storeGetter[H] - getterByHeight storeGetterByHeight[H] - storeInitialized atomic.Bool + getter storeGetter[H] + getterByHeight storeGetterByHeight[H] + storeInitialized atomic.Bool } // DataSyncService is the P2P Sync Service for blocks. From 223f57e55ab86b939b8f05ea14a352d13c96d265 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Thu, 11 Dec 2025 10:44:25 +0100 Subject: [PATCH 5/6] move exchange to a single place --- .mockery.yaml | 7 + pkg/sync/exchange_wrapper.go | 29 +- pkg/sync/exchange_wrapper_test.go | 38 +-- pkg/sync/sync_service.go | 20 +- test/mocks/external/p2pexchange.go | 432 +++++++++++++++++++++++++++++ 5 files changed, 494 insertions(+), 32 deletions(-) create mode 100644 test/mocks/external/p2pexchange.go diff --git a/.mockery.yaml b/.mockery.yaml index 56eda85314..d7f7fac155 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -51,6 +51,13 @@ packages: dir: ./test/mocks pkgname: mocks filename: external/hstore.go + github.com/evstack/ev-node/pkg/sync: + interfaces: + P2PExchange: + config: + dir: ./test/mocks + pkgname: mocks + filename: external/p2pexchange.go github.com/evstack/ev-node/block/internal/syncing: interfaces: DARetriever: diff --git a/pkg/sync/exchange_wrapper.go b/pkg/sync/exchange_wrapper.go index ec574b627d..5d2d54bf47 100644 --- a/pkg/sync/exchange_wrapper.go +++ b/pkg/sync/exchange_wrapper.go @@ -10,8 +10,15 @@ import ( type storeGetter[H header.Header[H]] func(context.Context, store.Store, header.Hash) (H, error) type storeGetterByHeight[H header.Header[H]] func(context.Context, store.Store, uint64) (H, error) -type exchangeWrapper[H header.Header[H]] struct { +// P2PExchange defines the interface for the underlying P2P exchange. +type P2PExchange[H header.Header[H]] interface { header.Exchange[H] + Start(context.Context) error + Stop(context.Context) error +} + +type exchangeWrapper[H header.Header[H]] struct { + p2pExchange P2PExchange[H] daStore store.Store getter storeGetter[H] getterByHeight storeGetterByHeight[H] @@ -26,7 +33,7 @@ func (ew *exchangeWrapper[H]) Get(ctx context.Context, hash header.Hash) (H, err } // Fallback to network exchange - return ew.Exchange.Get(ctx, hash) + return ew.p2pExchange.Get(ctx, hash) } func (ew *exchangeWrapper[H]) GetByHeight(ctx context.Context, height uint64) (H, error) { @@ -38,5 +45,21 @@ func (ew *exchangeWrapper[H]) GetByHeight(ctx context.Context, height uint64) (H } // Fallback to network exchange - return ew.Exchange.GetByHeight(ctx, height) + return ew.p2pExchange.GetByHeight(ctx, height) +} + +func (ew *exchangeWrapper[H]) Head(ctx context.Context, opts ...header.HeadOption[H]) (H, error) { + return ew.p2pExchange.Head(ctx, opts...) +} + +func (ew *exchangeWrapper[H]) GetRangeByHeight(ctx context.Context, from H, to uint64) ([]H, error) { + return ew.p2pExchange.GetRangeByHeight(ctx, from, to) +} + +func (ew *exchangeWrapper[H]) Start(ctx context.Context) error { + return ew.p2pExchange.Start(ctx) +} + +func (ew *exchangeWrapper[H]) Stop(ctx context.Context) error { + return ew.p2pExchange.Stop(ctx) } diff --git a/pkg/sync/exchange_wrapper_test.go b/pkg/sync/exchange_wrapper_test.go index f6efc8bf24..fba9a51676 100644 --- a/pkg/sync/exchange_wrapper_test.go +++ b/pkg/sync/exchange_wrapper_test.go @@ -19,7 +19,7 @@ func TestExchangeWrapper_Get(t *testing.T) { expectedHeader := &types.SignedHeader{} // Just a dummy t.Run("Hit in Store", func(t *testing.T) { - mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + mockEx := extmocks.NewMockP2PExchange[*types.SignedHeader](t) // Exchange should NOT be called getter := func(ctx context.Context, s store.Store, h header.Hash) (*types.SignedHeader, error) { @@ -27,9 +27,9 @@ func TestExchangeWrapper_Get(t *testing.T) { } ew := &exchangeWrapper[*types.SignedHeader]{ - Exchange: mockEx, - daStore: mocks.NewMockStore(t), - getter: getter, + p2pExchange: mockEx, + daStore: mocks.NewMockStore(t), + getter: getter, } h, err := ew.Get(ctx, hash) @@ -38,17 +38,17 @@ func TestExchangeWrapper_Get(t *testing.T) { }) t.Run("Miss in Store", func(t *testing.T) { - mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) - mockEx.On("Get", ctx, hash).Return(expectedHeader, nil) + mockEx := extmocks.NewMockP2PExchange[*types.SignedHeader](t) + mockEx.EXPECT().Get(ctx, hash).Return(expectedHeader, nil) getter := func(ctx context.Context, s store.Store, h header.Hash) (*types.SignedHeader, error) { return nil, errors.New("not found") } ew := &exchangeWrapper[*types.SignedHeader]{ - Exchange: mockEx, - daStore: mocks.NewMockStore(t), - getter: getter, + p2pExchange: mockEx, + daStore: mocks.NewMockStore(t), + getter: getter, } h, err := ew.Get(ctx, hash) @@ -57,13 +57,13 @@ func TestExchangeWrapper_Get(t *testing.T) { }) t.Run("Store Not Configured", func(t *testing.T) { - mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) - mockEx.On("Get", ctx, hash).Return(expectedHeader, nil) + mockEx := extmocks.NewMockP2PExchange[*types.SignedHeader](t) + mockEx.EXPECT().Get(ctx, hash).Return(expectedHeader, nil) ew := &exchangeWrapper[*types.SignedHeader]{ - Exchange: mockEx, - daStore: nil, // No store - getter: nil, + p2pExchange: mockEx, + daStore: nil, // No store + getter: nil, } h, err := ew.Get(ctx, hash) @@ -78,14 +78,14 @@ func TestExchangeWrapper_GetByHeight(t *testing.T) { expectedHeader := &types.SignedHeader{} t.Run("Hit in Store", func(t *testing.T) { - mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) + mockEx := extmocks.NewMockP2PExchange[*types.SignedHeader](t) getterByHeight := func(ctx context.Context, s store.Store, h uint64) (*types.SignedHeader, error) { return expectedHeader, nil } ew := &exchangeWrapper[*types.SignedHeader]{ - Exchange: mockEx, + p2pExchange: mockEx, daStore: mocks.NewMockStore(t), getterByHeight: getterByHeight, } @@ -96,15 +96,15 @@ func TestExchangeWrapper_GetByHeight(t *testing.T) { }) t.Run("Miss in Store", func(t *testing.T) { - mockEx := extmocks.NewMockExchange[*types.SignedHeader](t) - mockEx.On("GetByHeight", ctx, height).Return(expectedHeader, nil) + mockEx := extmocks.NewMockP2PExchange[*types.SignedHeader](t) + mockEx.EXPECT().GetByHeight(ctx, height).Return(expectedHeader, nil) getterByHeight := func(ctx context.Context, s store.Store, h uint64) (*types.SignedHeader, error) { return nil, errors.New("not found") } ew := &exchangeWrapper[*types.SignedHeader]{ - Exchange: mockEx, + p2pExchange: mockEx, daStore: mocks.NewMockStore(t), getterByHeight: getterByHeight, } diff --git a/pkg/sync/sync_service.go b/pkg/sync/sync_service.go index 556d122cce..d6ba847cfe 100644 --- a/pkg/sync/sync_service.go +++ b/pkg/sync/sync_service.go @@ -49,9 +49,8 @@ type SyncService[H header.Header[H]] struct { p2p *p2p.Client - ex header.Exchange[H] - p2pExchange *goheaderp2p.Exchange[H] - sub *goheaderp2p.Subscriber[H] + ex *exchangeWrapper[H] + sub *goheaderp2p.Subscriber[H] p2pServer *goheaderp2p.ExchangeServer[H] store *goheaderstore.Store[H] daStore store.Store @@ -312,20 +311,21 @@ func (syncService *SyncService[H]) setupP2PInfrastructure(ctx context.Context) ( peerIDs := syncService.getPeerIDs() - if syncService.p2pExchange, err = newP2PExchange[H](syncService.p2p.Host(), peerIDs, networkID, syncService.genesis.ChainID, syncService.p2p.ConnectionGater()); err != nil { + p2pExchange, err := newP2PExchange[H](syncService.p2p.Host(), peerIDs, networkID, syncService.genesis.ChainID, syncService.p2p.ConnectionGater()) + if err != nil { return nil, fmt.Errorf("error while creating exchange: %w", err) } - if err := syncService.p2pExchange.Start(ctx); err != nil { - return nil, fmt.Errorf("error while starting exchange: %w", err) - } - // Wrap the exchange with the DA store check + // Create exchange wrapper with DA store check syncService.ex = &exchangeWrapper[H]{ - Exchange: syncService.p2pExchange, + p2pExchange: p2pExchange, daStore: syncService.daStore, getter: syncService.getter, getterByHeight: syncService.getterByHeight, } + if err := syncService.ex.Start(ctx); err != nil { + return nil, fmt.Errorf("error while starting exchange: %w", err) + } return peerIDs, nil } @@ -425,7 +425,7 @@ func (syncService *SyncService[H]) Stop(ctx context.Context) error { syncService.topicSubscription.Cancel() err := errors.Join( syncService.p2pServer.Stop(ctx), - syncService.p2pExchange.Stop(ctx), + syncService.ex.Stop(ctx), syncService.sub.Stop(ctx), ) if syncService.syncerStatus.isStarted() { diff --git a/test/mocks/external/p2pexchange.go b/test/mocks/external/p2pexchange.go new file mode 100644 index 0000000000..2ed96418cb --- /dev/null +++ b/test/mocks/external/p2pexchange.go @@ -0,0 +1,432 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + "github.com/celestiaorg/go-header" + mock "github.com/stretchr/testify/mock" +) + +// NewMockP2PExchange creates a new instance of MockP2PExchange. 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 NewMockP2PExchange[H header.Header[H]](t interface { + mock.TestingT + Cleanup(func()) +}) *MockP2PExchange[H] { + mock := &MockP2PExchange[H]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockP2PExchange is an autogenerated mock type for the P2PExchange type +type MockP2PExchange[H header.Header[H]] struct { + mock.Mock +} + +type MockP2PExchange_Expecter[H header.Header[H]] struct { + mock *mock.Mock +} + +func (_m *MockP2PExchange[H]) EXPECT() *MockP2PExchange_Expecter[H] { + return &MockP2PExchange_Expecter[H]{mock: &_m.Mock} +} + +// Get provides a mock function for the type MockP2PExchange +func (_mock *MockP2PExchange[H]) Get(context1 context.Context, hash header.Hash) (H, error) { + ret := _mock.Called(context1, hash) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, header.Hash) (H, error)); ok { + return returnFunc(context1, hash) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, header.Hash) H); ok { + r0 = returnFunc(context1, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, header.Hash) error); ok { + r1 = returnFunc(context1, hash) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockP2PExchange_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockP2PExchange_Get_Call[H header.Header[H]] struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - context1 context.Context +// - hash header.Hash +func (_e *MockP2PExchange_Expecter[H]) Get(context1 interface{}, hash interface{}) *MockP2PExchange_Get_Call[H] { + return &MockP2PExchange_Get_Call[H]{Call: _e.mock.On("Get", context1, hash)} +} + +func (_c *MockP2PExchange_Get_Call[H]) Run(run func(context1 context.Context, hash header.Hash)) *MockP2PExchange_Get_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 header.Hash + if args[1] != nil { + arg1 = args[1].(header.Hash) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockP2PExchange_Get_Call[H]) Return(v H, err error) *MockP2PExchange_Get_Call[H] { + _c.Call.Return(v, err) + return _c +} + +func (_c *MockP2PExchange_Get_Call[H]) RunAndReturn(run func(context1 context.Context, hash header.Hash) (H, error)) *MockP2PExchange_Get_Call[H] { + _c.Call.Return(run) + return _c +} + +// GetByHeight provides a mock function for the type MockP2PExchange +func (_mock *MockP2PExchange[H]) GetByHeight(context1 context.Context, v uint64) (H, error) { + ret := _mock.Called(context1, v) + + if len(ret) == 0 { + panic("no return value specified for GetByHeight") + } + + var r0 H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uint64) (H, error)); ok { + return returnFunc(context1, v) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, uint64) H); ok { + r0 = returnFunc(context1, v) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, uint64) error); ok { + r1 = returnFunc(context1, v) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockP2PExchange_GetByHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByHeight' +type MockP2PExchange_GetByHeight_Call[H header.Header[H]] struct { + *mock.Call +} + +// GetByHeight is a helper method to define mock.On call +// - context1 context.Context +// - v uint64 +func (_e *MockP2PExchange_Expecter[H]) GetByHeight(context1 interface{}, v interface{}) *MockP2PExchange_GetByHeight_Call[H] { + return &MockP2PExchange_GetByHeight_Call[H]{Call: _e.mock.On("GetByHeight", context1, v)} +} + +func (_c *MockP2PExchange_GetByHeight_Call[H]) Run(run func(context1 context.Context, v uint64)) *MockP2PExchange_GetByHeight_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uint64 + if args[1] != nil { + arg1 = args[1].(uint64) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockP2PExchange_GetByHeight_Call[H]) Return(v1 H, err error) *MockP2PExchange_GetByHeight_Call[H] { + _c.Call.Return(v1, err) + return _c +} + +func (_c *MockP2PExchange_GetByHeight_Call[H]) RunAndReturn(run func(context1 context.Context, v uint64) (H, error)) *MockP2PExchange_GetByHeight_Call[H] { + _c.Call.Return(run) + return _c +} + +// GetRangeByHeight provides a mock function for the type MockP2PExchange +func (_mock *MockP2PExchange[H]) GetRangeByHeight(ctx context.Context, from H, to uint64) ([]H, error) { + ret := _mock.Called(ctx, from, to) + + if len(ret) == 0 { + panic("no return value specified for GetRangeByHeight") + } + + var r0 []H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, H, uint64) ([]H, error)); ok { + return returnFunc(ctx, from, to) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, H, uint64) []H); ok { + r0 = returnFunc(ctx, from, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, H, uint64) error); ok { + r1 = returnFunc(ctx, from, to) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockP2PExchange_GetRangeByHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRangeByHeight' +type MockP2PExchange_GetRangeByHeight_Call[H header.Header[H]] struct { + *mock.Call +} + +// GetRangeByHeight is a helper method to define mock.On call +// - ctx context.Context +// - from H +// - to uint64 +func (_e *MockP2PExchange_Expecter[H]) GetRangeByHeight(ctx interface{}, from interface{}, to interface{}) *MockP2PExchange_GetRangeByHeight_Call[H] { + return &MockP2PExchange_GetRangeByHeight_Call[H]{Call: _e.mock.On("GetRangeByHeight", ctx, from, to)} +} + +func (_c *MockP2PExchange_GetRangeByHeight_Call[H]) Run(run func(ctx context.Context, from H, to uint64)) *MockP2PExchange_GetRangeByHeight_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 H + if args[1] != nil { + arg1 = args[1].(H) + } + var arg2 uint64 + if args[2] != nil { + arg2 = args[2].(uint64) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *MockP2PExchange_GetRangeByHeight_Call[H]) Return(vs []H, err error) *MockP2PExchange_GetRangeByHeight_Call[H] { + _c.Call.Return(vs, err) + return _c +} + +func (_c *MockP2PExchange_GetRangeByHeight_Call[H]) RunAndReturn(run func(ctx context.Context, from H, to uint64) ([]H, error)) *MockP2PExchange_GetRangeByHeight_Call[H] { + _c.Call.Return(run) + return _c +} + +// Head provides a mock function for the type MockP2PExchange +func (_mock *MockP2PExchange[H]) Head(context1 context.Context, headOptions ...header.HeadOption[H]) (H, error) { + // header.HeadOption[H] + _va := make([]interface{}, len(headOptions)) + for _i := range headOptions { + _va[_i] = headOptions[_i] + } + var _ca []interface{} + _ca = append(_ca, context1) + _ca = append(_ca, _va...) + ret := _mock.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Head") + } + + var r0 H + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, ...header.HeadOption[H]) (H, error)); ok { + return returnFunc(context1, headOptions...) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, ...header.HeadOption[H]) H); ok { + r0 = returnFunc(context1, headOptions...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(H) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, ...header.HeadOption[H]) error); ok { + r1 = returnFunc(context1, headOptions...) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockP2PExchange_Head_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Head' +type MockP2PExchange_Head_Call[H header.Header[H]] struct { + *mock.Call +} + +// Head is a helper method to define mock.On call +// - context1 context.Context +// - headOptions ...header.HeadOption[H] +func (_e *MockP2PExchange_Expecter[H]) Head(context1 interface{}, headOptions ...interface{}) *MockP2PExchange_Head_Call[H] { + return &MockP2PExchange_Head_Call[H]{Call: _e.mock.On("Head", + append([]interface{}{context1}, headOptions...)...)} +} + +func (_c *MockP2PExchange_Head_Call[H]) Run(run func(context1 context.Context, headOptions ...header.HeadOption[H])) *MockP2PExchange_Head_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 []header.HeadOption[H] + variadicArgs := make([]header.HeadOption[H], len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(header.HeadOption[H]) + } + } + arg1 = variadicArgs + run( + arg0, + arg1..., + ) + }) + return _c +} + +func (_c *MockP2PExchange_Head_Call[H]) Return(v H, err error) *MockP2PExchange_Head_Call[H] { + _c.Call.Return(v, err) + return _c +} + +func (_c *MockP2PExchange_Head_Call[H]) RunAndReturn(run func(context1 context.Context, headOptions ...header.HeadOption[H]) (H, error)) *MockP2PExchange_Head_Call[H] { + _c.Call.Return(run) + return _c +} + +// Start provides a mock function for the type MockP2PExchange +func (_mock *MockP2PExchange[H]) Start(context1 context.Context) error { + ret := _mock.Called(context1) + + if len(ret) == 0 { + panic("no return value specified for Start") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(context1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockP2PExchange_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type MockP2PExchange_Start_Call[H header.Header[H]] struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +// - context1 context.Context +func (_e *MockP2PExchange_Expecter[H]) Start(context1 interface{}) *MockP2PExchange_Start_Call[H] { + return &MockP2PExchange_Start_Call[H]{Call: _e.mock.On("Start", context1)} +} + +func (_c *MockP2PExchange_Start_Call[H]) Run(run func(context1 context.Context)) *MockP2PExchange_Start_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MockP2PExchange_Start_Call[H]) Return(err error) *MockP2PExchange_Start_Call[H] { + _c.Call.Return(err) + return _c +} + +func (_c *MockP2PExchange_Start_Call[H]) RunAndReturn(run func(context1 context.Context) error) *MockP2PExchange_Start_Call[H] { + _c.Call.Return(run) + return _c +} + +// Stop provides a mock function for the type MockP2PExchange +func (_mock *MockP2PExchange[H]) Stop(context1 context.Context) error { + ret := _mock.Called(context1) + + if len(ret) == 0 { + panic("no return value specified for Stop") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(context1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockP2PExchange_Stop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Stop' +type MockP2PExchange_Stop_Call[H header.Header[H]] struct { + *mock.Call +} + +// Stop is a helper method to define mock.On call +// - context1 context.Context +func (_e *MockP2PExchange_Expecter[H]) Stop(context1 interface{}) *MockP2PExchange_Stop_Call[H] { + return &MockP2PExchange_Stop_Call[H]{Call: _e.mock.On("Stop", context1)} +} + +func (_c *MockP2PExchange_Stop_Call[H]) Run(run func(context1 context.Context)) *MockP2PExchange_Stop_Call[H] { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MockP2PExchange_Stop_Call[H]) Return(err error) *MockP2PExchange_Stop_Call[H] { + _c.Call.Return(err) + return _c +} + +func (_c *MockP2PExchange_Stop_Call[H]) RunAndReturn(run func(context1 context.Context) error) *MockP2PExchange_Stop_Call[H] { + _c.Call.Return(run) + return _c +} From dc1b78cb4154fe26aba8fe714f52b9e3f47a149e Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Thu, 11 Dec 2025 10:47:35 +0100 Subject: [PATCH 6/6] lint --- pkg/blob/README.md | 2 +- pkg/sync/sync_service.go | 4 ++-- sequencers/based/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/blob/README.md b/pkg/blob/README.md index 837dc6c314..41b9876709 100644 --- a/pkg/blob/README.md +++ b/pkg/blob/README.md @@ -7,7 +7,7 @@ This package is a **trimmed copy** of code from `celestia-node` to stay JSON-com - `blob.go` comes from `celestia-node/blob/blob.go` @ tag `v0.28.4` (release v0.28.4), with unused pieces removed (blob v1, proof helpers, share length calc, appconsts dependency, etc.). - `submit_options.go` mirrors the exported JSON fields of `celestia-node/state/tx_config.go` @ the same tag, leaving out functional options, defaults, and Cosmos keyring helpers. -## Why copy instead of import? +## Why copy instead of import - Avoids pulling Cosmos SDK / celestia-app dependencies into ev-node for the small surface we need (blob JSON and commitment for v0). - Keeps binary size and module graph smaller while remaining wire-compatible with celestia-node's blob service. diff --git a/pkg/sync/sync_service.go b/pkg/sync/sync_service.go index d6ba847cfe..822a1f661f 100644 --- a/pkg/sync/sync_service.go +++ b/pkg/sync/sync_service.go @@ -49,8 +49,8 @@ type SyncService[H header.Header[H]] struct { p2p *p2p.Client - ex *exchangeWrapper[H] - sub *goheaderp2p.Subscriber[H] + ex *exchangeWrapper[H] + sub *goheaderp2p.Subscriber[H] p2pServer *goheaderp2p.ExchangeServer[H] store *goheaderstore.Store[H] daStore store.Store diff --git a/sequencers/based/README.md b/sequencers/based/README.md index 9b425b5a96..6b15105474 100644 --- a/sequencers/based/README.md +++ b/sequencers/based/README.md @@ -59,7 +59,7 @@ When at an epoch end, the retriever fetches transactions from **all DA blocks in 3. Fetches forced inclusion blobs from `epochEnd` 4. Returns all transactions as a single `ForcedInclusionEvent` -### Why Epoch-Based? +### Why Epoch-Based - **Efficiency**: Reduces the number of DA queries - **Batching**: Allows processing multiple DA blocks worth of transactions together