@@ -2,11 +2,13 @@ package executing
22
33import (
44 "context"
5+ "crypto/sha256"
56 "errors"
67 "fmt"
78
89 "github.com/evstack/ev-node/pkg/store"
910 "github.com/evstack/ev-node/types"
11+ "github.com/ipfs/go-datastore"
1012 ds "github.com/ipfs/go-datastore"
1113)
1214
@@ -87,3 +89,60 @@ func (e *Executor) deletePendingBlock(ctx context.Context, batch store.Batch) er
8789 }
8890 return nil
8991}
92+
93+ // migrateLegacyPendingBlock detects old-style pending blocks that were stored
94+ // at height N+1 via SaveBlockData with an empty signature (pre-upgrade format)
95+ // and migrates them to the new metadata-key format (m/pending_header, m/pending_data).
96+ //
97+ // This prevents double-signing when a node is upgraded: without migration the
98+ // new code would not find the pending block and would create+sign a new one at
99+ // the same height.
100+ func (e * Executor ) migrateLegacyPendingBlock (ctx context.Context ) error {
101+ candidateHeight := e .getLastState ().LastBlockHeight + 1
102+ pendingHeader , pendingData , err := e .store .GetBlockData (ctx , candidateHeight )
103+ if err != nil {
104+ if ! errors .Is (err , datastore .ErrNotFound ) {
105+ return fmt .Errorf ("get block data: %w" , err )
106+ }
107+ return nil
108+ }
109+ if len (pendingHeader .Signature ) != 0 {
110+ return errors .New ("pending block with signatures found" )
111+ }
112+ // Migrate: write header+data to the new metadata keys.
113+ if err := e .savePendingBlock (ctx , pendingHeader , pendingData ); err != nil {
114+ return fmt .Errorf ("save migrated pending block: %w" , err )
115+ }
116+
117+ // Clean up old-style keys.
118+ batch , err := e .store .NewBatch (ctx )
119+ if err != nil {
120+ return fmt .Errorf ("create cleanup batch: %w" , err )
121+ }
122+
123+ headerBytes , err := pendingHeader .MarshalBinary ()
124+ if err != nil {
125+ return fmt .Errorf ("marshal header for hash: %w" , err )
126+ }
127+ headerHash := sha256 .Sum256 (headerBytes )
128+
129+ for _ , key := range []string {
130+ store .GetHeaderKey (candidateHeight ),
131+ store .GetDataKey (candidateHeight ),
132+ store .GetSignatureKey (candidateHeight ),
133+ store .GetIndexKey (headerHash [:]),
134+ } {
135+ if err := batch .Delete (ds .NewKey (key )); err != nil {
136+ return fmt .Errorf ("delete legacy key %s: %w" , key , err )
137+ }
138+ }
139+
140+ if err := batch .Commit (); err != nil {
141+ return fmt .Errorf ("commit cleanup batch: %w" , err )
142+ }
143+
144+ e .logger .Info ().
145+ Uint64 ("height" , candidateHeight ).
146+ Msg ("migrated legacy pending block to metadata format" )
147+ return nil
148+ }
0 commit comments