From d8d91e51791e80db92b89f0b54e5d84b0bd1c20a Mon Sep 17 00:00:00 2001 From: Evan Forbes <42654277+evan-forbes@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:57:18 -0500 Subject: [PATCH] refactor!: genesis creation into its own package (#2455) ## Overview This PR refactors genesis creation into its own package. Besides a general simplification and isolation of logic, this PR also allows for the ease of adding multiple validators to the genesis. The goal being that we can eventually use this genesis creation code across all of our testing utilities that require it including testnode, e2e, the testapp integration tests, and specificlally testground. This change was spun out of my recent efforts to refactor the testground tests and put them into celestia-app. #2033 ## Checklist - [x] New and updated code has appropriate documentation - [x] New and updated code has new and/or updated testing - [x] Required CI checks are passing - [x] Visual proof for any user facing features like CLI or documentation updates - [ ] Linked issues closed with keywords --------- Co-authored-by: CHAMI Rachid --- app/test/block_production_test.go | 2 +- app/test/integration_test.go | 4 +- app/test/max_total_blob_size_test.go | 2 +- app/test/prepare_proposal_context_test.go | 2 +- app/test/priority_test.go | 6 +- app/test/qgb_rpc_test.go | 6 +- app/test/square_size_test.go | 3 +- app/test/std_sdk_test.go | 16 +- pkg/user/signer_test.go | 2 +- test/cmd/txsim/cli_test.go | 7 +- test/e2e/setup.go | 4 +- test/e2e/simple_test.go | 2 +- test/e2e/testnet.go | 8 +- test/txsim/run_test.go | 4 +- test/util/genesis/accounts.go | 139 ++++++++++++ test/util/genesis/document.go | 115 ++++++++++ test/util/genesis/files.go | 69 ++++++ test/util/genesis/genesis.go | 212 ++++++++++++++++++ .../modifier.go} | 26 ++- test/util/genesis/util.go | 23 ++ test/util/malicious/app_test.go | 2 +- test/util/test_app.go | 4 +- test/util/testnode/config.go | 93 ++++---- test/util/testnode/full_node.go | 104 +-------- test/util/testnode/full_node_test.go | 9 +- test/util/testnode/node_init.go | 193 ---------------- x/qgb/integration_test.go | 2 +- x/upgrade/test/integration_test.go | 5 +- 28 files changed, 680 insertions(+), 384 deletions(-) create mode 100644 test/util/genesis/accounts.go create mode 100644 test/util/genesis/document.go create mode 100644 test/util/genesis/files.go create mode 100644 test/util/genesis/genesis.go rename test/util/{testnode/genesis_options.go => genesis/modifier.go} (75%) create mode 100644 test/util/genesis/util.go delete mode 100644 test/util/testnode/node_init.go diff --git a/app/test/block_production_test.go b/app/test/block_production_test.go index 11674b3e95..f8bfb5435a 100644 --- a/app/test/block_production_test.go +++ b/app/test/block_production_test.go @@ -36,7 +36,7 @@ func (s *BlockProductionTestSuite) SetupSuite() { } cfg := testnode.DefaultConfig(). - WithAccounts(accounts). + WithFundedAccounts(accounts...). WithTimeoutCommit(s.timeoutCommit) cctx, _, _ := testnode.NewNetwork(t, cfg) diff --git a/app/test/integration_test.go b/app/test/integration_test.go index cc78399dda..3fcb9d5dfd 100644 --- a/app/test/integration_test.go +++ b/app/test/integration_test.go @@ -60,7 +60,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.accounts[i] = tmrand.Str(20) } - cfg := testnode.DefaultConfig().WithAccounts(s.accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(s.accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) @@ -249,7 +249,7 @@ func (s *IntegrationTestSuite) TestSubmitPayForBlob() { "medium random with timeout height", mustNewBlob(ns1, tmrand.Bytes(100000), appconsts.ShareVersionZero), []user.TxOption{ - user.SetTimeoutHeight(1000), + user.SetTimeoutHeight(10000), user.SetGasLimit(1_000_000_000), }, }, diff --git a/app/test/max_total_blob_size_test.go b/app/test/max_total_blob_size_test.go index 2bd26eaa6a..87764727f0 100644 --- a/app/test/max_total_blob_size_test.go +++ b/app/test/max_total_blob_size_test.go @@ -52,7 +52,7 @@ func (s *MaxTotalBlobSizeSuite) SetupSuite() { cParams.Block.MaxBytes = 10 * mebibyte cfg := testnode.DefaultConfig(). - WithAccounts(s.accounts). + WithFundedAccounts(s.accounts...). WithTendermintConfig(tmConfig). WithConsensusParams(cParams) diff --git a/app/test/prepare_proposal_context_test.go b/app/test/prepare_proposal_context_test.go index fd6e6e27b8..ff52880fa6 100644 --- a/app/test/prepare_proposal_context_test.go +++ b/app/test/prepare_proposal_context_test.go @@ -33,7 +33,7 @@ func TestTimeInPrepareProposalContext(t *testing.T) { for i := 0; i < len(accounts); i++ { accounts[i] = tmrand.Str(9) } - cfg := testnode.DefaultConfig().WithAccounts(accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) ecfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) vestAccName := "vesting" diff --git a/app/test/priority_test.go b/app/test/priority_test.go index b83f58e9bc..c7cffd07ef 100644 --- a/app/test/priority_test.go +++ b/app/test/priority_test.go @@ -43,7 +43,7 @@ func (s *PriorityTestSuite) SetupSuite() { t := s.T() cfg := testnode.DefaultConfig(). - WithAccounts(testfactory.GenerateAccounts(10)). + WithFundedAccounts(testfactory.GenerateAccounts(10)...). // use a long block time to guarantee that some transactions are included in the same block WithTimeoutCommit(time.Second) @@ -54,8 +54,8 @@ func (s *PriorityTestSuite) SetupSuite() { require.NoError(t, cctx.WaitForNextBlock()) - for _, acc := range cfg.Accounts { - addr := testfactory.GetAddress(s.cctx.Keyring, acc) + for _, acc := range cfg.Genesis.Accounts() { + addr := testfactory.GetAddress(s.cctx.Keyring, acc.Name) signer, err := user.SetupSigner(s.cctx.GoContext(), s.cctx.Keyring, s.cctx.GRPCClient, addr, s.ecfg) signer.SetPollTime(time.Millisecond * 300) require.NoError(t, err) diff --git a/app/test/qgb_rpc_test.go b/app/test/qgb_rpc_test.go index f385328759..13f1816567 100644 --- a/app/test/qgb_rpc_test.go +++ b/app/test/qgb_rpc_test.go @@ -5,6 +5,9 @@ import ( "testing" "time" + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/celestiaorg/celestia-app/x/qgb/types" "github.com/stretchr/testify/assert" @@ -15,7 +18,8 @@ func TestQGBRPCQueries(t *testing.T) { if testing.Short() { t.Skip("skipping QGB integration test in short mode.") } - cfg := testnode.DefaultConfig() + ecfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + cfg := testnode.DefaultConfig().WithModifiers(genesis.SetDataCommitmentWindow(ecfg.Codec, 100)) cctx, _, _ := testnode.NewNetwork(t, cfg) diff --git a/app/test/square_size_test.go b/app/test/square_size_test.go index 0f3e5ae1a8..0577cc635d 100644 --- a/app/test/square_size_test.go +++ b/app/test/square_size_test.go @@ -12,6 +12,7 @@ import ( "github.com/celestiaorg/celestia-app/pkg/user" "github.com/celestiaorg/celestia-app/test/txsim" "github.com/celestiaorg/celestia-app/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testfactory" "github.com/celestiaorg/celestia-app/test/util/testnode" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" @@ -46,7 +47,7 @@ func (s *SquareSizeIntegrationTest) SetupSuite() { t.Log("setting up square size integration test") s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) cfg := testnode.DefaultConfig(). - WithGenesisOptions(testnode.ImmediateProposals(s.ecfg.Codec)) + WithModifiers(genesis.ImmediateProposals(s.ecfg.Codec)) cctx, rpcAddr, grpcAddr := testnode.NewNetwork(t, cfg) diff --git a/app/test/std_sdk_test.go b/app/test/std_sdk_test.go index c64c740642..9add7bbdfc 100644 --- a/app/test/std_sdk_test.go +++ b/app/test/std_sdk_test.go @@ -51,14 +51,14 @@ func (s *StandardSDKIntegrationTestSuite) SetupSuite() { t := s.T() t.Log("setting up integration test suite") - accounts := make([]string, 300) + accounts := make([]string, 35) for i := 0; i < len(accounts); i++ { accounts[i] = tmrand.Str(9) } - cfg := testnode.DefaultConfig().WithAccounts(accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) - s.accounts = cfg.Accounts + s.accounts = accounts s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) s.cctx = cctx } @@ -108,7 +108,7 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() { { name: "delegate 1 TIA", msgFunc: func() (msgs []sdk.Msg, signer string) { - valopAddr := sdk.ValAddress(testfactory.GetAddress(s.cctx.Keyring, "validator")) + valopAddr := sdk.ValAddress(testfactory.GetAddress(s.cctx.Keyring, testnode.DefaultValidatorAccountName)) account1 := s.unusedAccount() account1Addr := testfactory.GetAddress(s.cctx.Keyring, account1) msg := stakingtypes.NewMsgDelegate(account1Addr, valopAddr, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000))) @@ -119,10 +119,10 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() { { name: "undelegate 1 TIA", msgFunc: func() (msgs []sdk.Msg, signer string) { - valAccAddr := testfactory.GetAddress(s.cctx.Keyring, "validator") + valAccAddr := testfactory.GetAddress(s.cctx.Keyring, testnode.DefaultValidatorAccountName) valopAddr := sdk.ValAddress(valAccAddr) msg := stakingtypes.NewMsgUndelegate(valAccAddr, valopAddr, sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000))) - return []sdk.Msg{msg}, "validator" + return []sdk.Msg{msg}, testnode.DefaultValidatorAccountName }, expectedCode: abci.CodeTypeOK, }, @@ -136,10 +136,10 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() { msg, err := stakingtypes.NewMsgCreateValidator( valopAddr, pv.PrivKey.PubKey(), - sdk.NewCoin(app.BondDenom, sdk.NewInt(1000000)), + sdk.NewCoin(app.BondDenom, sdk.NewInt(1)), stakingtypes.NewDescription("taco tuesday", "my keybase", "www.celestia.org", "ping @celestiaorg on twitter", "fake validator"), stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(6, 0o2), sdk.NewDecWithPrec(12, 0o2), sdk.NewDecWithPrec(1, 0o2)), - sdk.NewInt(1000000), + sdk.NewInt(1), ) require.NoError(t, err) return []sdk.Msg{msg}, account diff --git a/pkg/user/signer_test.go b/pkg/user/signer_test.go index 99ff7259bd..4b062d690c 100644 --- a/pkg/user/signer_test.go +++ b/pkg/user/signer_test.go @@ -34,7 +34,7 @@ type SignerTestSuite struct { func (s *SignerTestSuite) SetupSuite() { s.encCfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) - s.ctx, _, _ = testnode.NewNetwork(s.T(), testnode.DefaultConfig().WithAccounts([]string{"a"})) + s.ctx, _, _ = testnode.NewNetwork(s.T(), testnode.DefaultConfig().WithFundedAccounts("a")) _, err := s.ctx.WaitForHeight(1) s.Require().NoError(err) rec, err := s.ctx.Keyring.Key("a") diff --git a/test/cmd/txsim/cli_test.go b/test/cmd/txsim/cli_test.go index f802c309d6..491b9c850e 100644 --- a/test/cmd/txsim/cli_test.go +++ b/test/cmd/txsim/cli_test.go @@ -9,6 +9,7 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testfactory" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -65,8 +66,10 @@ func setup(t testing.TB) (keyring.Keyring, string, string) { cfg := testnode.DefaultConfig(). WithConsensusParams(cparams). - WithAccounts([]string{testfactory.TestAccName}). - WithGenesisOptions(testnode.FundAccounts(cdc, []sdk.AccAddress{testnode.TestAddress()}, sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1e15)))) + WithFundedAccounts(testfactory.TestAccName). + WithModifiers( + genesis.FundAccounts(cdc, []sdk.AccAddress{testnode.TestAddress()}, sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1e15))), + ) cctx, rpcAddr, grpcAddr := testnode.NewNetwork(t, cfg) diff --git a/test/e2e/setup.go b/test/e2e/setup.go index e883fc4080..709c410876 100644 --- a/test/e2e/setup.go +++ b/test/e2e/setup.go @@ -23,12 +23,12 @@ import ( "github.com/tendermint/tendermint/types" ) -type GenesisAccount struct { +type Account struct { PubKey cryptotypes.PubKey InitialTokens int64 } -func MakeGenesis(nodes []*Node, accounts []*GenesisAccount) (types.GenesisDoc, error) { +func MakeGenesis(nodes []*Node, accounts []*Account) (types.GenesisDoc, error) { encCdc := encoding.MakeConfig(app.ModuleEncodingRegisters...) appGenState := app.ModuleBasics.DefaultGenesis(encCdc.Codec) bankGenesis := bank.DefaultGenesisState() diff --git a/test/e2e/simple_test.go b/test/e2e/simple_test.go index 92788979e2..71433ad69d 100644 --- a/test/e2e/simple_test.go +++ b/test/e2e/simple_test.go @@ -35,7 +35,7 @@ func TestE2ESimple(t *testing.T) { t.Cleanup(testnet.Cleanup) require.NoError(t, testnet.CreateGenesisNodes(4, latestVersion, 10000000)) - kr, err := testnet.CreateGenesisAccount("alice", 1e12) + kr, err := testnet.CreateAccount("alice", 1e12) require.NoError(t, err) require.NoError(t, testnet.Setup()) diff --git a/test/e2e/testnet.go b/test/e2e/testnet.go index ad3a104662..3bda11279e 100644 --- a/test/e2e/testnet.go +++ b/test/e2e/testnet.go @@ -16,7 +16,7 @@ import ( type Testnet struct { seed int64 nodes []*Node - genesisAccounts []*GenesisAccount + genesisAccounts []*Account keygen *keyGenerator } @@ -29,7 +29,7 @@ func New(name string, seed int64) (*Testnet, error) { return &Testnet{ seed: seed, nodes: make([]*Node, 0), - genesisAccounts: make([]*GenesisAccount, 0), + genesisAccounts: make([]*Account, 0), keygen: newKeyGenerator(seed), }, nil } @@ -67,7 +67,7 @@ func (t *Testnet) CreateNode(version string, startHeight int64) error { return nil } -func (t *Testnet) CreateGenesisAccount(name string, tokens int64) (keyring.Keyring, error) { +func (t *Testnet) CreateAccount(name string, tokens int64) (keyring.Keyring, error) { cdc := encoding.MakeConfig(app.ModuleEncodingRegisters...).Codec kr := keyring.NewInMemory(cdc) key, _, err := kr.NewMnemonic(name, keyring.English, "", "", hd.Secp256k1) @@ -78,7 +78,7 @@ func (t *Testnet) CreateGenesisAccount(name string, tokens int64) (keyring.Keyri if err != nil { return nil, err } - t.genesisAccounts = append(t.genesisAccounts, &GenesisAccount{ + t.genesisAccounts = append(t.genesisAccounts, &Account{ PubKey: pk, InitialTokens: tokens, }) diff --git a/test/txsim/run_test.go b/test/txsim/run_test.go index b4a0b06ad3..1bdec3792b 100644 --- a/test/txsim/run_test.go +++ b/test/txsim/run_test.go @@ -105,7 +105,7 @@ func TestTxSimulator(t *testing.T) { opts := txsim.DefaultOptions(). SuppressLogs(). - WithPollTime(time.Second) + WithPollTime(time.Millisecond * 100) if tc.useFeegrant { opts.UseFeeGrant() } @@ -144,7 +144,7 @@ func TestTxSimulator(t *testing.T) { func Setup(t testing.TB) (keyring.Keyring, string, string) { t.Helper() - cfg := testnode.DefaultConfig() + cfg := testnode.DefaultConfig().WithTimeoutCommit(300 * time.Millisecond).WithFundedAccounts("txsim-master") cctx, rpcAddr, grpcAddr := testnode.NewNetwork(t, cfg) diff --git a/test/util/genesis/accounts.go b/test/util/genesis/accounts.go new file mode 100644 index 0000000000..6605949443 --- /dev/null +++ b/test/util/genesis/accounts.go @@ -0,0 +1,139 @@ +package genesis + +import ( + "fmt" + mrand "math/rand" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/tendermint/tendermint/crypto" +) + +type Account struct { + Name string + InitialTokens int64 +} + +func NewAccounts(initBal int64, names ...string) []Account { + accounts := make([]Account, len(names)) + for i, name := range names { + accounts[i] = Account{ + Name: name, + InitialTokens: initBal, + } + } + return accounts +} + +func (ga *Account) ValidateBasic() error { + if ga.Name == "" { + return fmt.Errorf("name cannot be empty") + } + if ga.InitialTokens <= 0 { + return fmt.Errorf("initial tokens must be positive") + } + return nil +} + +type Validator struct { + Account + Stake int64 + + // ConsensusKey is the key used by the validator to sign votes. + ConsensusKey crypto.PrivKey + NetworkKey crypto.PrivKey +} + +func NewDefaultValidator(name string) Validator { + r := mrand.New(mrand.NewSource(time.Now().UnixNano())) + return Validator{ + Account: Account{ + Name: name, + InitialTokens: 999_999_999_999_999_999, + }, + Stake: 99_999_999_999_999_999, // save some tokens for fees + ConsensusKey: GenerateEd25519(NewSeed(r)), + NetworkKey: GenerateEd25519(NewSeed(r)), + } +} + +// ValidateBasic performs stateless validation on the validitor +func (v *Validator) ValidateBasic() error { + if err := v.Account.ValidateBasic(); err != nil { + return err + } + if v.Stake <= 0 { + return fmt.Errorf("stake must be positive") + } + if v.ConsensusKey == nil { + return fmt.Errorf("consensus key cannot be empty") + } + if v.Stake > v.InitialTokens { + return fmt.Errorf("stake cannot be greater than initial tokens") + } + return nil +} + +// GenTx generates a genesis transaction to create a validator as configured by +// the validator struct. It assumes the validator's genesis account has already +// been added to the keyring and that the sequence for that account is 0. +func (v *Validator) GenTx(ecfg encoding.Config, kr keyring.Keyring, chainID string) (sdk.Tx, error) { + rec, err := kr.Key(v.Name) + if err != nil { + return nil, err + } + addr, err := rec.GetAddress() + if err != nil { + return nil, err + } + + commission, err := sdk.NewDecFromStr("0.5") + if err != nil { + return nil, err + } + + pk, err := cryptocodec.FromTmPubKeyInterface(v.ConsensusKey.PubKey()) + if err != nil { + return nil, fmt.Errorf("converting public key for node %s: %w", v.Name, err) + } + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + pk, + sdk.NewCoin(app.BondDenom, sdk.NewInt(v.Stake)), + stakingtypes.NewDescription(v.Name, "", "", "", ""), + stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), + sdk.NewInt(v.Stake/2), + ) + if err != nil { + return nil, err + } + + fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) + txBuilder := ecfg.TxConfig.NewTxBuilder() + err = txBuilder.SetMsgs(createValMsg) + if err != nil { + return nil, err + } + txBuilder.SetFeeAmount(fee) // Arbitrary fee + txBuilder.SetGasLimit(1000000) // Need at least 100386 + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(chainID). + WithKeybase(kr). + WithTxConfig(ecfg.TxConfig) + + err = tx.Sign(txFactory, v.Name, txBuilder, true) + if err != nil { + return nil, err + } + + return txBuilder.GetTx(), nil +} diff --git a/test/util/genesis/document.go b/test/util/genesis/document.go new file mode 100644 index 0000000000..4c18840dc1 --- /dev/null +++ b/test/util/genesis/document.go @@ -0,0 +1,115 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// Document will create a valid genesis doc with funded addresses. +func Document( + ecfg encoding.Config, + params *tmproto.ConsensusParams, + chainID string, + gentxs []json.RawMessage, + addrs []string, + pubkeys []cryptotypes.PubKey, + mods ...Modifier, +) (*coretypes.GenesisDoc, error) { + genutilGenState := genutiltypes.DefaultGenesisState() + genutilGenState.GenTxs = gentxs + + genBals, genAccs, err := accountsToSDKTypes(addrs, pubkeys) + if err != nil { + return nil, err + } + + accounts, err := authtypes.PackAccounts(genAccs) + if err != nil { + return nil, err + } + + authGenState := authtypes.DefaultGenesisState() + bankGenState := banktypes.DefaultGenesisState() + authGenState.Accounts = append(authGenState.Accounts, accounts...) + bankGenState.Balances = append(bankGenState.Balances, genBals...) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + // perform some basic validation of the genesis state + if err := authtypes.ValidateGenesis(*authGenState); err != nil { + return nil, err + } + if err := bankGenState.Validate(); err != nil { + return nil, err + } + if err := genutiltypes.ValidateGenesis(genutilGenState, ecfg.TxConfig.TxJSONDecoder()); err != nil { + return nil, err + } + + state := app.ModuleBasics.DefaultGenesis(ecfg.Codec) + state[authtypes.ModuleName] = ecfg.Codec.MustMarshalJSON(authGenState) + state[banktypes.ModuleName] = ecfg.Codec.MustMarshalJSON(bankGenState) + state[genutiltypes.ModuleName] = ecfg.Codec.MustMarshalJSON(genutilGenState) + + for _, modifer := range mods { + state = modifer(state) + } + + stateBz, err := json.MarshalIndent(state, "", " ") + if err != nil { + return nil, err + } + + // Create the genesis doc + genesisDoc := &coretypes.GenesisDoc{ + ChainID: chainID, + GenesisTime: time.Now(), + ConsensusParams: params, + AppState: stateBz, + } + + return genesisDoc, nil +} + +// accountsToSDKTypes converts the genesis accounts to native SDK types. +func accountsToSDKTypes(addrs []string, pubkeys []cryptotypes.PubKey) ([]banktypes.Balance, []authtypes.GenesisAccount, error) { + if len(addrs) != len(pubkeys) { + return nil, nil, fmt.Errorf("length of addresses and public keys are not equal") + } + genBals := make([]banktypes.Balance, len(addrs)) + genAccs := make([]authtypes.GenesisAccount, len(addrs)) + hasMap := make(map[string]bool) + for i, addr := range addrs { + if hasMap[addr] { + return nil, nil, fmt.Errorf("duplicate account address %s", addr) + } + hasMap[addr] = true + + pubKey := pubkeys[i] + + balances := sdk.NewCoins( + sdk.NewCoin(appconsts.BondDenom, sdk.NewInt(999_999_999_999_999_999)), + ) + + genBals[i] = banktypes.Balance{Address: addr, Coins: balances.Sort()} + + parsedAddress, err := sdk.AccAddressFromBech32(addr) + if err != nil { + return nil, nil, err + } + + genAccs[i] = authtypes.NewBaseAccount(parsedAddress, pubKey, uint64(i), 0) + } + return genBals, genAccs, nil +} diff --git a/test/util/genesis/files.go b/test/util/genesis/files.go new file mode 100644 index 0000000000..3272b46537 --- /dev/null +++ b/test/util/genesis/files.go @@ -0,0 +1,69 @@ +package genesis + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/tendermint/tendermint/config" + tmos "github.com/tendermint/tendermint/libs/os" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/privval" +) + +// InitFiles initializes the files for a new tendermint node with the provided +// genesis. It will use the validatorIndex to save the validator's consensus +// key. +func InitFiles( + dir string, + tmCfg *config.Config, + g *Genesis, + validatorIndex int, +) (string, error) { + val, has := g.Validator(validatorIndex) + if !has { + return "", fmt.Errorf("validator %d not found", validatorIndex) + } + + basePath := filepath.Join(dir, ".celestia-app") + tmCfg.SetRoot(basePath) + + // save the genesis file + configPath := filepath.Join(basePath, "config") + err := os.MkdirAll(configPath, os.ModePerm) + if err != nil { + return "", err + } + gDoc, err := g.Export() + if err != nil { + return "", err + } + err = gDoc.SaveAs(tmCfg.GenesisFile()) + if err != nil { + return "", err + } + + pvStateFile := tmCfg.PrivValidatorStateFile() + if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { + return "", err + } + pvKeyFile := tmCfg.PrivValidatorKeyFile() + if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { + return "", err + } + filePV := privval.NewFilePV(val.ConsensusKey, pvKeyFile, pvStateFile) + filePV.Save() + + nodeKeyFile := tmCfg.NodeKeyFile() + if err := tmos.EnsureDir(filepath.Dir(nodeKeyFile), 0o777); err != nil { + return "", err + } + nodeKey := &p2p.NodeKey{ + PrivKey: val.NetworkKey, + } + if err := nodeKey.SaveAs(nodeKeyFile); err != nil { + return "", err + } + + return basePath, nil +} diff --git a/test/util/genesis/genesis.go b/test/util/genesis/genesis.go new file mode 100644 index 0000000000..560d20291d --- /dev/null +++ b/test/util/genesis/genesis.go @@ -0,0 +1,212 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// Genesis manages the creation of the genesis state of a network. It is meant +// to be used as the first step to any test that requires a network. +type Genesis struct { + ecfg encoding.Config + // ConsensusParams are the consensus parameters of the network. + ConsensusParams *tmproto.ConsensusParams + // ChainID is the chain ID of the network. + ChainID string + // GenesisTime is the genesis time of the network. + GenesisTime time.Time + + // kr is the keyring used to generate the genesis accounts and validators. + // Transaction keys for all genesis accounts are stored in this keyring and + // are indexed by account name. Public keys and addresses can be derived + // from those keys using the existing keyring API. + kr keyring.Keyring + + // accounts are the genesis accounts that will be included in the genesis. + accounts []Account + // validators are the validators of the network. Note that each validator + // also has a genesis account. + validators []Validator + // genTxs are the genesis transactions that will be included in the genesis. + // Transactions are generated upon adding a validator to the genesis. + genTxs []sdk.Tx + genOps []Modifier +} + +// NewDefaultGenesis creates a new default genesis with no accounts or validators. +func NewDefaultGenesis() *Genesis { + ecfg := encoding.MakeConfig(app.ModuleBasics) + g := &Genesis{ + ecfg: ecfg, + ConsensusParams: DefaultConsensusParams(), + ChainID: tmrand.Str(6), + GenesisTime: time.Now(), + kr: keyring.NewInMemory(ecfg.Codec), + genOps: []Modifier{}, + } + return g +} + +func (g *Genesis) WithModifiers(ops ...Modifier) *Genesis { + g.genOps = append(g.genOps, ops...) + return g +} + +func (g *Genesis) WithConsensusParams(params *tmproto.ConsensusParams) *Genesis { + g.ConsensusParams = params + return g +} + +func (g *Genesis) WithChainID(chainID string) *Genesis { + g.ChainID = chainID + return g +} + +func (g *Genesis) WithGenesisTime(genesisTime time.Time) *Genesis { + g.GenesisTime = genesisTime + return g +} + +func (g *Genesis) WithValidators(vals ...Validator) *Genesis { + for _, val := range vals { + err := g.AddValidator(val) + if err != nil { + panic(err) + } + } + return g +} + +func (g *Genesis) WithAccounts(accs ...Account) *Genesis { + for _, acc := range accs { + err := g.AddAccount(acc) + if err != nil { + panic(err) + } + } + return g +} + +func (g *Genesis) AddAccount(acc Account) error { + _, err := g.kr.Key(acc.Name) + if err == nil { + return fmt.Errorf("account with name %s already exists", acc.Name) + } + if err := acc.ValidateBasic(); err != nil { + return err + } + _, _, err = g.kr.NewMnemonic(acc.Name, keyring.English, "", "", hd.Secp256k1) + if err != nil { + return err + } + g.accounts = append(g.accounts, acc) + return nil +} + +func (g *Genesis) AddValidator(val Validator) error { + if err := val.ValidateBasic(); err != nil { + return err + } + + // Add the validator's genesis account + if err := g.AddAccount(val.Account); err != nil { + return err + } + + // Add the validator's genesis transaction + gentx, err := val.GenTx(g.ecfg, g.kr, g.ChainID) + if err != nil { + return err + } + + // install the validator + g.genTxs = append(g.genTxs, gentx) + g.validators = append(g.validators, val) + return nil +} + +func (g *Genesis) Accounts() []Account { + return g.accounts +} + +func (g *Genesis) Export() (*coretypes.GenesisDoc, error) { + addrs := make([]string, 0, len(g.accounts)) + pubKeys := make([]cryptotypes.PubKey, 0, len(g.accounts)) + gentxs := make([]json.RawMessage, 0, len(g.genTxs)) + + for _, acc := range g.Accounts() { + rec, err := g.kr.Key(acc.Name) + if err != nil { + return nil, err + } + + addr, err := rec.GetAddress() + if err != nil { + return nil, err + } + + addrs = append(addrs, addr.String()) + + pubK, err := rec.GetPubKey() + if err != nil { + return nil, err + } + + pubKeys = append(pubKeys, pubK) + } + + for _, genTx := range g.genTxs { + bz, err := g.ecfg.TxConfig.TxJSONEncoder()(genTx) + if err != nil { + return nil, err + } + + gentxs = append(gentxs, json.RawMessage(bz)) + } + + return Document( + g.ecfg, + g.ConsensusParams, + g.ChainID, + gentxs, + addrs, + pubKeys, + g.genOps..., + ) +} + +func (g *Genesis) Keyring() keyring.Keyring { + return g.kr +} + +func (g *Genesis) Validators() []Validator { + return g.validators +} + +// Validator returns the validator at the given index. False is returned if the +// index is out of bounds. +func (g *Genesis) Validator(i int) (Validator, bool) { + if i < len(g.validators) { + return g.validators[i], true + } + return Validator{}, false +} + +func DefaultConsensusParams() *tmproto.ConsensusParams { + cparams := coretypes.DefaultConsensusParams() + cparams.Block.TimeIotaMs = 1 + cparams.Block.MaxBytes = appconsts.DefaultMaxBytes + return cparams +} diff --git a/test/util/testnode/genesis_options.go b/test/util/genesis/modifier.go similarity index 75% rename from test/util/testnode/genesis_options.go rename to test/util/genesis/modifier.go index f18894d7fb..9ffe274d11 100644 --- a/test/util/testnode/genesis_options.go +++ b/test/util/genesis/modifier.go @@ -1,4 +1,4 @@ -package testnode +package genesis import ( "encoding/json" @@ -6,6 +6,7 @@ import ( "github.com/celestiaorg/celestia-app/app" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" + qgbtypes "github.com/celestiaorg/celestia-app/x/qgb/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -14,13 +15,13 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" ) -// GenesisOption allows for arbitrary changes to be made on the genesis state +// Modifier allows for arbitrary changes to be made on the genesis state // after initial accounts have been added. It accepts the genesis state as input -// and is expected to return the modifed genesis as output. -type GenesisOption func(state map[string]json.RawMessage) map[string]json.RawMessage +// and is expected to return the modified genesis as output. +type Modifier func(state map[string]json.RawMessage) map[string]json.RawMessage // SetBlobParams will set the provided blob params as genesis state. -func SetBlobParams(codec codec.Codec, params blobtypes.Params) GenesisOption { +func SetBlobParams(codec codec.Codec, params blobtypes.Params) Modifier { return func(state map[string]json.RawMessage) map[string]json.RawMessage { blobGenState := blobtypes.DefaultGenesis() blobGenState.Params = params @@ -31,7 +32,7 @@ func SetBlobParams(codec codec.Codec, params blobtypes.Params) GenesisOption { // ImmediateProposals sets the thresholds for getting a gov proposal to very low // levels. -func ImmediateProposals(codec codec.Codec) GenesisOption { +func ImmediateProposals(codec codec.Codec) Modifier { return func(state map[string]json.RawMessage) map[string]json.RawMessage { gs := v1.DefaultGenesisState() gs.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) @@ -44,10 +45,21 @@ func ImmediateProposals(codec codec.Codec) GenesisOption { } } +// SetDataCommitmentWindow will set the provided data commitment window in the +// qgb module's genesis state. +func SetDataCommitmentWindow(codec codec.Codec, window uint64) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + qgbGenState := qgbtypes.DefaultGenesis() + qgbGenState.Params.DataCommitmentWindow = window + state[qgbtypes.ModuleName] = codec.MustMarshalJSON(qgbGenState) + return state + } +} + // FundAccounts adds a set of accounts to the genesis and then sets their balance as provided. // This is good in the case where you have a separate keyring you want to test against and not // use the one generated by the testnet infra. -func FundAccounts(codec codec.Codec, addresses []sdk.AccAddress, balance sdk.Coin) GenesisOption { +func FundAccounts(codec codec.Codec, addresses []sdk.AccAddress, balance sdk.Coin) Modifier { return func(state map[string]json.RawMessage) map[string]json.RawMessage { // set the accounts in the genesis state var authGenState authtypes.GenesisState diff --git a/test/util/genesis/util.go b/test/util/genesis/util.go new file mode 100644 index 0000000000..42d11288d9 --- /dev/null +++ b/test/util/genesis/util.go @@ -0,0 +1,23 @@ +package genesis + +import ( + "io" + mrand "math/rand" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func NewSeed(r *mrand.Rand) []byte { + seed := make([]byte, ed25519.SeedSize) + + _, err := io.ReadFull(r, seed) + if err != nil { + panic(err) // this shouldn't happen + } + return seed +} + +func GenerateEd25519(seed []byte) crypto.PrivKey { + return ed25519.GenPrivKeyFromSecret(seed) +} diff --git a/test/util/malicious/app_test.go b/test/util/malicious/app_test.go index 40d8cf6fd0..df4bd2bad4 100644 --- a/test/util/malicious/app_test.go +++ b/test/util/malicious/app_test.go @@ -69,7 +69,7 @@ func TestMaliciousTestNode(t *testing.T) { } accounts := testfactory.RandomAccountNames(5) cfg := OutOfOrderNamespaceConfig(5). - WithAccounts(accounts) + WithFundedAccounts(accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) _, err := cctx.WaitForHeight(6) diff --git a/test/util/test_app.go b/test/util/test_app.go index 35efbe9f70..2fde7bbff9 100644 --- a/test/util/test_app.go +++ b/test/util/test_app.go @@ -113,10 +113,10 @@ func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts return testApp, kr } -// AddGenesisAccount mimics the cli addGenesisAccount command, providing an +// AddAccount mimics the cli addAccount command, providing an // account with an allocation of to "token" and "tia" tokens in the genesis // state -func AddGenesisAccount(addr sdk.AccAddress, appState app.GenesisState, cdc codec.Codec) (map[string]json.RawMessage, error) { +func AddAccount(addr sdk.AccAddress, appState app.GenesisState, cdc codec.Codec) (map[string]json.RawMessage, error) { // create concrete account type based on input parameters var genAccount authtypes.GenesisAccount diff --git a/test/util/testnode/config.go b/test/util/testnode/config.go index b6453e804c..5e272e10bf 100644 --- a/test/util/testnode/config.go +++ b/test/util/testnode/config.go @@ -5,6 +5,7 @@ import ( "github.com/celestiaorg/celestia-app/cmd/celestia-appd/cmd" "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/test/util/genesis" pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/server" srvconfig "github.com/cosmos/cosmos-sdk/server/config" @@ -15,33 +16,27 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + DefaultValidatorAccountName = "validator" +) + // Config is the configuration of a test node. type Config struct { - // ChainID is the chain ID of the network. - ChainID string - // GenesisTime is the genesis block time of the network. - GenesisTime time.Time + Genesis *genesis.Genesis // TmConfig is the Tendermint configuration used for the network. TmConfig *tmconfig.Config // AppConfig is the application configuration of the test node. AppConfig *srvconfig.Config - // ConsensusParams are the consensus parameters of the test node. - ConsensusParams *tmproto.ConsensusParams // AppOptions are the application options of the test node. AppOptions *KVAppOptions - // GenesisOptions are the genesis options of the test node. - GenesisOptions []GenesisOption - // Accounts are the accounts of the test node. - Accounts []string // AppCreator is used to create the application for the testnode. AppCreator srvtypes.AppCreator // SupressLogs SupressLogs bool } -// WithChainID sets the ChainID and returns the Config. -func (c *Config) WithChainID(s string) *Config { - c.ChainID = s +func (c *Config) WithGenesis(g *genesis.Genesis) *Config { + c.Genesis = g return c } @@ -57,30 +52,12 @@ func (c *Config) WithAppConfig(conf *srvconfig.Config) *Config { return c } -// WithConsensusParams sets the ConsensusParams and returns the Config. -func (c *Config) WithConsensusParams(params *tmproto.ConsensusParams) *Config { - c.ConsensusParams = params - return c -} - // WithAppOptions sets the AppOptions and returns the Config. func (c *Config) WithAppOptions(opts *KVAppOptions) *Config { c.AppOptions = opts return c } -// WithGenesisOptions sets the GenesisOptions and returns the Config. -func (c *Config) WithGenesisOptions(opts ...GenesisOption) *Config { - c.GenesisOptions = opts - return c -} - -// WithAccounts sets the Accounts and returns the Config. -func (c *Config) WithAccounts(accs []string) *Config { - c.Accounts = accs - return c -} - // WithAppCreator sets the AppCreator and returns the Config. func (c *Config) WithAppCreator(creator srvtypes.AppCreator) *Config { c.AppCreator = creator @@ -93,31 +70,60 @@ func (c *Config) WithSupressLogs(sl bool) *Config { return c } -// WithGensisTime sets the GenesisTime and returns the Config. -func (c *Config) WithGensisTime(t time.Time) *Config { - c.GenesisTime = t - return c -} - // WithTimeoutCommit sets the CommitTimeout and returns the Config. func (c *Config) WithTimeoutCommit(d time.Duration) *Config { c.TmConfig.Consensus.TimeoutCommit = d return c } +// WithFundedAccounts sets the genesis accounts and returns the Config. +func (c *Config) WithFundedAccounts(accounts ...string) *Config { + c.Genesis = c.Genesis.WithAccounts( + genesis.NewAccounts(999999999999999999, accounts...)..., + ) + return c +} + +// WithModifiers sets the genesis options and returns the Config. +func (c *Config) WithModifiers(ops ...genesis.Modifier) *Config { + c.Genesis = c.Genesis.WithModifiers(ops...) + return c +} + +// WithGenesisTime sets the genesis time and returns the Config. +func (c *Config) WithGenesisTime(t time.Time) *Config { + c.Genesis = c.Genesis.WithGenesisTime(t) + return c +} + +// WithChainID sets the chain ID and returns the Config. +func (c *Config) WithChainID(id string) *Config { + c.Genesis = c.Genesis.WithChainID(id) + return c +} + +// WithConsensusParams sets the consensus params and returns the Config. +func (c *Config) WithConsensusParams(params *tmproto.ConsensusParams) *Config { + c.Genesis = c.Genesis.WithConsensusParams(params) + return c +} + func DefaultConfig() *Config { tmcfg := DefaultTendermintConfig() tmcfg.Consensus.TimeoutCommit = 1 * time.Millisecond cfg := &Config{} return cfg. - WithGensisTime(time.Now()). - WithAccounts([]string{}). - WithChainID(tmrand.Str(6)). + WithGenesis( + genesis.NewDefaultGenesis(). + WithChainID(tmrand.Str(6)). + WithGenesisTime(time.Now()). + WithConsensusParams(DefaultParams()). + WithModifiers(). + WithValidators(genesis.NewDefaultValidator(DefaultValidatorAccountName)), + ). WithTendermintConfig(DefaultTendermintConfig()). WithAppConfig(DefaultAppConfig()). - WithConsensusParams(DefaultParams()). WithAppOptions(DefaultAppOptions()). - WithGenesisOptions(). WithAppCreator(cmd.NewAppServer). WithSupressLogs(true) } @@ -156,8 +162,7 @@ func DefaultTendermintConfig() *tmconfig.Config { // before starting the next height. This duration influences the time // interval between blocks. A smaller TimeoutCommit value could lead to // less time between blocks (i.e. shorter block intervals). - tmCfg.Consensus.TimeoutCommit = 300 * time.Millisecond - tmCfg.Consensus.TimeoutPropose = 200 * time.Millisecond + tmCfg.Consensus.TimeoutCommit = 1 * time.Millisecond // set the mempool's MaxTxBytes to allow the testnode to accept a // transaction that fills the entire square. Any blob transaction larger diff --git a/test/util/testnode/full_node.go b/test/util/testnode/full_node.go index 18391ca17b..183d7a5f6d 100644 --- a/test/util/testnode/full_node.go +++ b/test/util/testnode/full_node.go @@ -2,33 +2,22 @@ package testnode import ( "context" - "encoding/json" "fmt" "net" "os" "path/filepath" "testing" - "time" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keyring" srvtypes "github.com/cosmos/cosmos-sdk/server/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proxy" dbm "github.com/tendermint/tm-db" - - "github.com/celestiaorg/celestia-app/app" - "github.com/celestiaorg/celestia-app/app/encoding" - qgbtypes "github.com/celestiaorg/celestia-app/x/qgb/types" ) // NewCometNode creates a ready to use comet node that operates a single @@ -70,82 +59,6 @@ func NewCometNode(t testing.TB, baseDir string, cfg *Config) (*node.Node, srvtyp return tmNode, app, err } -// InitFiles initializes the files for a new tendermint node with the provided -// genesis state and consensus parameters. The provided keyring is used to -// create a validator key and the chainID is used to initialize the genesis -// state. The keyring is returned with the validator account added. -func InitFiles( - t testing.TB, - cparams *tmproto.ConsensusParams, - tmCfg *config.Config, - genState map[string]json.RawMessage, - kr keyring.Keyring, - chainID string, - genesisTime time.Time, -) (string, keyring.Keyring, error) { - baseDir, err := initFileStructure(t, tmCfg) - if err != nil { - return baseDir, kr, err - } - - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - - nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) - if err != nil { - return baseDir, kr, err - } - - err = createValidator(kr, encCfg, pubKey, "validator", nodeID, chainID, baseDir) - if err != nil { - return baseDir, kr, err - } - - err = initGenFiles(cparams, genState, encCfg.Codec, tmCfg.GenesisFile(), chainID, genesisTime) - if err != nil { - return baseDir, kr, err - } - - return baseDir, kr, collectGenFiles(tmCfg, encCfg, pubKey, nodeID, baseDir) -} - -// DefaultGenesisState returns a default genesis state and a keyring with -// accounts that have coins. It adds a default "validator" account that is -// funded and used for the valop address of the single validator. The keyring -// accounts are based on the fundedAccounts parameter. -func DefaultGenesisState(fundedAccounts ...string) (map[string]json.RawMessage, keyring.Keyring, error) { - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - state := app.ModuleBasics.DefaultGenesis(encCfg.Codec) - fundedAccounts = append(fundedAccounts, "validator") - kr, bankBals, authAccs := FundKeyringAccounts(fundedAccounts...) - - // set the accounts in the genesis state - var authGenState authtypes.GenesisState - encCfg.Codec.MustUnmarshalJSON(state[authtypes.ModuleName], &authGenState) - - accounts, err := authtypes.PackAccounts(authAccs) - if err != nil { - return nil, nil, err - } - - authGenState.Accounts = append(authGenState.Accounts, accounts...) - state[authtypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&authGenState) - - // set the balances in the genesis state - var bankGenState banktypes.GenesisState - encCfg.Codec.MustUnmarshalJSON(state[banktypes.ModuleName], &bankGenState) - - bankGenState.Balances = append(bankGenState.Balances, bankBals...) - state[banktypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&bankGenState) - - // use the minimum data commitment window (100) - var qgbGenState qgbtypes.GenesisState - encCfg.Codec.MustUnmarshalJSON(state[qgbtypes.ModuleName], &qgbGenState) - qgbGenState.Params.DataCommitmentWindow = qgbtypes.MinimumDataCommitmentWindow - state[qgbtypes.ModuleName] = encCfg.Codec.MustMarshalJSON(&qgbGenState) - - return state, kr, nil -} - // NewNetwork starts a single valiator celestia-app network using the provided // configurations. Configured accounts will be funded and their keys can be // accessed in keyring returned client.Context. All rpc, p2p, and grpc addresses @@ -161,23 +74,14 @@ func NewNetwork(t testing.TB, cfg *Config) (cctx Context, rpcAddr, grpcAddr stri tmCfg.P2P.ListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", GetFreePort()) tmCfg.RPC.GRPCListenAddress = fmt.Sprintf("tcp://127.0.0.1:%d", GetFreePort()) - genState, kr, err := DefaultGenesisState(cfg.Accounts...) - require.NoError(t, err) - - for _, opt := range cfg.GenesisOptions { - genState = opt(genState) - } - - chainID := cfg.ChainID - genTime := cfg.GenesisTime - - baseDir, kr, err := InitFiles(t, cfg.ConsensusParams, tmCfg, genState, kr, chainID, genTime) + // initialize the genesis file and validator files for the first validator. + baseDir, err := genesis.InitFiles(t.TempDir(), tmCfg, cfg.Genesis, 0) require.NoError(t, err) tmNode, app, err := NewCometNode(t, baseDir, cfg) require.NoError(t, err) - cctx = NewContext(context.TODO(), kr, tmCfg, chainID) + cctx = NewContext(context.TODO(), cfg.Genesis.Keyring(), tmCfg, cfg.Genesis.ChainID) cctx, stopNode, err := StartNode(tmNode, cctx) require.NoError(t, err) diff --git a/test/util/testnode/full_node_test.go b/test/util/testnode/full_node_test.go index 56c3d09f47..a2e9dca5d8 100644 --- a/test/util/testnode/full_node_test.go +++ b/test/util/testnode/full_node_test.go @@ -10,6 +10,7 @@ import ( "github.com/celestiaorg/celestia-app/app/encoding" "github.com/celestiaorg/celestia-app/pkg/appconsts" appns "github.com/celestiaorg/celestia-app/pkg/namespace" + "github.com/celestiaorg/celestia-app/test/util/genesis" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/stretchr/testify/suite" @@ -35,8 +36,8 @@ func (s *IntegrationTestSuite) SetupSuite() { } t := s.T() - accounts := make([]string, 40) - for i := 0; i < 40; i++ { + accounts := make([]string, 10) + for i := 0; i < 10; i++ { accounts[i] = tmrand.Str(10) } @@ -45,8 +46,8 @@ func (s *IntegrationTestSuite) SetupSuite() { blobGenState.Params.GovMaxSquareSize = uint64(appconsts.DefaultSquareSizeUpperBound) cfg := DefaultConfig(). - WithAccounts(accounts). - WithGenesisOptions(SetBlobParams(ecfg.Codec, blobGenState.Params)) + WithFundedAccounts(accounts...). + WithModifiers(genesis.SetBlobParams(ecfg.Codec, blobGenState.Params)) cctx, _, _ := NewNetwork(t, cfg) s.cctx = cctx diff --git a/test/util/testnode/node_init.go b/test/util/testnode/node_init.go deleted file mode 100644 index 2004a46bcb..0000000000 --- a/test/util/testnode/node_init.go +++ /dev/null @@ -1,193 +0,0 @@ -package testnode - -import ( - "encoding/json" - "fmt" - "net/url" - "os" - "path/filepath" - "testing" - "time" - - "github.com/celestiaorg/celestia-app/app" - "github.com/celestiaorg/celestia-app/app/encoding" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/tendermint/tendermint/config" - tmos "github.com/tendermint/tendermint/libs/os" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/tendermint/tendermint/types" -) - -func collectGenFiles(tmCfg *config.Config, encCfg encoding.Config, pubKey cryptotypes.PubKey, nodeID, rootDir string) error { - gentxsDir := filepath.Join(rootDir, "gentxs") - - genFile := tmCfg.GenesisFile() - genDoc, err := types.GenesisDocFromFile(genFile) - if err != nil { - return err - } - - initCfg := genutiltypes.NewInitConfig(genDoc.ChainID, gentxsDir, nodeID, pubKey) - - appState, err := genutil.GenAppStateFromConfig( - encCfg.Codec, - encCfg.TxConfig, - tmCfg, - initCfg, - *genDoc, - banktypes.GenesisBalancesIterator{}, - ) - if err != nil { - return err - } - - genDoc = &types.GenesisDoc{ - GenesisTime: genDoc.GenesisTime, - ChainID: genDoc.ChainID, - Validators: nil, - AppState: appState, - ConsensusParams: genDoc.ConsensusParams, - } - - if err := genDoc.ValidateAndComplete(); err != nil { - return err - } - - return genDoc.SaveAs(genFile) -} - -func initGenFiles( - cparams *tmproto.ConsensusParams, - state map[string]json.RawMessage, - _ codec.Codec, - file, - chainID string, - genTime time.Time, -) error { - appGenStateJSON, err := json.MarshalIndent(state, "", " ") - if err != nil { - return err - } - - genDoc := types.GenesisDoc{ - GenesisTime: genTime, - ChainID: chainID, - AppState: appGenStateJSON, - ConsensusParams: cparams, - Validators: nil, - } - - return genDoc.SaveAs(file) -} - -// createValidator creates a genesis transaction for adding a validator account. -// The transaction is stored in the `test.json` file under the 'baseDir/gentxs`. -func createValidator( - kr keyring.Keyring, - encCfg encoding.Config, - pubKey cryptotypes.PubKey, - valopAcc, - nodeID, - chainID, - baseDir string, -) error { - rec, err := kr.Key(valopAcc) - if err != nil { - return err - } - addr, err := rec.GetAddress() - if err != nil { - return err - } - p2pAddr, _, err := server.FreeTCPAddr() - if err != nil { - return err - } - p2pURL, err := url.Parse(p2pAddr) - if err != nil { - return err - } - commission, err := sdk.NewDecFromStr("0.5") - if err != nil { - return err - } - - createValMsg, err := stakingtypes.NewMsgCreateValidator( - sdk.ValAddress(addr), - pubKey, - sdk.NewCoin(app.BondDenom, sdk.NewInt(100000000)), - stakingtypes.NewDescription("test", "", "", "", ""), - stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), - ) - if err != nil { - return err - } - - memo := fmt.Sprintf("%s@%s:%s", nodeID, p2pURL.Hostname(), p2pURL.Port()) - fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) - txBuilder := encCfg.TxConfig.NewTxBuilder() - err = txBuilder.SetMsgs(createValMsg) - if err != nil { - return err - } - txBuilder.SetFeeAmount(fee) // Arbitrary fee - txBuilder.SetGasLimit(1000000) // Need at least 100386 - txBuilder.SetMemo(memo) - - txFactory := tx.Factory{} - txFactory = txFactory. - WithChainID(chainID). - WithMemo(memo). - WithKeybase(kr). - WithTxConfig(encCfg.TxConfig) - - err = tx.Sign(txFactory, valopAcc, txBuilder, true) - if err != nil { - return err - } - - txBz, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - gentxsDir := filepath.Join(baseDir, "gentxs") - return writeFile(fmt.Sprintf("%v.json", "test"), gentxsDir, txBz) -} - -func writeFile(name string, dir string, contents []byte) error { - writePath := filepath.Join(dir) - file := filepath.Join(writePath, name) - - err := tmos.EnsureDir(writePath, 0o755) - if err != nil { - return err - } - - err = os.WriteFile(file, contents, 0o644) // nolint: gosec - if err != nil { - return err - } - - return nil -} - -func initFileStructure(t testing.TB, tmCfg *config.Config) (string, error) { - basePath := filepath.Join(t.TempDir(), ".celestia-app") - tmCfg.SetRoot(basePath) - configPath := filepath.Join(basePath, "config") - err := os.MkdirAll(configPath, os.ModePerm) - if err != nil { - return "", err - } - return basePath, nil -} diff --git a/x/qgb/integration_test.go b/x/qgb/integration_test.go index 6beb7f1739..834d1cc713 100644 --- a/x/qgb/integration_test.go +++ b/x/qgb/integration_test.go @@ -39,7 +39,7 @@ func (s *QGBIntegrationSuite) SetupSuite() { s.accounts = []string{"jimmy"} - cfg := testnode.DefaultConfig().WithAccounts(s.accounts) + cfg := testnode.DefaultConfig().WithFundedAccounts(s.accounts...) cctx, _, _ := testnode.NewNetwork(t, cfg) s.ecfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) s.cctx = cctx diff --git a/x/upgrade/test/integration_test.go b/x/upgrade/test/integration_test.go index b481c9ef3e..ddae14016d 100644 --- a/x/upgrade/test/integration_test.go +++ b/x/upgrade/test/integration_test.go @@ -9,6 +9,7 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" "github.com/celestiaorg/celestia-app/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/test/util/genesis" "github.com/celestiaorg/celestia-app/test/util/testnode" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" @@ -61,9 +62,9 @@ func (s *UpgradeTestSuite) SetupSuite() { tmCfg.Consensus.TimeoutCommit = 3 * time.Second cfg := testnode.DefaultConfig(). - WithAccounts(accounts). + WithFundedAccounts(accounts...). WithTendermintConfig(tmCfg). - WithGenesisOptions(testnode.ImmediateProposals(s.ecfg.Codec)) + WithModifiers(genesis.ImmediateProposals(s.ecfg.Codec)) cctx, _, _ := testnode.NewNetwork(t, cfg)