Skip to content

Commit 14d3478

Browse files
mojtaba-eskMojtaba
andauthored
fix: respect existing genesis file (#2868)
## Describe your changes and provide context This PR is a followup for #2835 to handle a case where the user already has their own genesis file and does not want to remove them or overwrite them. Here is the slack comment by @masih that suggested this fix: https://sei-network-io.slack.com/archives/C06A3D4FWE5/p1770833867974669?thread_ts=1770803742.497719&cid=C06A3D4FWE5 ## Testing performed to validate your change --------- Co-authored-by: Mojtaba <mojtaba@celestia.org>
1 parent f45028f commit 14d3478

2 files changed

Lines changed: 196 additions & 41 deletions

File tree

cmd/seid/cmd/init.go

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/sei-protocol/sei-chain/app/params"
1414
tmcfg "github.com/sei-protocol/sei-chain/sei-tendermint/config"
1515
"github.com/sei-protocol/sei-chain/sei-tendermint/libs/cli"
16+
"github.com/sei-protocol/sei-chain/sei-tendermint/libs/log"
1617
tmos "github.com/sei-protocol/sei-chain/sei-tendermint/libs/os"
1718
"github.com/sei-protocol/sei-chain/sei-tendermint/types"
1819
"github.com/spf13/cobra"
@@ -21,6 +22,8 @@ import (
2122
clientconfig "github.com/cosmos/cosmos-sdk/client/config"
2223
"github.com/cosmos/cosmos-sdk/client/flags"
2324
"github.com/cosmos/cosmos-sdk/client/input"
25+
"github.com/cosmos/cosmos-sdk/codec"
26+
"github.com/cosmos/cosmos-sdk/server"
2427
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
2528
sdk "github.com/cosmos/cosmos-sdk/types"
2629
"github.com/cosmos/cosmos-sdk/types/module"
@@ -141,46 +144,10 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
141144

142145
genFile := tmConfig.GenesisFile()
143146
overwrite, _ := cmd.Flags().GetBool(FlagOverwrite)
144-
145-
if !overwrite && tmos.FileExists(genFile) {
146-
return fmt.Errorf("genesis.json file already exists: %v", genFile)
147-
}
148-
149-
var genDoc *types.GenesisDoc
150-
var appState json.RawMessage
151-
if genesis.IsWellKnown(chainID) {
152-
var err error
153-
genDoc, err = genesis.EmbeddedGenesisDoc(chainID)
154-
if err != nil {
155-
return errors.Wrapf(err, "failed to load embedded genesis for %s", chainID)
156-
}
157-
appState = genDoc.AppState
158-
} else {
159-
var err error
160-
appState, err = json.MarshalIndent(mbm.DefaultGenesis(cdc), "", " ")
161-
if err != nil {
162-
return errors.Wrap(err, "Failed to marshall default genesis state")
163-
}
164-
genDoc = &types.GenesisDoc{}
165-
if fi, err := os.Stat(genFile); err != nil {
166-
if !os.IsNotExist(err) {
167-
return err
168-
}
169-
} else {
170-
if fi.IsDir() {
171-
return fmt.Errorf("genesis path is a directory, not a file: %s", genFile)
172-
}
173-
genDoc, err = types.GenesisDocFromFile(genFile)
174-
if err != nil {
175-
return errors.Wrap(err, "Failed to read genesis doc from file")
176-
}
177-
}
178-
genDoc.ChainID = chainID
179-
genDoc.Validators = nil
180-
genDoc.AppState = appState
181-
}
182-
if err = genutil.ExportGenesisFile(genDoc, genFile); err != nil {
183-
return errors.Wrap(err, "Failed to export genesis file")
147+
serverCtx := server.GetServerContextFromCmd(cmd)
148+
genDoc, err := loadOrWriteGenesis(serverCtx.Logger, genFile, chainID, overwrite, mbm, cdc)
149+
if err != nil {
150+
return err
184151
}
185152

186153
clientConfig, err := clientconfig.GetClientConfig(configPath, clientCtx.Viper)
@@ -191,7 +158,7 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
191158
return err
192159
}
193160

194-
toPrint := newPrintInfo(tmConfig.Moniker, chainID, nodeID, "", appState)
161+
toPrint := newPrintInfo(tmConfig.Moniker, chainID, nodeID, "", genDoc.AppState)
195162

196163
// Write Tendermint config.toml
197164
err = tmcfg.WriteConfigFile(tmConfig.RootDir, tmConfig)
@@ -233,3 +200,78 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
233200

234201
return cmd
235202
}
203+
204+
// loadOrWriteGenesis loads existing genesis at genFile if present and !overwrite, else writes embedded (well-known) or default.
205+
func loadOrWriteGenesis(logger log.Logger, genFile, chainID string, overwrite bool, mbm module.BasicManager, cdc codec.JSONCodec) (*types.GenesisDoc, error) {
206+
if !overwrite && tmos.FileExists(genFile) {
207+
if err := ensureGenesisPathIsFile(genFile); err != nil {
208+
return nil, err
209+
}
210+
genDoc, err := types.GenesisDocFromFile(genFile)
211+
if err != nil {
212+
return nil, errors.Wrapf(err, "failed to read genesis file %s", genFile)
213+
}
214+
if genDoc.ChainID != chainID {
215+
return nil, fmt.Errorf("genesis file %s has chain_id %q but init chain-id is %q", genFile, genDoc.ChainID, chainID)
216+
}
217+
if logger != nil {
218+
logger.Debug("using existing genesis file (not overwriting)", "path", genFile)
219+
}
220+
return genDoc, nil
221+
}
222+
223+
if genesis.IsWellKnown(chainID) {
224+
genDoc, err := genesis.EmbeddedGenesisDoc(chainID)
225+
if err != nil {
226+
return nil, errors.Wrapf(err, "failed to load embedded genesis for %s", chainID)
227+
}
228+
if logger != nil {
229+
logger.Debug("writing genesis file (embedded)", "chain_id", chainID, "path", genFile)
230+
}
231+
if err := genutil.ExportGenesisFile(genDoc, genFile); err != nil {
232+
return nil, errors.Wrap(err, "Failed to export genesis file")
233+
}
234+
return genDoc, nil
235+
}
236+
237+
appState, err := json.MarshalIndent(mbm.DefaultGenesis(cdc), "", " ")
238+
if err != nil {
239+
return nil, errors.Wrap(err, "Failed to marshall default genesis state")
240+
}
241+
genDoc := &types.GenesisDoc{ChainID: chainID, AppState: appState}
242+
if tmos.FileExists(genFile) {
243+
if err := ensureGenesisPathIsFile(genFile); err != nil {
244+
return nil, err
245+
}
246+
var err error
247+
genDoc, err = types.GenesisDocFromFile(genFile)
248+
if err != nil {
249+
return nil, errors.Wrap(err, "Failed to read genesis doc from file")
250+
}
251+
genDoc.ChainID = chainID
252+
genDoc.Validators = nil
253+
genDoc.AppState = appState
254+
}
255+
256+
if logger != nil {
257+
logger.Debug("writing genesis file (default)", "chain_id", chainID, "path", genFile)
258+
}
259+
if err := genutil.ExportGenesisFile(genDoc, genFile); err != nil {
260+
return nil, errors.Wrap(err, "Failed to export genesis file")
261+
}
262+
return genDoc, nil
263+
}
264+
265+
func ensureGenesisPathIsFile(genFile string) error {
266+
fi, err := os.Stat(genFile)
267+
if err != nil {
268+
if os.IsNotExist(err) {
269+
return nil
270+
}
271+
return errors.Wrapf(err, "failed to stat genesis file %s", genFile)
272+
}
273+
if fi.IsDir() {
274+
return fmt.Errorf("genesis path is a directory, not a file: %s", genFile)
275+
}
276+
return nil
277+
}

cmd/seid/cmd/init_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package cmd
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
67
"path/filepath"
78
"testing"
89

910
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
11+
"github.com/sei-protocol/sei-chain/app"
12+
"github.com/sei-protocol/sei-chain/app/genesis"
1013
"github.com/sei-protocol/sei-chain/app/params"
1114
evmrpcconfig "github.com/sei-protocol/sei-chain/evmrpc/config"
1215
seidbconfig "github.com/sei-protocol/sei-chain/sei-db/config"
1316
tmcfg "github.com/sei-protocol/sei-chain/sei-tendermint/config"
17+
"github.com/sei-protocol/sei-chain/sei-tendermint/libs/log"
18+
"github.com/sei-protocol/sei-chain/sei-tendermint/types"
1419
"github.com/spf13/viper"
1520
"github.com/stretchr/testify/require"
1621
)
@@ -230,3 +235,111 @@ func TestModeConfigurationMatrix(t *testing.T) {
230235
})
231236
}
232237
}
238+
239+
// TestLoadOrWriteGenesis_ExplicitConfigWins: existing genesis + no overwrite leaves file unchanged.
240+
func TestLoadOrWriteGenesis_ExplicitConfigWins(t *testing.T) {
241+
dir := t.TempDir()
242+
genFile := filepath.Join(dir, "genesis.json")
243+
const chainID = "atlantic-2"
244+
245+
// a minimal valid genesis with matching chain_id
246+
existing := &types.GenesisDoc{ChainID: chainID, AppState: json.RawMessage(`{}`)}
247+
err := existing.SaveAs(genFile)
248+
require.NoError(t, err)
249+
origContent, err := os.ReadFile(genFile)
250+
require.NoError(t, err)
251+
252+
encCfg := app.MakeEncodingConfig()
253+
genDoc, err := loadOrWriteGenesis(log.NewNopLogger(), genFile, chainID, false, app.ModuleBasics, encCfg.Marshaler)
254+
require.NoError(t, err)
255+
require.NotNil(t, genDoc)
256+
require.Equal(t, chainID, genDoc.ChainID)
257+
258+
// File must be unchanged
259+
afterContent, err := os.ReadFile(genFile)
260+
require.NoError(t, err)
261+
require.Equal(t, string(origContent), string(afterContent), "genesis file should not be overwritten")
262+
}
263+
264+
// TestLoadOrWriteGenesis_WrongChainID: wrong chain_id in file returns error.
265+
func TestLoadOrWriteGenesis_WrongChainID(t *testing.T) {
266+
dir := t.TempDir()
267+
genFile := filepath.Join(dir, "genesis.json")
268+
existing := &types.GenesisDoc{ChainID: "wrong-chain", AppState: json.RawMessage(`{}`)}
269+
err := existing.SaveAs(genFile)
270+
require.NoError(t, err)
271+
272+
encCfg := app.MakeEncodingConfig()
273+
_, err = loadOrWriteGenesis(log.NewNopLogger(), genFile, "atlantic-2", false, app.ModuleBasics, encCfg.Marshaler)
274+
require.Error(t, err)
275+
require.Contains(t, err.Error(), "chain_id")
276+
}
277+
278+
// TestLoadOrWriteGenesis_PathIsDirectory: path is directory returns error.
279+
func TestLoadOrWriteGenesis_PathIsDirectory(t *testing.T) {
280+
dir := t.TempDir()
281+
genFile := filepath.Join(dir, "genesis.json")
282+
require.NoError(t, os.MkdirAll(genFile, 0755))
283+
284+
encCfg := app.MakeEncodingConfig()
285+
_, err := loadOrWriteGenesis(log.NewNopLogger(), genFile, "atlantic-2", false, app.ModuleBasics, encCfg.Marshaler)
286+
require.Error(t, err)
287+
require.Contains(t, err.Error(), "directory")
288+
}
289+
290+
// TestLoadOrWriteGenesis_WellKnownWritesEmbedded: well-known chain gets embedded genesis.
291+
func TestLoadOrWriteGenesis_WellKnownWritesEmbedded(t *testing.T) {
292+
dir := t.TempDir()
293+
genFile := filepath.Join(dir, "genesis.json")
294+
const chainID = "atlantic-2"
295+
require.True(t, genesis.IsWellKnown(chainID), "atlantic-2 should be well-known")
296+
encCfg := app.MakeEncodingConfig()
297+
298+
genDoc, err := loadOrWriteGenesis(log.NewNopLogger(), genFile, chainID, false, app.ModuleBasics, encCfg.Marshaler)
299+
require.NoError(t, err)
300+
require.NotNil(t, genDoc)
301+
require.Equal(t, chainID, genDoc.ChainID)
302+
require.NotEmpty(t, genDoc.AppState)
303+
304+
embedded, err := genesis.EmbeddedGenesisDoc(chainID)
305+
require.NoError(t, err)
306+
require.Equal(t, embedded.ChainID, genDoc.ChainID)
307+
}
308+
309+
// TestLoadOrWriteGenesis_OverwriteReplacesFile: overwrite=true replaces existing file.
310+
func TestLoadOrWriteGenesis_OverwriteReplacesFile(t *testing.T) {
311+
dir := t.TempDir()
312+
genFile := filepath.Join(dir, "genesis.json")
313+
const chainID = "atlantic-2"
314+
existing := &types.GenesisDoc{ChainID: chainID, AppState: json.RawMessage(`{"old":true}`)}
315+
require.NoError(t, existing.SaveAs(genFile))
316+
317+
encCfg := app.MakeEncodingConfig()
318+
genDoc, err := loadOrWriteGenesis(log.NewNopLogger(), genFile, chainID, true, app.ModuleBasics, encCfg.Marshaler)
319+
require.NoError(t, err)
320+
require.NotNil(t, genDoc)
321+
// Should be overwritten
322+
require.NotEqual(t, `{"old":true}`, string(genDoc.AppState))
323+
}
324+
325+
// TestLoadOrWriteGenesis_UnknownChainWritesDefault: unknown chain gets default genesis.
326+
func TestLoadOrWriteGenesis_UnknownChainWritesDefault(t *testing.T) {
327+
dir := t.TempDir()
328+
genFile := filepath.Join(dir, "genesis.json")
329+
const chainID = "custom-chain-1"
330+
require.False(t, genesis.IsWellKnown(chainID), "custom-chain-1 should not be well-known")
331+
332+
encCfg := app.MakeEncodingConfig()
333+
genDoc, err := loadOrWriteGenesis(log.NewNopLogger(), genFile, chainID, false, app.ModuleBasics, encCfg.Marshaler)
334+
require.NoError(t, err)
335+
require.NotNil(t, genDoc)
336+
require.Equal(t, chainID, genDoc.ChainID)
337+
require.NotEmpty(t, genDoc.AppState)
338+
339+
// Should contain known module keys
340+
var state map[string]json.RawMessage
341+
require.NoError(t, json.Unmarshal(genDoc.AppState, &state))
342+
require.Contains(t, state, "bank")
343+
require.Contains(t, state, "staking")
344+
require.Contains(t, state, "genutil")
345+
}

0 commit comments

Comments
 (0)