-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add test/util/genesis on v1.x (#3520)
## Overview adding genesis to utils as part of #2414
- Loading branch information
1 parent
21b5bc7
commit 9c6322c
Showing
6 changed files
with
649 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Oops, something went wrong.