Skip to content

Commit 6281b2a

Browse files
committed
Implementation
Please enter the commit message for your changes. Lines starting
1 parent 658528e commit 6281b2a

30 files changed

Lines changed: 816 additions & 444 deletions

.mockery.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,3 @@ packages:
6565
dir: ./block/internal/common
6666
pkgname: common
6767
filename: broadcaster_mock.go
68-
p2pHandler:
69-
config:
70-
dir: ./block/internal/syncing
71-
pkgname: syncing
72-
filename: syncer_mock.go

apps/grpc/single/cmd/run.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
rollcmd "github.com/evstack/ev-node/pkg/cmd"
1515
"github.com/evstack/ev-node/pkg/config"
1616
rollgenesis "github.com/evstack/ev-node/pkg/genesis"
17-
"github.com/evstack/ev-node/pkg/p2p"
1817
"github.com/evstack/ev-node/pkg/p2p/key"
1918
"github.com/evstack/ev-node/pkg/store"
2019
"github.com/evstack/ev-node/sequencers/single"
@@ -100,14 +99,8 @@ The execution client must implement the Evolve execution gRPC interface.`,
10099
return err
101100
}
102101

103-
// Create P2P client
104-
p2pClient, err := p2p.NewClient(nodeConfig.P2P, nodeKey.PrivKey, datastore, genesis.ChainID, logger, nil)
105-
if err != nil {
106-
return err
107-
}
108-
109102
// Start the node
110-
return rollcmd.StartNode(logger, cmd, executor, sequencer, &daJrpc.DA, p2pClient, datastore, nodeConfig, genesis, node.NodeOptions{})
103+
return rollcmd.StartNode(logger, cmd, executor, sequencer, &daJrpc.DA, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{})
111104
},
112105
}
113106

block/components.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ func (bc *Components) Stop() error {
119119
errs = errors.Join(errs, fmt.Errorf("failed to stop submitter: %w", err))
120120
}
121121
}
122+
if bc.Cache != nil {
123+
if err := bc.Cache.SaveToDisk(); err != nil {
124+
errs = errors.Join(errs, fmt.Errorf("failed to save caches: %w", err))
125+
}
126+
}
122127

123128
return errs
124129
}
@@ -139,7 +144,6 @@ func NewSyncComponents(
139144
blockOpts BlockOptions,
140145
raftNode common.RaftNode,
141146
) (*Components, error) {
142-
logger.Info().Msg("Starting in sync-mode")
143147
cacheManager, err := cache.NewManager(config, store, logger)
144148
if err != nil {
145149
return nil, fmt.Errorf("failed to create cache manager: %w", err)
@@ -205,7 +209,6 @@ func NewAggregatorComponents(
205209
blockOpts BlockOptions,
206210
raftNode common.RaftNode,
207211
) (*Components, error) {
208-
logger.Info().Msg("Starting in aggregator-mode")
209212
cacheManager, err := cache.NewManager(config, store, logger)
210213
if err != nil {
211214
return nil, fmt.Errorf("failed to create cache manager: %w", err)

block/internal/common/broadcaster_mock.go

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

block/internal/common/expected_interfaces.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
"github.com/celestiaorg/go-header"
88
)
99

10-
// broadcaster interface for P2P broadcasting
10+
// Broadcaster interface for P2P broadcasting
1111
type Broadcaster[H header.Header[H]] interface {
1212
WriteToStoreAndBroadcast(ctx context.Context, payload H, opts ...pubsub.PubOpt) error
1313
Store() header.Store[H]
14+
Height() uint64
1415
}

block/internal/common/raft.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,9 @@ import (
99
// RaftNode interface for raft consensus integration
1010
type RaftNode interface {
1111
IsLeader() bool
12-
NodeID() string
1312
GetState() *raft.RaftBlockState
1413

1514
Broadcast(ctx context.Context, state *raft.RaftBlockState) error
1615

1716
SetApplyCallback(ch chan<- raft.RaftApplyMsg)
18-
Shutdown() error
19-
20-
AddPeer(nodeID, addr string) error
21-
RemovePeer(nodeID string) error
2217
}

block/internal/executing/executor.go

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"sync/atomic"
1111
"time"
1212

13+
"github.com/evstack/ev-node/pkg/raft"
1314
"github.com/ipfs/go-datastore"
1415
"github.com/rs/zerolog"
1516
"golang.org/x/sync/errgroup"
@@ -20,7 +21,6 @@ import (
2021
coresequencer "github.com/evstack/ev-node/core/sequencer"
2122
"github.com/evstack/ev-node/pkg/config"
2223
"github.com/evstack/ev-node/pkg/genesis"
23-
"github.com/evstack/ev-node/pkg/raft"
2424
"github.com/evstack/ev-node/pkg/signer"
2525
"github.com/evstack/ev-node/pkg/store"
2626
"github.com/evstack/ev-node/types"
@@ -100,6 +100,9 @@ func NewExecutor(
100100
if !bytes.Equal(addr, genesis.ProposerAddress) {
101101
return nil, common.ErrNotProposer
102102
}
103+
if raftNode != nil && reflect.ValueOf(raftNode).IsNil() {
104+
raftNode = nil
105+
}
103106

104107
return &Executor{
105108
store: store,
@@ -210,6 +213,12 @@ func (e *Executor) initializeState() error {
210213
}
211214
}
212215

216+
if e.raftNode != nil {
217+
// ensure node is fully synced before producing any blocks
218+
if raftState := e.raftNode.GetState(); raftState != nil && raftState.Height != state.LastBlockHeight {
219+
return fmt.Errorf("invalid state: node is not synced with the chain: raft %d != %d state", raftState.Height, state.LastBlockHeight)
220+
}
221+
}
213222
e.setLastState(state)
214223

215224
// Initialize store height using batch for atomicity
@@ -318,7 +327,7 @@ func (e *Executor) produceBlock() error {
318327
}()
319328

320329
// Check raft leadership if raft is enabled
321-
if !reflect.ValueOf(e.raftNode).IsNil() && !e.raftNode.IsLeader() {
330+
if e.raftNode != nil && !e.raftNode.IsLeader() {
322331
return errors.New("not raft leader")
323332
}
324333

@@ -422,16 +431,8 @@ func (e *Executor) produceBlock() error {
422431
return fmt.Errorf("failed to update state: %w", err)
423432
}
424433

425-
if err := batch.Commit(); err != nil {
426-
return fmt.Errorf("failed to commit batch: %w", err)
427-
}
428-
429-
// Update in-memory state after successful commit
430-
e.setLastState(newState)
431-
e.metrics.Height.Set(float64(newState.LastBlockHeight))
432-
433-
// Propose block to raft before p2p broadcast if raft is enabled
434-
if !reflect.ValueOf(e.raftNode).IsNil() {
434+
// Propose block to raft to share state in the cluster
435+
if e.raftNode != nil {
435436
headerBytes, err := header.MarshalBinary()
436437
if err != nil {
437438
return fmt.Errorf("failed to marshal header: %w", err)
@@ -452,8 +453,19 @@ func (e *Executor) produceBlock() error {
452453
return fmt.Errorf("failed to propose block to raft: %w", err)
453454
}
454455
e.logger.Debug().Uint64("height", newHeight).Msg("proposed block to raft")
456+
457+
}
458+
if err := batch.Commit(); err != nil {
459+
return fmt.Errorf("failed to commit batch: %w", err)
460+
}
461+
if err := e.store.Sync(context.Background()); err != nil {
462+
return fmt.Errorf("failed to sync store: %w", err)
455463
}
456464

465+
// Update in-memory state after successful commit
466+
e.setLastState(newState)
467+
e.metrics.Height.Set(float64(newState.LastBlockHeight))
468+
457469
// broadcast header and data to P2P network
458470
g, ctx := errgroup.WithContext(e.ctx)
459471
g.Go(func() error { return e.headerBroadcaster.WriteToStoreAndBroadcast(ctx, header) })
@@ -694,6 +706,15 @@ func (e *Executor) recordBlockMetrics(data *types.Data) {
694706
e.metrics.CommittedHeight.Set(float64(data.Metadata.Height))
695707
}
696708

709+
// IsSynced checks if the last block height in the stored state matches the expected height and returns true if they are equal.
710+
func (e *Executor) IsSynced(expHeight uint64) bool {
711+
state, err := e.store.GetState(e.ctx)
712+
if err != nil {
713+
return false
714+
}
715+
return state.LastBlockHeight == expHeight
716+
}
717+
697718
// BatchData represents batch data from sequencer
698719
type BatchData struct {
699720
*coresequencer.Batch

block/internal/executing/executor_restart_test.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package executing
22

33
import (
44
"context"
5+
"fmt"
56
"testing"
67
"time"
78

@@ -256,6 +257,8 @@ func TestExecutor_RestartNoPendingHeader(t *testing.T) {
256257
cfg.Node.BlockTime = config.DurationWrapper{Duration: 10 * time.Millisecond}
257258
cfg.Node.MaxPendingHeadersAndData = 1000
258259

260+
const numBlocks = 5
261+
259262
gen := genesis.Genesis{
260263
ChainID: "test-chain",
261264
InitialHeight: 1,
@@ -294,24 +297,29 @@ func TestExecutor_RestartNoPendingHeader(t *testing.T) {
294297
Return(initStateRoot, uint64(1024), nil).Once()
295298
require.NoError(t, exec1.initializeState())
296299

297-
exec1.ctx, exec1.cancel = context.WithCancel(context.Background())
300+
exec1.ctx, exec1.cancel = context.WithCancel(t.Context())
298301

299-
// Produce first block
302+
// Produce n blocks
300303
mockSeq1.EXPECT().GetNextBatch(mock.Anything, mock.AnythingOfType("sequencer.GetNextBatchRequest")).
301304
RunAndReturn(func(ctx context.Context, req coreseq.GetNextBatchRequest) (*coreseq.GetNextBatchResponse, error) {
302305
return &coreseq.GetNextBatchResponse{
303306
Batch: &coreseq.Batch{
304-
Transactions: [][]byte{[]byte("tx1")},
307+
Transactions: [][]byte{[]byte("any_tx")},
305308
},
306309
Timestamp: time.Now(),
307310
}, nil
308-
}).Once()
311+
}).Times(numBlocks)
309312

310-
mockExec1.EXPECT().ExecuteTxs(mock.Anything, mock.Anything, uint64(1), mock.AnythingOfType("time.Time"), initStateRoot).
311-
Return([]byte("new_root_1"), uint64(1024), nil).Once()
313+
lastStateRoot := initStateRoot
314+
for i := range numBlocks {
315+
newStateRoot := []byte(fmt.Sprintf("new_root_%d", i+1))
316+
mockExec1.EXPECT().ExecuteTxs(mock.Anything, mock.Anything, gen.InitialHeight+uint64(i), mock.AnythingOfType("time.Time"), lastStateRoot).
317+
Return(newStateRoot, uint64(1024), nil).Once()
318+
lastStateRoot = newStateRoot
312319

313-
err = exec1.produceBlock()
314-
require.NoError(t, err)
320+
require.NoError(t, exec1.produceBlock())
321+
}
322+
require.Equal(t, uint64(numBlocks), exec1.GetLastState().LastBlockHeight)
315323

316324
// Stop first executor
317325
exec1.cancel()
@@ -343,12 +351,12 @@ func TestExecutor_RestartNoPendingHeader(t *testing.T) {
343351
require.NoError(t, err)
344352

345353
require.NoError(t, exec2.initializeState())
346-
exec2.ctx, exec2.cancel = context.WithCancel(context.Background())
354+
exec2.ctx, exec2.cancel = context.WithCancel(t.Context())
347355
defer exec2.cancel()
348356

349357
// Verify state loaded correctly
350358
state := exec2.getLastState()
351-
assert.Equal(t, uint64(1), state.LastBlockHeight)
359+
require.Equal(t, uint64(numBlocks), state.LastBlockHeight)
352360

353361
// Now produce next block - should go through normal sequencer flow since no pending block
354362
mockSeq2.EXPECT().GetNextBatch(mock.Anything, mock.AnythingOfType("sequencer.GetNextBatchRequest")).
@@ -361,16 +369,16 @@ func TestExecutor_RestartNoPendingHeader(t *testing.T) {
361369
}, nil
362370
}).Once()
363371

364-
mockExec2.EXPECT().ExecuteTxs(mock.Anything, mock.Anything, uint64(2), mock.AnythingOfType("time.Time"), []byte("new_root_1")).
365-
Return([]byte("new_root_2"), uint64(1024), nil).Once()
372+
mockExec2.EXPECT().ExecuteTxs(mock.Anything, mock.Anything, uint64(numBlocks+1), mock.AnythingOfType("time.Time"), lastStateRoot).
373+
Return([]byte("new_root_after_restart"), uint64(1024), nil).Once()
366374

367375
err = exec2.produceBlock()
368376
require.NoError(t, err)
369377

370378
// Verify normal operation
371379
h, err := memStore.Height(context.Background())
372380
require.NoError(t, err)
373-
assert.Equal(t, uint64(2), h)
381+
assert.Equal(t, uint64(numBlocks+1), h)
374382

375383
// Verify sequencer was called (normal flow)
376384
mockSeq2.AssertCalled(t, "GetNextBatch", mock.Anything, mock.AnythingOfType("sequencer.GetNextBatchRequest"))

block/internal/submitting/submitter.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func (s *Submitter) Start(ctx context.Context) error {
104104

105105
// Start DA submission loop if signer is available (aggregator nodes only)
106106
if s.signer != nil {
107+
s.logger.Info().Msg("starting DA submission loop")
107108
s.wg.Add(1)
108109
go func() {
109110
defer s.wg.Done()

block/internal/syncing/raft_retriever.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ func (e eventProcessorFn) handle(ctx context.Context, event common.DAHeightEvent
2323
}
2424

2525
type raftRetriever struct {
26-
// Raft consensus
2726
raftNode common.RaftNode
2827
wg sync.WaitGroup
2928
logger zerolog.Logger

0 commit comments

Comments
 (0)