Skip to content

Commit c78dd42

Browse files
authored
chore: adding migration test (#277)
* chore: adding migration test * chore: run go lint * chore: replace the github workflow with the new test * chore: corrected repo to evstack * chore: pin to new version of tastora * chore: corrected job name * chore: remove extra --migrate flag
1 parent fed8a53 commit c78dd42

10 files changed

Lines changed: 638 additions & 374 deletions

File tree

.github/workflows/migration_test.yml

Lines changed: 127 additions & 341 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
22
coverage.txt
33
vendor
4-
.idea
4+
.idea
5+
integration.test

Dockerfile.cosmos-sdk

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
FROM golang:1.24-alpine AS ignite-builder
2+
3+
# Install dependencies needed for ignite and building
4+
RUN apk add --no-cache \
5+
libc6-compat \
6+
curl \
7+
bash
8+
9+
# Set environment variables
10+
ENV IGNITE_VERSION=v29.3.1
11+
12+
RUN curl -sSL https://get.ignite.com/cli@${IGNITE_VERSION}! | bash
13+
14+
WORKDIR /workspace
15+
16+
RUN ignite scaffold chain gm --no-module --skip-git --address-prefix gm
17+
18+
WORKDIR /workspace/gm
19+
20+
# Build standard cosmos-sdk chain (without evolve modules)
21+
RUN ignite chain build --skip-proto
22+
23+
# create lightweight runtime image
24+
FROM alpine:latest
25+
26+
RUN apk add --no-cache ca-certificates
27+
28+
# create non-root user
29+
RUN addgroup -g 10001 -S gm && \
30+
adduser -u 10001 -S gm -G gm
31+
32+
WORKDIR /home/gm
33+
34+
# copy the built binary from the builder stage
35+
COPY --from=ignite-builder /go/bin/gmd /usr/local/bin/gmd
36+
37+
RUN chown -R gm:gm /home/gm
38+
USER gm
39+
40+
# expose common ports
41+
EXPOSE 26657 26656 9090 1317
42+
43+
CMD ["gmd"]

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ require (
4848
golang.org/x/sync v0.17.0
4949
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7
5050
google.golang.org/grpc v1.75.1
51-
google.golang.org/protobuf v1.36.10
51+
google.golang.org/protobuf v1.36.9
5252
)
5353

5454
require (

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,8 +1484,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
14841484
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
14851485
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
14861486
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
1487-
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
1488-
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
1487+
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
1488+
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
14891489
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
14901490
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
14911491
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

server/migration_cmd.go

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ After migration, start the node normally - it will automatically detect and use
111111
return err
112112
}
113113

114+
// Ensure all stores are stopped/closed in the right order on any exit path.
115+
// Order matters: stop header/data sync stores first, then close rollkit store,
116+
// then close Comet stores.
117+
defer func() {
118+
_ = rollkitStores.headerSyncStore.Stop(ctx)
119+
_ = rollkitStores.dataSyncStore.Stop(ctx)
120+
_ = rollkitStores.rollkitStore.Close()
121+
_ = cometBlockStore.Close()
122+
_ = cometStateStore.Close()
123+
}()
124+
114125
// save current CometBFT state to the ABCI exec store
115126
if err = rollkitStores.abciExecStore.SaveState(ctx, &cometBFTstate); err != nil {
116127
return fmt.Errorf("failed to save CometBFT state to ABCI exec store: %w", err)
@@ -126,21 +137,16 @@ After migration, start the node normally - it will automatically detect and use
126137
return err
127138
}
128139

129-
if err = rollkitStores.rollkitStore.UpdateState(ctx, rollkitState); err != nil {
130-
return fmt.Errorf("failed to update evolve state: %w", err)
131-
}
132-
133140
// create minimal rollkit genesis file for future startups
134141
if err := createRollkitMigrationGenesis(config.RootDir, cometBFTstate); err != nil {
135142
return fmt.Errorf("failed to create evolve migration genesis: %w", err)
136143
}
137144

138-
cmd.Println("Created ev_genesis.json.json for migration - the node will use this on subsequent startups")
145+
cmd.Println("Created ev_genesis.json for migration - the node will use this on subsequent startups")
139146

140147
// migrate all the blocks from the CometBFT block store to the evolve store
141148
// the migration is done in reverse order, starting from the last block
142149
missedBlocks := make(map[int64]bool)
143-
initSyncStores := true
144150

145151
for height := lastBlockHeight; height > 0; height-- {
146152
cmd.Printf("Migrating block %d...\n", height)
@@ -163,25 +169,17 @@ After migration, start the node normally - it will automatically detect and use
163169
return fmt.Errorf("failed to save block data: %w", err)
164170
}
165171

166-
// set last data in sync stores
167-
if initSyncStores {
168-
if err := rollkitStores.headerSyncStore.Start(ctx); err != nil {
169-
return fmt.Errorf("failed to start header sync store: %w", err)
170-
}
171-
172-
if err = rollkitStores.headerSyncStore.Append(ctx, header); err != nil {
173-
return fmt.Errorf("failed to initialize header sync store: %w", err)
174-
}
175-
176-
if err := rollkitStores.dataSyncStore.Start(ctx); err != nil {
177-
return fmt.Errorf("failed to start data sync store: %w", err)
178-
}
179-
180-
if err = rollkitStores.dataSyncStore.Append(ctx, data); err != nil {
181-
return fmt.Errorf("failed to initialize data sync store: %w", err)
182-
}
183-
184-
initSyncStores = false
172+
// Save BlockID for this height so execution can build last commit post-migration
173+
blockParts, err := block.MakePartSet(cmttypes.BlockPartSizeBytes)
174+
if err != nil {
175+
return fmt.Errorf("failed to build part set for block %d: %w", height, err)
176+
}
177+
blockID := cmttypes.BlockID{
178+
Hash: block.Header.Hash(),
179+
PartSetHeader: blockParts.Header(),
180+
}
181+
if err := rollkitStores.abciExecStore.SaveBlockID(ctx, uint64(block.Height), &blockID); err != nil {
182+
return fmt.Errorf("failed to save BlockID for height %d: %w", height, err)
185183
}
186184

187185
// Only save extended commit info if vote extensions are enabled
@@ -199,8 +197,38 @@ After migration, start the node normally - it will automatically detect and use
199197
return fmt.Errorf("failed to set last height in Evolve store: %w", err)
200198
}
201199

200+
// persist the rollkit state at the after SetHeight is called.
201+
if err = rollkitStores.rollkitStore.UpdateState(ctx, rollkitState); err != nil {
202+
return fmt.Errorf("failed to update evolve state at height %d: %w", lastBlockHeight, err)
203+
}
204+
205+
cmd.Println("Seeding sync stores head with latest migrated header/data ...")
206+
if err := rollkitStores.headerSyncStore.Start(ctx); err != nil {
207+
return fmt.Errorf("failed to start header sync store: %w", err)
208+
}
209+
if err := rollkitStores.dataSyncStore.Start(ctx); err != nil {
210+
return fmt.Errorf("failed to start data sync store: %w", err)
211+
}
212+
213+
currentSyncHeight := rollkitStores.headerSyncStore.Height()
214+
if currentSyncHeight == 0 {
215+
header, data, err := rollkitStores.rollkitStore.GetBlockData(ctx, uint64(lastBlockHeight))
216+
if err != nil {
217+
return fmt.Errorf("failed to get block data for seeding sync stores at height %d: %w", lastBlockHeight, err)
218+
}
219+
if err := rollkitStores.headerSyncStore.Append(ctx, header); err != nil {
220+
return fmt.Errorf("failed to append header to sync store at height %d: %w", lastBlockHeight, err)
221+
}
222+
if err := rollkitStores.dataSyncStore.Append(ctx, data); err != nil {
223+
return fmt.Errorf("failed to append data to sync store at height %d: %w", lastBlockHeight, err)
224+
}
225+
cmd.Printf("Seeded sync stores head at height %d\n", lastBlockHeight)
226+
} else {
227+
cmd.Println("Sync stores already initialized. Skipping seeding")
228+
}
229+
// Defer cleanup above will stop/close stores in correct order
202230
cmd.Println("Migration completed successfully")
203-
return errors.Join(rollkitStores.rollkitStore.Close(), cometBlockStore.Close(), cometStateStore.Close())
231+
return nil
204232
},
205233
}
206234

