Skip to content

Commit 9d4c64c

Browse files
authored
fix: sync service for non zero height starts with empty store (#2834)
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> --> sync service fix for when we are not on genesis but have an empty store in 1.0.0-beta.9 we fixed writing to p2p store but the issue is when the store is empty AND we are not in genesis then the store is not initialised, this pr aims to fix this
1 parent 3ad84b8 commit 9d4c64c

File tree

28 files changed

+817
-216
lines changed

28 files changed

+817
-216
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
### Added
1313

1414
- Enhanced health check system with separate liveness (`/health/live`) and readiness (`/health/ready`) HTTP endpoints. Readiness endpoint includes P2P listening check and aggregator block production rate validation (5x block time threshold). ([#2800](https://github.com/evstack/ev-node/pull/2800))
15+
- Added `GetP2PStoreInfo` RPC method to retrieve head/tail metadata for go-header stores used by P2P sync ([#2835](https://github.com/evstack/ev-node/pull/2835))
16+
- Added protobuf definitions for `P2PStoreEntry` and `P2PStoreSnapshot` messages to support P2P store inspection
1517

1618
### Changed
1719

1820
- Remove GasPrice and GasMultiplier from DA interface and configuration to use celestia-node's native fee estimation. ([#2822](https://github.com/evstack/ev-node/pull/2822))
1921
- Use cache instead of in memory store for reaper. Persist cache on reload. Autoclean after 24 hours. ([#2811](https://github.com/evstack/ev-node/pull/2811))
22+
- Improved P2P sync service store initialization to be atomic and prevent race conditions ([#2838](https://github.com/evstack/ev-node/pull/2838))
23+
- Enhanced P2P bootstrap behavior to intelligently detect starting height from local store instead of requiring trusted hash
24+
- Relaxed execution layer height validation in block replay to allow execution to be ahead of target height, enabling recovery from manual intervention scenarios
2025

2126
### Removed
2227

2328
- **BREAKING:** Removed `evnode.v1.HealthService` gRPC endpoint. Use HTTP endpoints: `GET /health/live` and `GET /health/ready`. ([#2800](https://github.com/evstack/ev-node/pull/2800))
29+
- **BREAKING:** Removed `TrustedHash` configuration option and `--evnode.node.trusted_hash` flag. Sync service now automatically determines starting height from local store state ([#2838](https://github.com/evstack/ev-node/pull/2838))
30+
31+
### Fixed
32+
33+
- Fixed sync service initialization issue when node is not on genesis but has an empty store
2434

2535
## v1.0.0-beta.9
2636

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Ev-node is the basis of the Evolve Stack. For more in-depth information about Ev
88
[![GoDoc](https://godoc.org/github.com/evstack/ev-node?status.svg)](https://godoc.org/github.com/evstack/ev-node)
99
<!-- markdownlint-enable MD013 -->
1010

11-
> **⚠️ Version Notice**: Do not use tags or releases before v1.*. Pre-v1 releases are not stable and should be considered abandoned.
11+
> **⚠️ Version Notice**: Do not use tags or releases before v1.*. Pre-v1 releases are not stable and should be considered abandoned.
1212
1313
## Using Evolve
1414

RELEASE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ go get github.com/evstack/ev-node/[email protected]
303303

304304
- Wait 5-30 minutes for propagation
305305
- Use `go list -m` to verify availability
306-
- Check https://proxy.golang.org/
306+
- Check <https://proxy.golang.org/>
307307

308308
**Dependency version conflicts**
309309

block/internal/common/replay.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func NewReplayer(
4242
// This is useful for crash recovery scenarios where ev-node is ahead of the execution layer.
4343
//
4444
// Returns:
45-
// - error if sync fails or if execution layer is ahead of ev-node (unexpected state)
45+
// - error if sync fails
4646
func (s *Replayer) SyncToHeight(ctx context.Context, targetHeight uint64) error {
4747
// Check if the executor implements HeightProvider
4848
execHeightProvider, ok := s.exec.(coreexecutor.HeightProvider)
@@ -67,13 +67,15 @@ func (s *Replayer) SyncToHeight(ctx context.Context, targetHeight uint64) error
6767
Uint64("exec_layer_height", execHeight).
6868
Msg("execution layer height check")
6969

70-
// If execution layer is ahead, this is unexpected, fail hard
70+
// If execution layer is ahead, skip syncing and continue. This can happen if execution
71+
// progressed independently (e.g. after manual intervention). We log it for visibility but
72+
// do not treat it as fatal.
7173
if execHeight > targetHeight {
72-
s.logger.Error().
74+
s.logger.Warn().
7375
Uint64("target_height", targetHeight).
7476
Uint64("exec_layer_height", execHeight).
75-
Msg("execution layer is ahead of target height - this should not happen")
76-
return fmt.Errorf("execution layer height (%d) is ahead of target height (%d)", execHeight, targetHeight)
77+
Msg("execution layer is ahead of target height - skipping replay")
78+
return nil
7779
}
7880

7981
// If execution layer is behind, sync the missing blocks

block/internal/common/replay_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,12 @@ func TestReplayer_SyncToHeight_ExecutorAhead(t *testing.T) {
141141

142142
mockExec.On("GetLatestHeight", mock.Anything).Return(execHeight, nil)
143143

144-
// Execute sync - should fail
144+
// Execute sync - should just log and continue without error
145145
err := syncer.SyncToHeight(ctx, targetHeight)
146-
require.Error(t, err)
147-
require.Contains(t, err.Error(), "execution layer height (101) is ahead of target height (100)")
146+
require.NoError(t, err)
147+
148+
// No replay should be attempted
149+
mockExec.AssertNotCalled(t, "ExecuteTxs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
148150
}
149151

150152
func TestReplayer_SyncToHeight_NoHeightProvider(t *testing.T) {

block/internal/syncing/da_retriever_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ func TestDARetriever_RetrieveFromDA_Timeout(t *testing.T) {
128128
assert.Contains(t, err.Error(), "context deadline exceeded")
129129
assert.Len(t, events, 0)
130130

131-
// Verify timeout occurred approximately at expected time (with some tolerance)
132-
assert.Greater(t, duration, 9*time.Second, "should timeout after approximately 10 seconds")
133-
assert.Less(t, duration, 12*time.Second, "should not take much longer than timeout")
131+
// Verify timeout occurred approximately at the helper timeout (with some tolerance)
132+
assert.Greater(t, duration, defaultDATimeout-2*time.Second, "should timeout close to the helper timeout")
133+
assert.Less(t, duration, defaultDATimeout+time.Second, "should not take much longer than timeout")
134134
}
135135

136136
func TestDARetriever_RetrieveFromDA_TimeoutFast(t *testing.T) {

block/internal/syncing/syncer.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,7 @@ func (s *Syncer) fetchDAUntilCaughtUp() error {
299299

300300
daHeight := s.GetDAHeight()
301301

302-
// Create a new context with a timeout for the DA call
303-
ctx, cancel := context.WithTimeout(s.ctx, 5*time.Second)
304-
defer cancel()
305-
306-
events, err := s.daRetriever.RetrieveFromDA(ctx, daHeight)
302+
events, err := s.daRetriever.RetrieveFromDA(s.ctx, daHeight)
307303
if err != nil {
308304
switch {
309305
case errors.Is(err, coreda.ErrBlobNotFound):

docs/guides/full-node.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This guide covers how to set up a full node to run alongside a sequencer node in a Evolve-based blockchain network. A full node maintains a complete copy of the blockchain and helps validate transactions, improving the network's decentralization and security.
66

7-
> ** Note: The guide on how to run an evolve EVM full node can be found [here](./evm/single#setting-up-a-full-node). **
7+
> **Note: The guide on how to run an evolve EVM full node can be found [in the evm section](./evm/single#setting-up-a-full-node).**
88
99
## Prerequisites
1010

docs/learn/config.md

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ This document provides a comprehensive reference for all configuration options a
1717
- [Maximum Pending Blocks](#maximum-pending-blocks)
1818
- [Lazy Mode (Lazy Aggregator)](#lazy-mode-lazy-aggregator)
1919
- [Lazy Block Interval](#lazy-block-interval)
20-
- [Trusted Hash](#trusted-hash)
2120
- [Data Availability Configuration (`da`)](#data-availability-configuration-da)
2221
- [DA Service Address](#da-service-address)
2322
- [DA Authentication Token](#da-authentication-token)
@@ -275,24 +274,6 @@ _Example:_ `--rollkit.node.lazy_block_interval 1m`
275274
_Default:_ `"30s"`
276275
_Constant:_ `FlagLazyBlockTime`
277276

278-
### Trusted Hash
279-
280-
**Description:**
281-
The initial trusted hash used to bootstrap the header exchange service. This allows nodes to start synchronizing from a specific, trusted point in the chain history instead of from the genesis block. When provided, the node will fetch the corresponding header/block from peers using this hash. If not provided, the node attempts to sync from genesis.
282-
283-
**YAML:**
284-
285-
```yaml
286-
node:
287-
trusted_hash: "YOUR_TRUSTED_HASH_HEX_STRING"
288-
```
289-
290-
**Command-line Flag:**
291-
`--rollkit.node.trusted_hash <string>`
292-
_Example:_ `--rollkit.node.trusted_hash ABCDEF012345...`
293-
_Default:_ `""` (empty, sync from genesis)
294-
_Constant:_ `FlagTrustedHash`
295-
296277
## Data Availability Configuration (`da`)
297278

298279
Parameters for connecting and interacting with the Data Availability (DA) layer, which Evolve uses to publish block data.
@@ -658,6 +639,7 @@ curl http://localhost:7331/health/live
658639
#### `/health/ready`
659640

660641
Returns `200 OK` if the node can serve correct data. Checks:
642+
661643
- P2P is listening (if enabled)
662644
- Has synced blocks
663645
- Not too far behind network
@@ -669,6 +651,7 @@ curl http://localhost:7331/health/ready
669651
```
670652

671653
Configure max blocks behind:
654+
672655
```yaml
673656
node:
674657
readiness_max_blocks_behind: 15

docs/learn/specs/header-sync.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,9 @@ The Executor component (in aggregator nodes) broadcasts headers and data in para
6868
- Chain IDs for pubsub topics are also separated:
6969
- Headers: `{chainID}-headerSync` creates topic like `/gm-headerSync/header-sub/v0.0.1`
7070
- Data: `{chainID}-dataSync` creates topic like `/gm-dataSync/header-sub/v0.0.1`
71-
- Both stores must be initialized with genesis items before starting:
72-
- Header store needs genesis header
73-
- Data store needs genesis data (if applicable)
74-
- Genesis items can be loaded via `NodeConfig.TrustedHash` or P2P network query
71+
- Both stores must contain at least one item before the syncer starts:
72+
- On first boot, the services fetch the configured genesis height from peers
73+
- On restart, each store reuses its latest item to derive the initial height requested from peers
7574
- Sync services work only when connected to P2P network via `P2PConfig.Seeds`
7675
- Node context is passed to all components for graceful shutdown
7776
- Headers and data are linked through DataHash but synced independently

0 commit comments

Comments
 (0)