Skip to content

Commit bdca5fb

Browse files
committed
x
1 parent d45758e commit bdca5fb

File tree

12 files changed

+131
-145
lines changed

12 files changed

+131
-145
lines changed

apps/evm/go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ module github.com/evstack/ev-node/apps/evm
22

33
go 1.25.6
44

5-
//replace (
6-
// github.com/evstack/ev-node => ../../
7-
// github.com/evstack/ev-node/execution/evm => ../../execution/evm
8-
//)
5+
replace (
6+
github.com/evstack/ev-node => ../../
7+
github.com/evstack/ev-node/execution/evm => ../../execution/evm
8+
)
99

1010
require (
1111
github.com/ethereum/go-ethereum v1.16.8

apps/evm/go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,12 +411,8 @@ github.com/ethereum/go-ethereum v1.16.8 h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9i
411411
github.com/ethereum/go-ethereum v1.16.8/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
412412
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
413413
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
414-
github.com/evstack/ev-node v1.0.0-rc.4 h1:Ju7pSETFdadBZxmAj0//4z7hHkXbSRDy9iTzhF60Dew=
415-
github.com/evstack/ev-node v1.0.0-rc.4/go.mod h1:xGCH5NCdGiYk6v3GVPm4NhzAtcKQgnaVnORg8b4tbOk=
416414
github.com/evstack/ev-node/core v1.0.0-rc.1 h1:Dic2PMUMAYUl5JW6DkDj6HXDEWYzorVJQuuUJOV0FjE=
417415
github.com/evstack/ev-node/core v1.0.0-rc.1/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY=
418-
github.com/evstack/ev-node/execution/evm v1.0.0-rc.3 h1:3o8H1TNywnst56lo2RlS2SXulDfp9yZJtkYYh7ZJrdM=
419-
github.com/evstack/ev-node/execution/evm v1.0.0-rc.3/go.mod h1:VUEEklKoclg45GL7dzLoDwu3UQ4ptT3rF8bw5zUmnRk=
420416
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
421417
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
422418
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=

block/internal/executing/executor.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -429,12 +429,12 @@ func (e *Executor) ProduceBlock(ctx context.Context) error {
429429

430430
// Check if there's an already stored block at the newHeight
431431
// If there is use that instead of creating a new block
432-
pendingHeader, pendingData, err := e.store.GetBlockData(ctx, newHeight)
433-
if err == nil {
432+
pendingHeader, pendingData, err := e.getPendingBlock(ctx)
433+
if err == nil && pendingHeader != nil && pendingHeader.Height() == newHeight {
434434
e.logger.Info().Uint64("height", newHeight).Msg("using pending block")
435435
header = pendingHeader
436436
data = pendingData
437-
} else if !errors.Is(err, datastore.ErrNotFound) {
437+
} else if err != nil && !errors.Is(err, datastore.ErrNotFound) {
438438
return fmt.Errorf("failed to get block data: %w", err)
439439
} else {
440440
// get batch from sequencer
@@ -452,18 +452,9 @@ func (e *Executor) ProduceBlock(ctx context.Context) error {
452452
if err != nil {
453453
return fmt.Errorf("failed to create block: %w", err)
454454
}
455-
456-
// saved early for crash recovery, will be overwritten later with the final signature
457-
batch, err := e.store.NewBatch(ctx)
458-
if err != nil {
459-
return fmt.Errorf("failed to create batch for early save: %w", err)
460-
}
461-
if err = batch.SaveBlockData(header, data, &types.Signature{}); err != nil {
455+
if err := e.savePendingBlock(ctx, header, data); err != nil {
462456
return fmt.Errorf("failed to save block data: %w", err)
463457
}
464-
if err = batch.Commit(); err != nil {
465-
return fmt.Errorf("failed to commit early save batch: %w", err)
466-
}
467458
}
468459

469460
if e.raftNode != nil && !e.raftNode.HasQuorum() {
@@ -535,6 +526,10 @@ func (e *Executor) ProduceBlock(ctx context.Context) error {
535526
}
536527
e.logger.Debug().Uint64("height", newHeight).Msg("proposed block to raft")
537528
}
529+
if err := e.deletePendingBlock(e.ctx, batch); err != nil {
530+
e.logger.Warn().Err(err).Uint64("height", newHeight).Msg("failed to delete pending block metadata")
531+
}
532+
538533
if err := batch.Commit(); err != nil {
539534
return fmt.Errorf("failed to commit batch: %w", err)
540535
}

block/internal/executing/executor_restart_test.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,8 @@ func TestExecutor_RestartUsesPendingHeader(t *testing.T) {
158158
pendingHeader.DataHash = pendingData.DACommitment()
159159

160160
// Save pending block data (this is what would happen during a crash)
161-
batch, err := memStore.NewBatch(context.Background())
162-
require.NoError(t, err)
163-
err = batch.SaveBlockData(pendingHeader, pendingData, &types.Signature{})
164-
require.NoError(t, err)
165-
err = batch.Commit()
161+
// We use savePendingBlock directly which writes to the metadata keys expected by the executor on restart
162+
err = exec1.savePendingBlock(context.Background(), pendingHeader, pendingData)
166163
require.NoError(t, err)
167164

168165
// Stop first executor (simulating crash/restart)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package executing
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/evstack/ev-node/pkg/store"
9+
"github.com/evstack/ev-node/types"
10+
ds "github.com/ipfs/go-datastore"
11+
)
12+
13+
const (
14+
headerKey = "pending_header"
15+
dataKey = "pending_data"
16+
)
17+
18+
// getPendingBlock retrieves the pending block from metadata if it exists
19+
func (e *Executor) getPendingBlock(ctx context.Context) (*types.SignedHeader, *types.Data, error) {
20+
headerBytes, err := e.store.GetMetadata(ctx, headerKey)
21+
if err != nil {
22+
if errors.Is(err, ds.ErrNotFound) {
23+
return nil, nil, nil
24+
}
25+
return nil, nil, err
26+
}
27+
28+
dataBytes, err := e.store.GetMetadata(ctx, dataKey)
29+
if err != nil {
30+
if errors.Is(err, ds.ErrNotFound) {
31+
return nil, nil, nil
32+
}
33+
return nil, nil, err
34+
}
35+
36+
header := new(types.SignedHeader)
37+
if err := header.UnmarshalBinary(headerBytes); err != nil {
38+
return nil, nil, fmt.Errorf("unmarshal pending header: %w", err)
39+
}
40+
41+
data := new(types.Data)
42+
if err := data.UnmarshalBinary(dataBytes); err != nil {
43+
return nil, nil, fmt.Errorf("unmarshal pending data: %w", err)
44+
}
45+
return header, data, nil
46+
}
47+
48+
// savePendingBlock saves a block to metadata as pending
49+
func (e *Executor) savePendingBlock(ctx context.Context, header *types.SignedHeader, data *types.Data) error {
50+
headerBytes, err := header.MarshalBinary()
51+
if err != nil {
52+
return fmt.Errorf("marshal header: %w", err)
53+
}
54+
55+
dataBytes, err := data.MarshalBinary()
56+
if err != nil {
57+
return fmt.Errorf("marshal data: %w", err)
58+
}
59+
60+
batch, err := e.store.NewBatch(ctx)
61+
if err != nil {
62+
return fmt.Errorf("create batch for early save: %w", err)
63+
}
64+
65+
if err := batch.Put(ds.NewKey(store.GetMetaKey(headerKey)), headerBytes); err != nil {
66+
return fmt.Errorf("save pending header: %w", err)
67+
}
68+
69+
if err := batch.Put(ds.NewKey(store.GetMetaKey(dataKey)), dataBytes); err != nil {
70+
return fmt.Errorf("save pending data: %w", err)
71+
}
72+
73+
if err := batch.Commit(); err != nil {
74+
return fmt.Errorf("commit pending block: %w", err)
75+
}
76+
return nil
77+
}
78+
79+
// deletePendingBlock removes pending block metadata
80+
func (e *Executor) deletePendingBlock(ctx context.Context, batch store.Batch) error {
81+
if err := batch.Delete(ds.NewKey(store.GetMetaKey(headerKey))); err != nil {
82+
return fmt.Errorf("delete pending header: %w", err)
83+
}
84+
85+
if err := batch.Delete(ds.NewKey(store.GetMetaKey(dataKey))); err != nil {
86+
return fmt.Errorf("delete pending data: %w", err)
87+
}
88+
return nil
89+
}

block/internal/syncing/syncer.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ func (s *Syncer) TrySyncNextBlock(ctx context.Context, event *common.DAHeightEve
722722
currentState := s.getLastState()
723723
headerHash := header.Hash().String()
724724

725-
s.logger.Info().Uint64("height", nextHeight).Msg("syncing block")
725+
s.logger.Info().Uint64("height", nextHeight).Msg("syncing block started")
726726

727727
// Compared to the executor logic where the current block needs to be applied first,
728728
// here only the previous block needs to be applied to proceed to the verification.
@@ -732,6 +732,17 @@ func (s *Syncer) TrySyncNextBlock(ctx context.Context, event *common.DAHeightEve
732732
s.cache.RemoveHeaderDAIncluded(headerHash)
733733
s.cache.RemoveDataDAIncluded(data.DACommitment().String())
734734

735+
s.logger.Warn().
736+
Err(err).
737+
Uint64("height", header.Height()).
738+
Uint64("time", uint64(header.Time().Unix())).
739+
Hex("proposer", header.ProposerAddress).
740+
Str("data_hash", hex.EncodeToString(header.DataHash)).
741+
Str("app_hash", hex.EncodeToString(header.AppHash)).
742+
Hex("last_header_hash", header.LastHeaderHash).
743+
Int("len signature", len(header.Signature)).
744+
Msg("block validation failed")
745+
735746
if !errors.Is(err, errInvalidState) && !errors.Is(err, errInvalidBlock) {
736747
return errors.Join(errInvalidBlock, err)
737748
}
@@ -799,7 +810,7 @@ func (s *Syncer) TrySyncNextBlock(ctx context.Context, event *common.DAHeightEve
799810
if s.p2pHandler != nil {
800811
s.p2pHandler.SetProcessedHeight(newState.LastBlockHeight)
801812
}
802-
813+
s.logger.Info().Uint64("height", nextHeight).Msg("syncing block completed")
803814
return nil
804815
}
805816

pkg/store/header_store_adapter_test.go

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -615,87 +615,3 @@ func TestHeaderStoreAdapter_GetFromPendingByHash(t *testing.T) {
615615
require.NoError(t, err)
616616
assert.Equal(t, h1.Height(), retrieved.Height())
617617
}
618-
619-
// TestHeaderStoreGetter_HeightGuard verifies that HeaderStoreGetter.GetByHeight
620-
// and HasAt respect the committed store height. Data written to the datastore
621-
// without updating store.Height() (like the executor's crash-recovery early save)
622-
// must NOT be visible through the getter.
623-
func TestHeaderStoreGetter_HeightGuard(t *testing.T) {
624-
t.Parallel()
625-
ctx := t.Context()
626-
627-
ds, err := NewTestInMemoryKVStore()
628-
require.NoError(t, err)
629-
store := New(ds)
630-
getter := NewHeaderStoreGetter(store)
631-
632-
h1, d1 := types.GetRandomBlock(1, 2, "test-chain")
633-
h2, d2 := types.GetRandomBlock(2, 2, "test-chain")
634-
635-
specs := map[string]struct {
636-
setup func()
637-
height uint64
638-
expFound bool
639-
expHasAt bool
640-
}{
641-
"data at height without height update is invisible": {
642-
setup: func() {
643-
// Simulate the executor's early save: write data but do NOT call SetHeight.
644-
batch, bErr := store.NewBatch(ctx)
645-
require.NoError(t, bErr)
646-
require.NoError(t, batch.SaveBlockData(h1, d1, &types.Signature{}))
647-
require.NoError(t, batch.Commit())
648-
},
649-
height: 1,
650-
expFound: false,
651-
expHasAt: false,
652-
},
653-
"data becomes visible after height is updated": {
654-
setup: func() {
655-
// Now commit the signed version with SetHeight (the final save).
656-
batch, bErr := store.NewBatch(ctx)
657-
require.NoError(t, bErr)
658-
require.NoError(t, batch.SaveBlockData(h1, d1, &h1.Signature))
659-
require.NoError(t, batch.SetHeight(1))
660-
require.NoError(t, batch.Commit())
661-
},
662-
height: 1,
663-
expFound: true,
664-
expHasAt: true,
665-
},
666-
"height above committed store height is invisible": {
667-
setup: func() {
668-
// Save h2 data but only set height to 1.
669-
batch, bErr := store.NewBatch(ctx)
670-
require.NoError(t, bErr)
671-
require.NoError(t, batch.SaveBlockData(h2, d2, &types.Signature{}))
672-
require.NoError(t, batch.Commit())
673-
},
674-
height: 2,
675-
expFound: false,
676-
expHasAt: false,
677-
},
678-
}
679-
680-
// Run in defined order since each step builds on the previous state.
681-
for _, name := range []string{
682-
"data at height without height update is invisible",
683-
"data becomes visible after height is updated",
684-
"height above committed store height is invisible",
685-
} {
686-
spec := specs[name]
687-
t.Run(name, func(t *testing.T) {
688-
spec.setup()
689-
690-
got, err := getter.GetByHeight(ctx, spec.height)
691-
if spec.expFound {
692-
require.NoError(t, err)
693-
assert.Equal(t, spec.height, got.Height())
694-
} else {
695-
require.Error(t, err)
696-
}
697-
698-
assert.Equal(t, spec.expHasAt, getter.HasAt(ctx, spec.height))
699-
})
700-
}
701-
}

pkg/store/keys.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func getStateAtHeightKey(height uint64) string {
5050
return GenerateKey([]string{statePrefix, strconv.FormatUint(height, 10)})
5151
}
5252

53-
func getMetaKey(key string) string {
53+
func GetMetaKey(key string) string {
5454
return GenerateKey([]string{metaPrefix, key})
5555
}
5656

pkg/store/store.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func (s *DefaultStore) GetHeader(ctx context.Context, height uint64) (*types.Sig
113113
if err = header.UnmarshalBinary(headerBlob); err != nil {
114114
return nil, fmt.Errorf("unmarshal block header: %w", err)
115115
}
116+
116117
return header, nil
117118
}
118119

@@ -176,7 +177,7 @@ func (s *DefaultStore) GetStateAtHeight(ctx context.Context, height uint64) (typ
176177
//
177178
// Metadata is separated from other data by using prefix in KV.
178179
func (s *DefaultStore) SetMetadata(ctx context.Context, key string, value []byte) error {
179-
err := s.db.Put(ctx, ds.NewKey(getMetaKey(key)), value)
180+
err := s.db.Put(ctx, ds.NewKey(GetMetaKey(key)), value)
180181
if err != nil {
181182
return fmt.Errorf("failed to set metadata for key '%s': %w", key, err)
182183
}
@@ -185,7 +186,7 @@ func (s *DefaultStore) SetMetadata(ctx context.Context, key string, value []byte
185186

186187
// GetMetadata returns values stored for given key with SetMetadata.
187188
func (s *DefaultStore) GetMetadata(ctx context.Context, key string) ([]byte, error) {
188-
data, err := s.db.Get(ctx, ds.NewKey(getMetaKey(key)))
189+
data, err := s.db.Get(ctx, ds.NewKey(GetMetaKey(key)))
189190
if err != nil {
190191
return nil, fmt.Errorf("failed to get metadata for key '%s': %w", key, err)
191192
}
@@ -196,7 +197,7 @@ func (s *DefaultStore) GetMetadata(ctx context.Context, key string) ([]byte, err
196197
// This is more efficient than iterating through known keys when the set of keys is unknown.
197198
func (s *DefaultStore) GetMetadataByPrefix(ctx context.Context, prefix string) ([]MetadataEntry, error) {
198199
// The full key in the datastore includes the meta prefix
199-
fullPrefix := getMetaKey(prefix)
200+
fullPrefix := GetMetaKey(prefix)
200201

201202
results, err := s.db.Query(ctx, dsq.Query{Prefix: fullPrefix})
202203
if err != nil {
@@ -213,7 +214,7 @@ func (s *DefaultStore) GetMetadataByPrefix(ctx context.Context, prefix string) (
213214
// Extract the original key by removing the meta prefix
214215
// The key from datastore is like "/m/cache/header-da-included/hash"
215216
// We want to return "cache/header-da-included/hash"
216-
metaKeyPrefix := getMetaKey("")
217+
metaKeyPrefix := GetMetaKey("")
217218
key := strings.TrimPrefix(result.Key, metaKeyPrefix)
218219
key = strings.TrimPrefix(key, "/") // Remove leading slash for consistency
219220

@@ -228,7 +229,7 @@ func (s *DefaultStore) GetMetadataByPrefix(ctx context.Context, prefix string) (
228229

229230
// DeleteMetadata removes a metadata key from the store.
230231
func (s *DefaultStore) DeleteMetadata(ctx context.Context, key string) error {
231-
err := s.db.Delete(ctx, ds.NewKey(getMetaKey(key)))
232+
err := s.db.Delete(ctx, ds.NewKey(GetMetaKey(key)))
232233
if err != nil {
233234
return fmt.Errorf("failed to delete metadata for key '%s': %w", key, err)
234235
}
@@ -279,7 +280,7 @@ func (s *DefaultStore) Rollback(ctx context.Context, height uint64, aggregator b
279280
} else { // in case of syncing issues, rollback the included height is OK.
280281
bz := make([]byte, 8)
281282
binary.LittleEndian.PutUint64(bz, height)
282-
if err := batch.Put(ctx, ds.NewKey(getMetaKey(DAIncludedHeightKey)), bz); err != nil {
283+
if err := batch.Put(ctx, ds.NewKey(GetMetaKey(DAIncludedHeightKey)), bz); err != nil {
283284
return fmt.Errorf("failed to update DA included height: %w", err)
284285
}
285286
}

0 commit comments

Comments
 (0)