tests/integration/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/evstack/ev-abci/tests/integration
33
go 1.24.6
44

55
require (
6-
github.com/celestiaorg/tastora v0.6.0
6+
github.com/celestiaorg/tastora v0.7.0
77
github.com/moby/moby v27.5.1+incompatible
88
github.com/stretchr/testify v1.11.1
99
go.uber.org/zap v1.27.0 // indirect

tests/integration/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ github.com/celestiaorg/go-square/v3 v3.0.1 h1:44xnE3AUiZn/3q/uJ0c20AezFS0lywFTGG
131131
github.com/celestiaorg/go-square/v3 v3.0.1/go.mod h1:Xc4ubl/7pbn/STD7w8Bnk/X1/PG3vk0ycOPW6tMOPX4=
132132
github.com/celestiaorg/nmt v0.24.1 h1:MhGKqp257eq2EQQKcva1H/BSYFqIt0Trk8/t3IWfWSw=
133133
github.com/celestiaorg/nmt v0.24.1/go.mod h1:IhLnJDgCdP70crZFpgihFmU6G+PGeXN37tnMRm+/4iU=
134-
github.com/celestiaorg/tastora v0.6.0 h1:UUl2qTQ57zcAS7xf3bpNB4vHlg2UezaBKhYteQXgxlg=
135-
github.com/celestiaorg/tastora v0.6.0/go.mod h1:Xw44XeRN2T/kSdopVCJjNhwFwRSO58wTW8GrVP7OWFI=
134+
github.com/celestiaorg/tastora v0.7.0 h1:40TzJUP8qVhpeuyaAsAS4Kp3SNGHUDZUd6Rw8UhXm8k=
135+
github.com/celestiaorg/tastora v0.7.0/go.mod h1:Xw44XeRN2T/kSdopVCJjNhwFwRSO58wTW8GrVP7OWFI=
136136
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
137137
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
138138
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=

0 commit comments

Comments
 (0)