Skip to content

Commit

Permalink
chore: add test/util/genesis on v1.x (#3520)
Browse files Browse the repository at this point in the history
## Overview

adding genesis to utils as part of #2414
  • Loading branch information
ninabarbakadze authored Jun 10, 2024
1 parent 21b5bc7 commit 9c6322c
Show file tree
Hide file tree
Showing 6 changed files with 649 additions and 0 deletions.
139 changes: 139 additions & 0 deletions test/util/genesis/accounts.go
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
}
115 changes: 115 additions & 0 deletions test/util/genesis/document.go
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
}
69 changes: 69 additions & 0 deletions test/util/genesis/files.go
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
}
Loading

0 comments on commit 9c6322c

Please sign in to comment.