From 164f78b41d1975d54220874f8273cbfad5334008 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 16 Sep 2024 09:20:14 +0200 Subject: [PATCH 01/12] checkpoint --- test/util/genesis/files.go | 27 ++++++++--------- test/util/testnode/network.go | 4 ++- tools/chainbuilder/main.go | 56 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 tools/chainbuilder/main.go diff --git a/test/util/genesis/files.go b/test/util/genesis/files.go index ff702d3c15..93a1fe3c57 100644 --- a/test/util/genesis/files.go +++ b/test/util/genesis/files.go @@ -19,51 +19,50 @@ func InitFiles( tmConfig *config.Config, genesis *Genesis, validatorIndex int, -) (basePath string, err error) { +) error { val, has := genesis.Validator(validatorIndex) if !has { - return "", fmt.Errorf("validator %d not found", validatorIndex) + return fmt.Errorf("validator %d not found", validatorIndex) } - basePath = filepath.Join(rootDir, ".celestia-app") - tmConfig.SetRoot(basePath) + tmConfig.SetRoot(rootDir) // save the genesis file - configPath := filepath.Join(basePath, "config") - err = os.MkdirAll(configPath, os.ModePerm) + configPath := filepath.Join(rootDir, "config") + err := os.MkdirAll(configPath, os.ModePerm) if err != nil { - return "", err + return err } genesisDoc, err := genesis.Export() if err != nil { - return "", fmt.Errorf("exporting genesis: %w", err) + return fmt.Errorf("exporting genesis: %w", err) } err = genesisDoc.SaveAs(tmConfig.GenesisFile()) if err != nil { - return "", err + return err } pvStateFile := tmConfig.PrivValidatorStateFile() if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { - return "", err + return err } pvKeyFile := tmConfig.PrivValidatorKeyFile() if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { - return "", err + return err } filePV := privval.NewFilePV(val.ConsensusKey, pvKeyFile, pvStateFile) filePV.Save() nodeKeyFile := tmConfig.NodeKeyFile() if err := tmos.EnsureDir(filepath.Dir(nodeKeyFile), 0o777); err != nil { - return "", err + return err } nodeKey := &p2p.NodeKey{ PrivKey: val.NetworkKey, } if err := nodeKey.SaveAs(nodeKeyFile); err != nil { - return "", err + return err } - return basePath, nil + return nil } diff --git a/test/util/testnode/network.go b/test/util/testnode/network.go index 79a1dc7d69..d0335b17e6 100644 --- a/test/util/testnode/network.go +++ b/test/util/testnode/network.go @@ -2,6 +2,7 @@ package testnode import ( "context" + "path/filepath" "testing" "github.com/celestiaorg/celestia-app/v3/test/util/genesis" @@ -19,7 +20,8 @@ func NewNetwork(t testing.TB, config *Config) (cctx Context, rpcAddr, grpcAddr s t.Helper() // initialize the genesis file and validator files for the first validator. - baseDir, err := genesis.InitFiles(t.TempDir(), config.TmConfig, config.Genesis, 0) + baseDir := filepath.Join(t.TempDir(), "testnode") + err := genesis.InitFiles(baseDir, config.TmConfig, config.Genesis, 0) require.NoError(t, err) tmNode, app, err := NewCometNode(baseDir, &config.UniversalTestingConfig) diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go new file mode 100644 index 0000000000..c590387043 --- /dev/null +++ b/tools/chainbuilder/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "os" + "time" + + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/test/util/genesis" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" + dbm "github.com/cometbft/cometbft-db" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/store" +) + +func main() { + +} + +func Run(ctx context.Context, numBlocks, blockSize int, blockInterval time.Duration) error { + dir, err := os.Getwd() + if err != nil { + return err + } + + validator := genesis.NewDefaultValidator(testnode.DefaultValidatorAccountName) + tmCfg := app.DefaultConsensusConfig() + gen := genesis.NewDefaultGenesis(). + WithValidators(validator) + + if err := genesis.InitFiles(dir, tmCfg, gen, 0); err != nil { + return err + } + + key := privval.LoadFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()) + + blockDb, err := dbm.NewDB("blockstore", dbm.GoLevelDBBackend, dir) + if err != nil { + return err + } + + blockStore := store.NewBlockStore(blockDb) + + stateDb, err := dbm.NewDB("state", dbm.GoLevelDBBackend, dir) + if err != nil { + return err + } + + stateStore := state.NewStore(stateDb, state.StoreOptions{ + DiscardABCIResponses: true, + }) + + lastHeight := blockStore.Height() + +} From 5bb12f34a961dc32d044ea24b09a7dc365b7d64d Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 18 Sep 2024 16:05:22 +0200 Subject: [PATCH 02/12] create chain builder tool --- pkg/user/account.go | 4 + test/util/genesis/document.go | 4 +- test/util/genesis/files.go | 7 + test/util/genesis/genesis.go | 10 + test/util/testnode/network.go | 2 +- tools/chainbuilder/README.md | 28 ++ tools/chainbuilder/benchmark_test.go | 26 ++ tools/chainbuilder/main.go | 465 +++++++++++++++++++++++++-- 8 files changed, 525 insertions(+), 21 deletions(-) create mode 100644 tools/chainbuilder/README.md create mode 100644 tools/chainbuilder/benchmark_test.go diff --git a/pkg/user/account.go b/pkg/user/account.go index 8ab1d031f4..9d66dc0540 100644 --- a/pkg/user/account.go +++ b/pkg/user/account.go @@ -40,6 +40,10 @@ func (a Account) PubKey() cryptotypes.PubKey { return a.pubKey } +func (a Account) AccountNumber() uint64 { + return a.accountNumber +} + // Sequence returns the sequence number of the account. // This is locally tracked func (a Account) Sequence() uint64 { diff --git a/test/util/genesis/document.go b/test/util/genesis/document.go index 3cc61f08b9..f45921431b 100644 --- a/test/util/genesis/document.go +++ b/test/util/genesis/document.go @@ -24,6 +24,7 @@ func Document( chainID string, gentxs []json.RawMessage, accounts []Account, + genesisTime time.Time, mods ...Modifier, ) (*coretypes.GenesisDoc, error) { genutilGenState := genutiltypes.DefaultGenesisState() @@ -73,7 +74,7 @@ func Document( // Create the genesis doc genesisDoc := &coretypes.GenesisDoc{ ChainID: chainID, - GenesisTime: time.Now(), + GenesisTime: genesisTime, ConsensusParams: params, AppState: stateBz, } @@ -101,7 +102,6 @@ func accountsToSDKTypes(accounts []Account) ([]banktypes.Balance, []authtypes.Ge ) genBals[i] = banktypes.Balance{Address: addr.String(), Coins: balances.Sort()} - genAccs[i] = authtypes.NewBaseAccount(addr, account.PubKey, uint64(i), 0) } return genBals, genAccs, nil diff --git a/test/util/genesis/files.go b/test/util/genesis/files.go index 93a1fe3c57..6d2452187b 100644 --- a/test/util/genesis/files.go +++ b/test/util/genesis/files.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" "github.com/tendermint/tendermint/config" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/p2p" @@ -17,6 +18,7 @@ import ( func InitFiles( rootDir string, tmConfig *config.Config, + appCfg *srvconfig.Config, genesis *Genesis, validatorIndex int, ) error { @@ -64,5 +66,10 @@ func InitFiles( return err } + appConfigFilePath := filepath.Join(rootDir, "config", "app.toml") + srvconfig.WriteConfigFile(appConfigFilePath, appCfg) + + config.WriteConfigFile(filepath.Join(rootDir, "config", "config.toml"), tmConfig) + return nil } diff --git a/test/util/genesis/genesis.go b/test/util/genesis/genesis.go index 5a52fdbb29..83eab54250 100644 --- a/test/util/genesis/genesis.go +++ b/test/util/genesis/genesis.go @@ -120,6 +120,11 @@ func (g *Genesis) WithKeyringAccounts(accs ...KeyringAccount) *Genesis { return g } +func (g *Genesis) WithKeyring(kr keyring.Keyring) *Genesis { + g.kr = kr + return g +} + // AddAccount adds an existing account to the genesis. func (g *Genesis) AddAccount(account Account) error { if err := account.ValidateBasic(); err != nil { @@ -208,6 +213,7 @@ func (g *Genesis) Export() (*coretypes.GenesisDoc, error) { g.ChainID, gentxs, g.accounts, + g.GenesisTime, g.genOps..., ) } @@ -220,3 +226,7 @@ func (g *Genesis) Validator(i int) (Validator, bool) { } return Validator{}, false } + +func (g *Genesis) EncodingConfig() encoding.Config { + return g.ecfg +} diff --git a/test/util/testnode/network.go b/test/util/testnode/network.go index d0335b17e6..4c0421c895 100644 --- a/test/util/testnode/network.go +++ b/test/util/testnode/network.go @@ -21,7 +21,7 @@ func NewNetwork(t testing.TB, config *Config) (cctx Context, rpcAddr, grpcAddr s // initialize the genesis file and validator files for the first validator. baseDir := filepath.Join(t.TempDir(), "testnode") - err := genesis.InitFiles(baseDir, config.TmConfig, config.Genesis, 0) + err := genesis.InitFiles(baseDir, config.TmConfig, config.AppConfig, config.Genesis, 0) require.NoError(t, err) tmNode, app, err := NewCometNode(baseDir, &config.UniversalTestingConfig) diff --git a/tools/chainbuilder/README.md b/tools/chainbuilder/README.md new file mode 100644 index 0000000000..8374ecf5c5 --- /dev/null +++ b/tools/chainbuilder/README.md @@ -0,0 +1,28 @@ +# Chainbuilder + +`chainbuilder` is a tool for building a Celestia chain for testing and development purposes. + +## Usage + +Use `go` to run the binary as follows: + +``` +go run ./tools/chainbuilder +``` + +This will create a directory with the name `testnode-{chainID}`. All files will be populated and blocks generated based on specified input. You can run a validator on the file system afterwards by calling: + +``` +celestia-appd start --home /path/to/testnode-{chainID} +``` + +The following are the set of options when generating a chain: + +- `num-blocks` the number of blocks to be generated (default: 100) +- `block-size` the size of the blocks to be generated (default <2MB). This will be a single PFB transaction +- `square-size` the size of the max square (default: 128) +- `existing-dir` point this to a directory if you want to extend an existing chain rather than create a new one + +This tool takes roughly 60-70ms per 2MB block. + + diff --git a/tools/chainbuilder/benchmark_test.go b/tools/chainbuilder/benchmark_test.go new file mode 100644 index 0000000000..5463dc37b6 --- /dev/null +++ b/tools/chainbuilder/benchmark_test.go @@ -0,0 +1,26 @@ +package main + +import ( + "context" + "testing" + "time" + + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" +) + +func BenchmarkRun(b *testing.B) { + cfg := BuilderConfig{ + NumBlocks: 100, + BlockSize: appconsts.DefaultMaxBytes, + SquareSize: appconsts.DefaultSquareSizeUpperBound, + BlockInterval: time.Second, + } + + dir := b.TempDir() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := Run(context.Background(), cfg, dir); err != nil { + b.Fatal(err) + } + } +} diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index c590387043..9748a5f6c5 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -2,55 +2,484 @@ package main import ( "context" + "fmt" "os" + "path/filepath" "time" "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/user" + "github.com/celestiaorg/celestia-app/v3/test/util" "github.com/celestiaorg/celestia-app/v3/test/util/genesis" "github.com/celestiaorg/celestia-app/v3/test/util/testnode" + blobtypes "github.com/celestiaorg/celestia-app/v3/x/blob/types" + "github.com/celestiaorg/go-square/v2" + "github.com/celestiaorg/go-square/v2/share" dbm "github.com/cometbft/cometbft-db" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/merkle" + "github.com/tendermint/tendermint/libs/log" + tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/privval" - "github.com/tendermint/tendermint/state" + smproto "github.com/tendermint/tendermint/proto/tendermint/state" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/store" + "github.com/tendermint/tendermint/types" + tmdbm "github.com/tendermint/tm-db" ) func main() { + rootCmd := &cobra.Command{ + Use: "chainbuilder", + Short: "Build a Celestia chain", + RunE: func(cmd *cobra.Command, _ []string) error { + numBlocks, _ := cmd.Flags().GetInt("num-blocks") + blockSize, _ := cmd.Flags().GetInt("block-size") + squareSize, _ := cmd.Flags().GetInt("square-size") + blockInterval, _ := cmd.Flags().GetDuration("block-interval") + existingDir, _ := cmd.Flags().GetString("existing-dir") -} + cfg := BuilderConfig{ + NumBlocks: numBlocks, + BlockSize: blockSize, + SquareSize: squareSize, + BlockInterval: blockInterval, + ExistingDir: existingDir, + } -func Run(ctx context.Context, numBlocks, blockSize int, blockInterval time.Duration) error { - dir, err := os.Getwd() - if err != nil { - return err + dir, err := os.Getwd() + if err != nil { + return fmt.Errorf("failed to get current working directory: %w", err) + } + + return Run(cmd.Context(), cfg, dir) + }, + } + + rootCmd.Flags().Int("num-blocks", 100, "Number of blocks to generate") + rootCmd.Flags().Int("block-size", appconsts.DefaultMaxBytes, "Size of each block in bytes") + rootCmd.Flags().Int("square-size", appconsts.DefaultSquareSizeUpperBound, "Size of the square") + rootCmd.Flags().Duration("block-interval", time.Second, "Interval between blocks") + rootCmd.Flags().String("existing-dir", "", "Existing directory to load chain from") + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) } +} + +type BuilderConfig struct { + NumBlocks int + BlockSize int + SquareSize int + BlockInterval time.Duration + ExistingDir string +} + +func Run(ctx context.Context, cfg BuilderConfig, dir string) error { + startTime := time.Now().Add(-1 * cfg.BlockInterval * time.Duration(cfg.NumBlocks)).UTC() + currentTime := startTime - validator := genesis.NewDefaultValidator(testnode.DefaultValidatorAccountName) + encCfg := encoding.MakeConfig(app.ModuleBasics) tmCfg := app.DefaultConsensusConfig() - gen := genesis.NewDefaultGenesis(). - WithValidators(validator) + var ( + gen *genesis.Genesis + kr keyring.Keyring + err error + ) + if cfg.ExistingDir == "" { + chainID := tmrand.Str(6) + dir = filepath.Join(dir, fmt.Sprintf("testnode-%s", chainID)) + kr, err = keyring.New(app.Name, keyring.BackendTest, dir, nil, encCfg.Codec) + if err != nil { + return fmt.Errorf("failed to create keyring: %w", err) + } - if err := genesis.InitFiles(dir, tmCfg, gen, 0); err != nil { - return err + validator := genesis.NewDefaultValidator(testnode.DefaultValidatorAccountName) + appCfg := app.DefaultAppConfig() + appCfg.Pruning = "everything" // we just want the last two states + gen = genesis.NewDefaultGenesis(). + WithKeyring(kr). + WithChainID(chainID). + WithGenesisTime(startTime). + WithValidators(validator) + + if err := genesis.InitFiles(dir, tmCfg, appCfg, gen, 0); err != nil { + return fmt.Errorf("failed to initialize genesis files: %w", err) + } + fmt.Println("Creating chain from scratch with Chain ID:", gen.ChainID) + } else { + cfgPath := filepath.Join(cfg.ExistingDir, "config/config.toml") + if _, err := os.Stat(cfgPath); os.IsNotExist(err) { + return fmt.Errorf("config file for existing chain not found at %s", cfgPath) + } + fmt.Println("Loading chain from existing directory:", cfg.ExistingDir) + tmCfg.SetRoot(cfg.ExistingDir) + kr, err = keyring.New(app.Name, keyring.BackendTest, cfg.ExistingDir, nil, encCfg.Codec) + if err != nil { + return fmt.Errorf("failed to load keyring: %w", err) + } } - key := privval.LoadFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()) + validatorKey := privval.LoadFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()) + validatorAddr := validatorKey.Key.Address - blockDb, err := dbm.NewDB("blockstore", dbm.GoLevelDBBackend, dir) + blockDB, err := dbm.NewDB("blockstore", dbm.GoLevelDBBackend, tmCfg.DBDir()) if err != nil { - return err + return fmt.Errorf("failed to create block database: %w", err) } - blockStore := store.NewBlockStore(blockDb) + blockStore := store.NewBlockStore(blockDB) - stateDb, err := dbm.NewDB("state", dbm.GoLevelDBBackend, dir) + stateDB, err := dbm.NewDB("state", dbm.GoLevelDBBackend, tmCfg.DBDir()) if err != nil { - return err + return fmt.Errorf("failed to create state database: %w", err) } - stateStore := state.NewStore(stateDb, state.StoreOptions{ + stateStore := sm.NewStore(stateDB, sm.StoreOptions{ DiscardABCIResponses: true, }) + appDB, err := tmdbm.NewDB("application", tmdbm.GoLevelDBBackend, tmCfg.DBDir()) + if err != nil { + return fmt.Errorf("failed to create application database: %w", err) + } + + simApp := app.New( + log.NewNopLogger(), + appDB, + nil, + 0, + encCfg, + 0, + util.EmptyAppOptions{}, + baseapp.SetMinGasPrices(fmt.Sprintf("%f%s", appconsts.DefaultMinGasPrice, appconsts.BondDenom)), + ) + + _ = simApp.Info(abci.RequestInfo{}) + lastHeight := blockStore.Height() + if lastHeight == 0 { + if gen == nil { + return fmt.Errorf("non empty directory but no blocks found") + } + + genDoc, err := gen.Export() + if err != nil { + return fmt.Errorf("failed to export genesis document: %w", err) + } + + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + if err != nil { + return fmt.Errorf("failed to load state from database or genesis document: %w", err) + } + + validators := make([]*types.Validator, len(genDoc.Validators)) + for i, val := range genDoc.Validators { + validators[i] = types.NewValidator(val.PubKey, val.Power) + } + validatorSet := types.NewValidatorSet(validators) + nextVals := types.TM2PB.ValidatorUpdates(validatorSet) + csParams := types.TM2PB.ConsensusParams(genDoc.ConsensusParams) + res := simApp.InitChain(abci.RequestInitChain{ + ChainId: genDoc.ChainID, + Time: genDoc.GenesisTime, + ConsensusParams: csParams, + Validators: nextVals, + AppStateBytes: genDoc.AppState, + InitialHeight: genDoc.InitialHeight, + }) + + vals, err := types.PB2TM.ValidatorUpdates(res.Validators) + if err != nil { + return fmt.Errorf("failed to convert validator updates: %w", err) + } + state.Validators = types.NewValidatorSet(vals) + state.NextValidators = types.NewValidatorSet(vals).CopyIncrementProposerPriority(1) + state.AppHash = res.AppHash + state.LastResultsHash = merkle.HashFromByteSlices(nil) + if err := stateStore.Save(state); err != nil { + return fmt.Errorf("failed to save initial state: %w", err) + } + currentTime = currentTime.Add(cfg.BlockInterval) + } else { + fmt.Println("Starting from height", lastHeight) + } + state, err := stateStore.Load() + if err != nil { + return fmt.Errorf("failed to load state: %w", err) + } + if cfg.ExistingDir != "" { + // if this is extending an existing chain, we want to start + // the time to be where the existing chain left off + currentTime = state.LastBlockTime.Add(cfg.BlockInterval) + } + + validatorPower := state.Validators.Validators[0].VotingPower + + signer, err := user.NewSigner( + kr, + encCfg.TxConfig, + state.ChainID, + state.ConsensusParams.Version.AppVersion, + user.NewAccount(testnode.DefaultValidatorAccountName, 0, uint64(lastHeight)+1), + ) + if err != nil { + return fmt.Errorf("failed to create new signer: %w", err) + } + + var ( + errCh = make(chan error, 2) + dataCh = make(chan *tmproto.Data, 100) + persistCh = make(chan persistData, 100) + commit = types.NewCommit(0, 0, types.BlockID{}, nil) + ) + if lastHeight > 0 { + commit = blockStore.LoadSeenCommit(lastHeight) + } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + go func() { + errCh <- generateSquareRoutine(ctx, signer, cfg, dataCh) + }() + + go func() { + errCh <- persistDataRoutine(ctx, stateStore, blockStore, persistCh) + }() + + for height := lastHeight + 1; height <= int64(cfg.NumBlocks)+lastHeight; height++ { + select { + case <-ctx.Done(): + return ctx.Err() + case dataPB := <-dataCh: + data, err := types.DataFromProto(dataPB) + if err != nil { + return fmt.Errorf("failed to convert data from protobuf: %w", err) + } + block, blockParts := state.MakeBlock(height, data, commit, nil, validatorAddr) + blockID := types.BlockID{ + Hash: block.Hash(), + PartSetHeader: blockParts.Header(), + } + + precommitVote := &tmproto.Vote{ + Height: height, + Round: 0, + Type: tmproto.PrecommitType, + BlockID: blockID.ToProto(), + ValidatorAddress: validatorAddr, + Timestamp: currentTime, + Signature: nil, + } + + if err := validatorKey.SignVote(state.ChainID, precommitVote); err != nil { + return fmt.Errorf("failed to sign precommit vote (%s): %w", precommitVote.String(), err) + } + + commitSig := types.CommitSig{ + BlockIDFlag: types.BlockIDFlagCommit, + ValidatorAddress: validatorAddr, + Timestamp: currentTime, + Signature: precommitVote.Signature, + } + commit = types.NewCommit(height, 0, blockID, []types.CommitSig{commitSig}) + + var lastCommitInfo abci.LastCommitInfo + if height > 1 { + lastCommitInfo = abci.LastCommitInfo{ + Round: 0, + Votes: []abci.VoteInfo{ + { + Validator: abci.Validator{ + Address: validatorAddr, + Power: validatorPower, + }, + SignedLastBlock: true, + }, + }, + } + } + + beginBlockResp := simApp.BeginBlock(abci.RequestBeginBlock{ + Hash: block.Hash(), + Header: *block.Header.ToProto(), + LastCommitInfo: lastCommitInfo, + }) + + deliverTxResponses := make([]*abci.ResponseDeliverTx, len(block.Data.Txs)) + + for idx, tx := range block.Data.Txs { + blobTx, isBlobTx := types.UnmarshalBlobTx(tx) + if isBlobTx { + tx = blobTx.Tx + } + deliverTxResponse := simApp.DeliverTx(abci.RequestDeliverTx{ + Tx: tx, + }) + if deliverTxResponse.Code != abci.CodeTypeOK { + return fmt.Errorf("failed to deliver tx: %s", deliverTxResponse.Log) + } + deliverTxResponses[idx] = &deliverTxResponse + } + + endBlockResp := simApp.EndBlock(abci.RequestEndBlock{ + Height: block.Height, + }) + + commitResp := simApp.Commit() + state.LastBlockHeight = height + state.LastBlockID = blockID + state.LastBlockTime = block.Time + state.LastValidators = state.Validators + state.Validators = state.NextValidators + state.NextValidators = state.NextValidators.CopyIncrementProposerPriority(1) + state.AppHash = commitResp.Data + state.LastResultsHash = sm.ABCIResponsesResultsHash(&smproto.ABCIResponses{ + DeliverTxs: deliverTxResponses, + BeginBlock: &beginBlockResp, + EndBlock: &endBlockResp, + }) + currentTime = currentTime.Add(cfg.BlockInterval) + persistCh <- persistData{ + state: state.Copy(), + block: block, + seenCommit: &types.Commit{ + Height: commit.Height, + Round: commit.Round, + BlockID: commit.BlockID, + Signatures: []types.CommitSig{commitSig}, + }, + } + } + } + + close(dataCh) + close(persistCh) + + var firstErr error + for i := 0; i < cap(errCh); i++ { + err := <-errCh + if err != nil && firstErr == nil { + firstErr = err + } + } + + if err := blockDB.Close(); err != nil { + return fmt.Errorf("failed to close block database: %w", err) + } + if err := stateDB.Close(); err != nil { + return fmt.Errorf("failed to close state database: %w", err) + } + if err := appDB.Close(); err != nil { + return fmt.Errorf("failed to close application database: %w", err) + } + + fmt.Println("Chain built successfully", state.LastBlockHeight) + + return firstErr +} + +func generateSquareRoutine( + ctx context.Context, + signer *user.Signer, + cfg BuilderConfig, + dataCh chan<- *tmproto.Data, +) error { + for i := 0; i < cfg.NumBlocks; i++ { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + account := signer.Accounts()[0] + + blob, err := share.NewV0Blob(share.RandomNamespace(), crypto.CRandBytes(cfg.BlockSize)) + if err != nil { + return err + } + + blobGas := blobtypes.DefaultEstimateGas([]uint32{uint32(cfg.BlockSize)}) + fee := float64(blobGas) * appconsts.DefaultMinGasPrice * 2 + tx, _, err := signer.CreatePayForBlobs(account.Name(), []*share.Blob{blob}, user.SetGasLimit(blobGas), user.SetFee(uint64(fee))) + if err != nil { + return err + } + if err := signer.IncrementSequence(account.Name()); err != nil { + return err + } + + dataSquare, txs, err := square.Build( + [][]byte{tx}, + cfg.SquareSize, + appconsts.SubtreeRootThreshold(1), + ) + if err != nil { + return err + } + + eds, err := da.ExtendShares(share.ToBytes(dataSquare)) + if err != nil { + return err + } + + dah, err := da.NewDataAvailabilityHeader(eds) + if err != nil { + return err + } + + select { + case dataCh <- &tmproto.Data{ + Txs: txs, + Hash: dah.Hash(), + SquareSize: uint64(dataSquare.Size()), + }: + case <-ctx.Done(): + return ctx.Err() + } + } + return nil +} + +type persistData struct { + state sm.State + block *types.Block + seenCommit *types.Commit +} + +func persistDataRoutine( + ctx context.Context, + stateStore sm.Store, + blockStore *store.BlockStore, + dataCh <-chan persistData, +) error { + for { + select { + case <-ctx.Done(): + return ctx.Err() + case data, ok := <-dataCh: + if !ok { + return nil + } + + if err := stateStore.Save(data.state); err != nil { + return err + } + blockParts := data.block.MakePartSet(types.BlockPartSizeBytes) + blockStore.SaveBlock(data.block, blockParts, data.seenCommit) + if blockStore.Height()%100 == 0 { + fmt.Println("Reached height", blockStore.Height()) + } + } + } } From 52d28659b557804e85afdf08f819111d40d52d11 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 18 Sep 2024 17:07:06 +0200 Subject: [PATCH 03/12] add an integration test to ensure that the tool works --- go.mod | 11 +-- go.sum | 21 +++-- tools/chainbuilder/README.md | 1 + tools/chainbuilder/integration_test.go | 102 +++++++++++++++++++++++++ tools/chainbuilder/main.go | 31 +++++++- 5 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 tools/chainbuilder/integration_test.go diff --git a/go.mod b/go.mod index 1de8165f96..643aa89d8a 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/celestiaorg/knuu v0.14.0 github.com/celestiaorg/nmt v0.22.1 github.com/celestiaorg/rsmt2d v0.14.0 + github.com/cometbft/cometbft-db v0.7.0 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.46.16 github.com/cosmos/gogoproto v1.7.0 @@ -57,17 +58,17 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/celestiaorg/bittwister v0.0.0-20231213180407-65cdbaf5b8c7 // indirect github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cilium/ebpf v0.12.3 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect - github.com/cometbft/cometbft-db v0.7.0 // indirect github.com/confio/ics23/go v0.9.1 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/containerd/continuity v0.4.2 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect @@ -83,7 +84,7 @@ require ( github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/distribution/reference v0.5.0 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v26.1.5+incompatible // indirect github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -173,7 +174,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -205,7 +206,7 @@ require ( github.com/ulikunitz/xz v0.5.10 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - go.etcd.io/bbolt v1.3.6 // indirect + go.etcd.io/bbolt v1.3.10 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect diff --git a/go.sum b/go.sum index b8775d02cd..7228f12197 100644 --- a/go.sum +++ b/go.sum @@ -335,8 +335,8 @@ github.com/celestiaorg/rsmt2d v0.14.0/go.mod h1:4kxqiTdFev49sGiKXTDjohbWYOG5GlcI github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -402,8 +402,8 @@ github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1 github.com/consensys/gnark-crypto v0.5.3/go.mod h1:hOdPlWQV1gDLp7faZVeg8Y0iEPFaOUnCc4XeCCk96p0= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -478,8 +478,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= @@ -1092,8 +1092,8 @@ github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8P github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= @@ -1354,8 +1354,8 @@ gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975/go.mod h1:ZkMZ gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs= gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1641,7 +1641,6 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/tools/chainbuilder/README.md b/tools/chainbuilder/README.md index 8374ecf5c5..480925d55c 100644 --- a/tools/chainbuilder/README.md +++ b/tools/chainbuilder/README.md @@ -22,6 +22,7 @@ The following are the set of options when generating a chain: - `block-size` the size of the blocks to be generated (default <2MB). This will be a single PFB transaction - `square-size` the size of the max square (default: 128) - `existing-dir` point this to a directory if you want to extend an existing chain rather than create a new one +- `namespace` allows you to pick a custom v0 namespace. By default "test" will be chosen. This tool takes roughly 60-70ms per 2MB block. diff --git a/tools/chainbuilder/integration_test.go b/tools/chainbuilder/integration_test.go new file mode 100644 index 0000000000..2c182ef21f --- /dev/null +++ b/tools/chainbuilder/integration_test.go @@ -0,0 +1,102 @@ +package main + +import ( + "context" + "fmt" + "path/filepath" + "testing" + "time" + + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/test/util" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/tendermint/tendermint/libs/log" + tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/rpc/client/local" + tmdbm "github.com/tendermint/tm-db" + + "github.com/stretchr/testify/require" +) + +func TestRun(t *testing.T) { + if testing.Short() { + t.Skip("skipping chainbuilder tool test") + } + + numBlocks := 10 + + cfg := BuilderConfig{ + NumBlocks: numBlocks, + BlockSize: appconsts.DefaultMaxBytes, + SquareSize: appconsts.DefaultSquareSizeUpperBound, + BlockInterval: time.Second, + ChainID: tmrand.Str(6), + Namespace: defaultNamespace, + } + + dir := t.TempDir() + + // First run + err := Run(context.Background(), cfg, dir) + require.NoError(t, err) + + // Second run with existing directory + cfg.ExistingDir = filepath.Join(dir, fmt.Sprintf("testnode-%s", cfg.ChainID)) + err = Run(context.Background(), cfg, dir) + require.NoError(t, err) + + tmCfg := testnode.DefaultTendermintConfig() + tmCfg.SetRoot(cfg.ExistingDir) + + appDB, err := tmdbm.NewDB("application", tmdbm.GoLevelDBBackend, tmCfg.DBDir()) + require.NoError(t, err) + + encCfg := encoding.MakeConfig(app.ModuleBasics) + + app := app.New( + log.NewNopLogger(), + appDB, + nil, + 0, + encCfg, + 0, + util.EmptyAppOptions{}, + baseapp.SetMinGasPrices(fmt.Sprintf("%f%s", appconsts.DefaultMinGasPrice, appconsts.BondDenom)), + ) + + nodeKey, err := p2p.LoadNodeKey(tmCfg.NodeKeyFile()) + require.NoError(t, err) + + cometNode, err := node.NewNode( + tmCfg, + privval.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + node.DefaultGenesisDocProviderFunc(tmCfg), + node.DefaultDBProvider, + node.DefaultMetricsProvider(tmCfg.Instrumentation), + log.NewNopLogger(), + ) + require.NoError(t, err) + + require.NoError(t, cometNode.Start()) + defer cometNode.Stop() + + client := local.New(cometNode) + status, err := client.Status(context.Background()) + require.NoError(t, err) + require.NotNil(t, status) + // assert that the new node eventually makes progress in the chain + require.Eventually(t, func() bool { + status, err := client.Status(context.Background()) + require.NoError(t, err) + return status.SyncInfo.LatestBlockHeight >= int64(numBlocks*2) + }, time.Second*10, time.Millisecond*100) +} diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 9748a5f6c5..3e9759ba89 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -36,6 +36,14 @@ import ( tmdbm "github.com/tendermint/tm-db" ) +var defaultNamespace share.Namespace + +const defaultNamespaceStr = "test" + +func init() { + defaultNamespace = share.MustNewV0Namespace([]byte(defaultNamespaceStr)) +} + func main() { rootCmd := &cobra.Command{ Use: "chainbuilder", @@ -46,6 +54,17 @@ func main() { squareSize, _ := cmd.Flags().GetInt("square-size") blockInterval, _ := cmd.Flags().GetDuration("block-interval") existingDir, _ := cmd.Flags().GetString("existing-dir") + namespaceStr, _ := cmd.Flags().GetString("namespace") + var namespace share.Namespace + if namespaceStr == "" { + namespace = defaultNamespace + } else { + var err error + namespace, err = share.NewV0Namespace([]byte(namespaceStr)) + if err != nil { + return fmt.Errorf("invalid namespace: %w", err) + } + } cfg := BuilderConfig{ NumBlocks: numBlocks, @@ -53,6 +72,8 @@ func main() { SquareSize: squareSize, BlockInterval: blockInterval, ExistingDir: existingDir, + Namespace: namespace, + ChainID: tmrand.Str(6), } dir, err := os.Getwd() @@ -69,6 +90,7 @@ func main() { rootCmd.Flags().Int("square-size", appconsts.DefaultSquareSizeUpperBound, "Size of the square") rootCmd.Flags().Duration("block-interval", time.Second, "Interval between blocks") rootCmd.Flags().String("existing-dir", "", "Existing directory to load chain from") + rootCmd.Flags().String("namespace", "", "Custom namespace for the chain") if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) @@ -81,6 +103,8 @@ type BuilderConfig struct { SquareSize int BlockInterval time.Duration ExistingDir string + Namespace share.Namespace + ChainID string } func Run(ctx context.Context, cfg BuilderConfig, dir string) error { @@ -95,8 +119,7 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { err error ) if cfg.ExistingDir == "" { - chainID := tmrand.Str(6) - dir = filepath.Join(dir, fmt.Sprintf("testnode-%s", chainID)) + dir = filepath.Join(dir, fmt.Sprintf("testnode-%s", cfg.ChainID)) kr, err = keyring.New(app.Name, keyring.BackendTest, dir, nil, encCfg.Codec) if err != nil { return fmt.Errorf("failed to create keyring: %w", err) @@ -107,7 +130,7 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { appCfg.Pruning = "everything" // we just want the last two states gen = genesis.NewDefaultGenesis(). WithKeyring(kr). - WithChainID(chainID). + WithChainID(cfg.ChainID). WithGenesisTime(startTime). WithValidators(validator) @@ -404,7 +427,7 @@ func generateSquareRoutine( account := signer.Accounts()[0] - blob, err := share.NewV0Blob(share.RandomNamespace(), crypto.CRandBytes(cfg.BlockSize)) + blob, err := share.NewV0Blob(cfg.Namespace, crypto.CRandBytes(cfg.BlockSize)) if err != nil { return err } From e9ff46cf8005487ab6386e301fb9979917e58b79 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 18 Sep 2024 17:59:35 +0200 Subject: [PATCH 04/12] lint --- tools/chainbuilder/integration_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/chainbuilder/integration_test.go b/tools/chainbuilder/integration_test.go index 2c182ef21f..cb5cc4f408 100644 --- a/tools/chainbuilder/integration_test.go +++ b/tools/chainbuilder/integration_test.go @@ -87,7 +87,7 @@ func TestRun(t *testing.T) { require.NoError(t, err) require.NoError(t, cometNode.Start()) - defer cometNode.Stop() + defer func() { _ = cometNode.Stop() }() client := local.New(cometNode) status, err := client.Status(context.Background()) @@ -99,4 +99,5 @@ func TestRun(t *testing.T) { require.NoError(t, err) return status.SyncInfo.LatestBlockHeight >= int64(numBlocks*2) }, time.Second*10, time.Millisecond*100) + require.NoError(t, cometNode.Stop()) } From 5465877e9f300b5931ff14c84c6832c138c2d0b4 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Thu, 19 Sep 2024 14:39:30 +0200 Subject: [PATCH 05/12] wait at the end of test to ensure proper cleanup --- tools/chainbuilder/README.md | 2 -- tools/chainbuilder/integration_test.go | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/chainbuilder/README.md b/tools/chainbuilder/README.md index 480925d55c..99911468ac 100644 --- a/tools/chainbuilder/README.md +++ b/tools/chainbuilder/README.md @@ -25,5 +25,3 @@ The following are the set of options when generating a chain: - `namespace` allows you to pick a custom v0 namespace. By default "test" will be chosen. This tool takes roughly 60-70ms per 2MB block. - - diff --git a/tools/chainbuilder/integration_test.go b/tools/chainbuilder/integration_test.go index cb5cc4f408..5ce8def3bc 100644 --- a/tools/chainbuilder/integration_test.go +++ b/tools/chainbuilder/integration_test.go @@ -100,4 +100,5 @@ func TestRun(t *testing.T) { return status.SyncInfo.LatestBlockHeight >= int64(numBlocks*2) }, time.Second*10, time.Millisecond*100) require.NoError(t, cometNode.Stop()) + cometNode.Wait() } From 8bfa93475e60bf9b58e8e07588919a7849d5ca16 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 11:12:27 +0200 Subject: [PATCH 06/12] rene's frankenstein patch --- go.sum | 3 +++ tools/chainbuilder/main.go | 30 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/go.sum b/go.sum index 27438e28c4..35e55325f6 100644 --- a/go.sum +++ b/go.sum @@ -213,6 +213,7 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.5.0 h1:+K/VEwIAaPcHiMtQvpLD4lqW7f0Gk3xdYZmI1hD+CXo= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -485,6 +486,7 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 h1:IPrmumsT9t5BS7XcPhgsCTlkWbYg80SEXUzDpReaU6Y= github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11/go.mod h1:a6bNUGTbQBsY6VRHTr4h/rkOXjl244DyRD0tx3fgq4Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -1209,6 +1211,7 @@ github.com/shirou/gopsutil v3.21.6+incompatible h1:mmZtAlWSd8U2HeRTjswbnDLPxqsEo github.com/shirou/gopsutil v3.21.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 3e9759ba89..27552a95d1 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -7,15 +7,6 @@ import ( "path/filepath" "time" - "github.com/celestiaorg/celestia-app/v3/app" - "github.com/celestiaorg/celestia-app/v3/app/encoding" - "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" - "github.com/celestiaorg/celestia-app/v3/pkg/da" - "github.com/celestiaorg/celestia-app/v3/pkg/user" - "github.com/celestiaorg/celestia-app/v3/test/util" - "github.com/celestiaorg/celestia-app/v3/test/util/genesis" - "github.com/celestiaorg/celestia-app/v3/test/util/testnode" - blobtypes "github.com/celestiaorg/celestia-app/v3/x/blob/types" "github.com/celestiaorg/go-square/v2" "github.com/celestiaorg/go-square/v2/share" dbm "github.com/cometbft/cometbft-db" @@ -34,6 +25,16 @@ import ( "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" tmdbm "github.com/tendermint/tm-db" + + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/encoding" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v3/pkg/da" + "github.com/celestiaorg/celestia-app/v3/pkg/user" + "github.com/celestiaorg/celestia-app/v3/test/util" + "github.com/celestiaorg/celestia-app/v3/test/util/genesis" + "github.com/celestiaorg/celestia-app/v3/test/util/testnode" + blobtypes "github.com/celestiaorg/celestia-app/v3/x/blob/types" ) var defaultNamespace share.Namespace @@ -128,7 +129,10 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { validator := genesis.NewDefaultValidator(testnode.DefaultValidatorAccountName) appCfg := app.DefaultAppConfig() appCfg.Pruning = "everything" // we just want the last two states + cp := app.DefaultConsensusParams() + cp.Version.AppVersion = 2 gen = genesis.NewDefaultGenesis(). + WithConsensusParams(cp). WithKeyring(kr). WithChainID(cfg.ChainID). WithGenesisTime(startTime). @@ -280,7 +284,15 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { errCh <- persistDataRoutine(ctx, stateStore, blockStore, persistCh) }() + lastBlock := blockStore.LoadBlock(blockStore.Height()) + for height := lastHeight + 1; height <= int64(cfg.NumBlocks)+lastHeight; height++ { + if lastBlock.Time.Add(cfg.BlockInterval).After(time.Now().UTC()) { + fmt.Println(fmt.Sprintf("blocks cannot be generated into the future, stopping at height %d", + lastBlock.Height)) + break + } + select { case <-ctx.Done(): return ctx.Err() From c02da350c5a24493e18e45e1f5149a0f83f25b79 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 11:30:16 +0200 Subject: [PATCH 07/12] allow to build chain up to present --- tools/chainbuilder/benchmark_test.go | 1 - tools/chainbuilder/integration_test.go | 1 - tools/chainbuilder/main.go | 26 ++++++++++++++++---------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tools/chainbuilder/benchmark_test.go b/tools/chainbuilder/benchmark_test.go index 5463dc37b6..b72d9297e5 100644 --- a/tools/chainbuilder/benchmark_test.go +++ b/tools/chainbuilder/benchmark_test.go @@ -12,7 +12,6 @@ func BenchmarkRun(b *testing.B) { cfg := BuilderConfig{ NumBlocks: 100, BlockSize: appconsts.DefaultMaxBytes, - SquareSize: appconsts.DefaultSquareSizeUpperBound, BlockInterval: time.Second, } diff --git a/tools/chainbuilder/integration_test.go b/tools/chainbuilder/integration_test.go index 5ce8def3bc..eb671cd1f0 100644 --- a/tools/chainbuilder/integration_test.go +++ b/tools/chainbuilder/integration_test.go @@ -35,7 +35,6 @@ func TestRun(t *testing.T) { cfg := BuilderConfig{ NumBlocks: numBlocks, BlockSize: appconsts.DefaultMaxBytes, - SquareSize: appconsts.DefaultSquareSizeUpperBound, BlockInterval: time.Second, ChainID: tmrand.Str(6), Namespace: defaultNamespace, diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 27552a95d1..755563d089 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -39,7 +39,10 @@ import ( var defaultNamespace share.Namespace -const defaultNamespaceStr = "test" +const ( + defaultNamespaceStr = "test" + maxSquareSize = 512 +) func init() { defaultNamespace = share.MustNewV0Namespace([]byte(defaultNamespaceStr)) @@ -52,10 +55,11 @@ func main() { RunE: func(cmd *cobra.Command, _ []string) error { numBlocks, _ := cmd.Flags().GetInt("num-blocks") blockSize, _ := cmd.Flags().GetInt("block-size") - squareSize, _ := cmd.Flags().GetInt("square-size") blockInterval, _ := cmd.Flags().GetDuration("block-interval") existingDir, _ := cmd.Flags().GetString("existing-dir") namespaceStr, _ := cmd.Flags().GetString("namespace") + upToTime, _ := cmd.Flags().GetBool("up-to-now") + appVersion, _ := cmd.Flags().GetUint64("app-version") var namespace share.Namespace if namespaceStr == "" { namespace = defaultNamespace @@ -70,11 +74,12 @@ func main() { cfg := BuilderConfig{ NumBlocks: numBlocks, BlockSize: blockSize, - SquareSize: squareSize, BlockInterval: blockInterval, ExistingDir: existingDir, Namespace: namespace, ChainID: tmrand.Str(6), + UpToTime: upToTime, + AppVersion: appVersion, } dir, err := os.Getwd() @@ -88,10 +93,11 @@ func main() { rootCmd.Flags().Int("num-blocks", 100, "Number of blocks to generate") rootCmd.Flags().Int("block-size", appconsts.DefaultMaxBytes, "Size of each block in bytes") - rootCmd.Flags().Int("square-size", appconsts.DefaultSquareSizeUpperBound, "Size of the square") rootCmd.Flags().Duration("block-interval", time.Second, "Interval between blocks") rootCmd.Flags().String("existing-dir", "", "Existing directory to load chain from") rootCmd.Flags().String("namespace", "", "Custom namespace for the chain") + rootCmd.Flags().Bool("up-to-now", false, "Tool will terminate if the block time reaches the current time") + rootCmd.Flags().Uint64("app-version", appconsts.LatestVersion, "App version to use for the chain") if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) @@ -101,11 +107,12 @@ func main() { type BuilderConfig struct { NumBlocks int BlockSize int - SquareSize int BlockInterval time.Duration ExistingDir string Namespace share.Namespace ChainID string + AppVersion uint64 + UpToTime bool } func Run(ctx context.Context, cfg BuilderConfig, dir string) error { @@ -130,7 +137,7 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { appCfg := app.DefaultAppConfig() appCfg.Pruning = "everything" // we just want the last two states cp := app.DefaultConsensusParams() - cp.Version.AppVersion = 2 + cp.Version.AppVersion = cfg.AppVersion // set the app version gen = genesis.NewDefaultGenesis(). WithConsensusParams(cp). WithKeyring(kr). @@ -287,9 +294,8 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { lastBlock := blockStore.LoadBlock(blockStore.Height()) for height := lastHeight + 1; height <= int64(cfg.NumBlocks)+lastHeight; height++ { - if lastBlock.Time.Add(cfg.BlockInterval).After(time.Now().UTC()) { - fmt.Println(fmt.Sprintf("blocks cannot be generated into the future, stopping at height %d", - lastBlock.Height)) + if cfg.UpToTime && lastBlock != nil && lastBlock.Time.Add(cfg.BlockInterval).After(time.Now().UTC()) { + fmt.Printf("blocks cannot be generated into the future, stopping at height %d\n", lastBlock.Height) break } @@ -456,7 +462,7 @@ func generateSquareRoutine( dataSquare, txs, err := square.Build( [][]byte{tx}, - cfg.SquareSize, + maxSquareSize, appconsts.SubtreeRootThreshold(1), ) if err != nil { From cf534ba922aa62c9170f5bfd8df01e618990418d Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 11:38:11 +0200 Subject: [PATCH 08/12] catch some errors for height differences --- tools/chainbuilder/main.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 755563d089..3b24530382 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -197,9 +197,12 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { baseapp.SetMinGasPrices(fmt.Sprintf("%f%s", appconsts.DefaultMinGasPrice, appconsts.BondDenom)), ) - _ = simApp.Info(abci.RequestInfo{}) + infoResp := simApp.Info(abci.RequestInfo{}) lastHeight := blockStore.Height() + if infoResp.LastBlockHeight != lastHeight { + return fmt.Errorf("last application height is %d, but the block store height is %d", infoResp.LastBlockHeight, lastHeight) + } if lastHeight == 0 { if gen == nil { @@ -257,6 +260,14 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { currentTime = state.LastBlockTime.Add(cfg.BlockInterval) } + if state.ConsensusParams.Version.AppVersion != cfg.AppVersion { + return fmt.Errorf("app version mismatch: state has %d, but cfg has %d", state.ConsensusParams.Version.AppVersion, cfg.AppVersion) + } + + if state.LastBlockHeight != lastHeight { + return fmt.Errorf("last block height mismatch: state has %d, but block store has %d", state.LastBlockHeight, lastHeight) + } + validatorPower := state.Validators.Validators[0].VotingPower signer, err := user.NewSigner( From df5b62a4e06d333ec6dfeb6d3c95059efd6fe9e4 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 11:39:35 +0200 Subject: [PATCH 09/12] make sure block store is committed before state store --- tools/chainbuilder/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 3b24530382..041777eea0 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -523,15 +523,15 @@ func persistDataRoutine( if !ok { return nil } - - if err := stateStore.Save(data.state); err != nil { - return err - } blockParts := data.block.MakePartSet(types.BlockPartSizeBytes) blockStore.SaveBlock(data.block, blockParts, data.seenCommit) if blockStore.Height()%100 == 0 { fmt.Println("Reached height", blockStore.Height()) } + + if err := stateStore.Save(data.state); err != nil { + return err + } } } } From fb01eb0ba86455843125b62d1c2d40d70f19f19b Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 12:03:29 +0200 Subject: [PATCH 10/12] allow to set the chain ID --- tools/chainbuilder/main.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 041777eea0..e2c67be222 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -60,6 +60,7 @@ func main() { namespaceStr, _ := cmd.Flags().GetString("namespace") upToTime, _ := cmd.Flags().GetBool("up-to-now") appVersion, _ := cmd.Flags().GetUint64("app-version") + chainID, _ := cmd.Flags().GetString("chain-id") var namespace share.Namespace if namespaceStr == "" { namespace = defaultNamespace @@ -82,6 +83,10 @@ func main() { AppVersion: appVersion, } + if chainID == "" { + cfg.ChainID = tmrand.Str(6) + } + dir, err := os.Getwd() if err != nil { return fmt.Errorf("failed to get current working directory: %w", err) @@ -98,6 +103,9 @@ func main() { rootCmd.Flags().String("namespace", "", "Custom namespace for the chain") rootCmd.Flags().Bool("up-to-now", false, "Tool will terminate if the block time reaches the current time") rootCmd.Flags().Uint64("app-version", appconsts.LatestVersion, "App version to use for the chain") + rootCmd.Flags().String("chain-id", "", "Chain ID to use for the chain. Defaults to a random 6 character string") + rootCmd.SilenceUsage = true + rootCmd.SilenceErrors = true if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) From 4f016df67e3ef47595fa4fef39e1a8da168f4c7d Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 12:06:49 +0200 Subject: [PATCH 11/12] fix chainID --- tools/chainbuilder/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index e2c67be222..2307ddea1f 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -83,8 +83,8 @@ func main() { AppVersion: appVersion, } - if chainID == "" { - cfg.ChainID = tmrand.Str(6) + if chainID != "" { + cfg.ChainID = chainID } dir, err := os.Getwd() From 13f5cc7420bd4b4ac4e7a52a4882e2b1975e4f90 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 23 Sep 2024 12:10:47 +0200 Subject: [PATCH 12/12] disable snapshots --- tools/chainbuilder/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/chainbuilder/main.go b/tools/chainbuilder/main.go index 2307ddea1f..fe385084b3 100644 --- a/tools/chainbuilder/main.go +++ b/tools/chainbuilder/main.go @@ -144,7 +144,9 @@ func Run(ctx context.Context, cfg BuilderConfig, dir string) error { validator := genesis.NewDefaultValidator(testnode.DefaultValidatorAccountName) appCfg := app.DefaultAppConfig() appCfg.Pruning = "everything" // we just want the last two states + appCfg.StateSync.SnapshotInterval = 0 cp := app.DefaultConsensusParams() + cp.Version.AppVersion = cfg.AppVersion // set the app version gen = genesis.NewDefaultGenesis(). WithConsensusParams(cp).