block-sdk/tests/e2e/validator.go
2023-05-08 14:45:17 -04:00

273 lines
6.7 KiB
Go

package e2e
import (
"encoding/json"
"fmt"
"os"
"path"
"path/filepath"
cometcfg "github.com/cometbft/cometbft/config"
"github.com/cometbft/cometbft/p2p"
"github.com/cometbft/cometbft/privval"
sdkcrypto "github.com/cosmos/cosmos-sdk/crypto"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"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"
sdktx "github.com/cosmos/cosmos-sdk/types/tx"
txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/genutil"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/skip-mev/pob/tests/app"
)
type validator struct {
chain *chain
index int
moniker string
mnemonic string
keyInfo keyring.Record
privateKey cryptotypes.PrivKey
consensusKey privval.FilePVKey
nodeKey p2p.NodeKey
}
func (v *validator) instanceName() string {
return fmt.Sprintf("%s%d", v.moniker, v.index)
}
func (v *validator) configDir() string {
return fmt.Sprintf("%s/%s", v.chain.configDir(), v.instanceName())
}
func (v *validator) createConfig() error {
p := path.Join(v.configDir(), "config")
return os.MkdirAll(p, 0o755)
}
func (v *validator) init() error {
if err := v.createConfig(); err != nil {
return err
}
serverCtx := server.NewDefaultContext()
config := serverCtx.Config
config.SetRoot(v.configDir())
config.Moniker = v.moniker
genDoc, err := getGenDoc(v.configDir())
if err != nil {
return err
}
appState, err := json.MarshalIndent(app.ModuleBasics.DefaultGenesis(cdc), "", " ")
if err != nil {
return fmt.Errorf("failed to JSON encode app genesis state: %w", err)
}
genDoc.ChainID = v.chain.id
genDoc.Validators = nil
genDoc.AppState = appState
if err = genutil.ExportGenesisFile(genDoc, config.GenesisFile()); err != nil {
return fmt.Errorf("failed to export app genesis state: %w", err)
}
cometcfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
return nil
}
func (v *validator) createNodeKey() error {
serverCtx := server.NewDefaultContext()
config := serverCtx.Config
config.SetRoot(v.configDir())
config.Moniker = v.moniker
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
v.nodeKey = *nodeKey
return nil
}
func (v *validator) createConsensusKey() error {
serverCtx := server.NewDefaultContext()
config := serverCtx.Config
config.SetRoot(v.configDir())
config.Moniker = v.moniker
pvKeyFile := config.PrivValidatorKeyFile()
if err := os.MkdirAll(filepath.Dir(pvKeyFile), 0o777); err != nil {
return fmt.Errorf("could not create directory %q: %w", filepath.Dir(pvKeyFile), err)
}
pvStateFile := config.PrivValidatorStateFile()
if err := os.MkdirAll(filepath.Dir(pvStateFile), 0o777); err != nil {
return fmt.Errorf("could not create directory %q: %w", filepath.Dir(pvStateFile), err)
}
filePV := privval.LoadOrGenFilePV(pvKeyFile, pvStateFile)
v.consensusKey = filePV.Key
return nil
}
func (v *validator) createKeyFromMnemonic(name, mnemonic string) error {
kb, err := keyring.New(keyringAppName, keyring.BackendTest, v.configDir(), nil, cdc)
if err != nil {
return err
}
keyringAlgos, _ := kb.SupportedAlgorithms()
algo, err := keyring.NewSigningAlgoFromString(string(hd.Secp256k1Type), keyringAlgos)
if err != nil {
return err
}
info, err := kb.NewAccount(name, mnemonic, "", sdk.FullFundraiserPath, algo)
if err != nil {
return err
}
privKeyArmor, err := kb.ExportPrivKeyArmor(name, keyringPassphrase)
if err != nil {
return err
}
privKey, _, err := sdkcrypto.UnarmorDecryptPrivKey(privKeyArmor, keyringPassphrase)
if err != nil {
return err
}
v.keyInfo = *info
v.mnemonic = mnemonic
v.privateKey = privKey
return nil
}
func (v *validator) createKey(name string) error {
mnemonic, err := createMnemonic()
if err != nil {
return err
}
return v.createKeyFromMnemonic(name, mnemonic)
}
func (v *validator) buildCreateValidatorMsg(amount sdk.Coin) (sdk.Msg, error) {
description := stakingtypes.NewDescription(v.moniker, "", "", "", "")
commissionRates := stakingtypes.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.1"),
MaxRate: sdk.MustNewDecFromStr("0.2"),
MaxChangeRate: sdk.MustNewDecFromStr("0.01"),
}
// get the initial validator min self delegation
minSelfDelegation, _ := sdk.NewIntFromString("1")
valPubKey, err := cryptocodec.FromTmPubKeyInterface(v.consensusKey.PubKey)
if err != nil {
return nil, err
}
valAddr, err := v.keyInfo.GetAddress()
if err != nil {
return nil, err
}
return stakingtypes.NewMsgCreateValidator(
sdk.ValAddress(valAddr),
valPubKey,
amount,
description,
commissionRates,
minSelfDelegation,
)
}
func (v *validator) signMsg(msgs ...sdk.Msg) (*sdktx.Tx, error) {
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
if err := txBuilder.SetMsgs(msgs...); err != nil {
return nil, err
}
txBuilder.SetMemo(fmt.Sprintf("%s@%s:26656", v.nodeKey.ID(), v.instanceName()))
txBuilder.SetFeeAmount(sdk.NewCoins())
txBuilder.SetGasLimit(200_000)
signerData := authsigning.SignerData{
ChainID: v.chain.id,
AccountNumber: 0,
Sequence: 0,
}
// For SIGN_MODE_DIRECT, calling SetSignatures calls setSignerInfos on
// TxBuilder under the hood, and SignerInfos is needed to generate the sign
// bytes. This is the reason for setting SetSignatures here, with a nil
// signature.
//
// Note: This line is not needed for SIGN_MODE_LEGACY_AMINO, but putting it
// also doesn't affect its generated sign bytes, so for code's simplicity
// sake, we put it here.
pubKey, err := v.keyInfo.GetPubKey()
if err != nil {
return nil, err
}
sig := txsigning.SignatureV2{
PubKey: pubKey,
Data: &txsigning.SingleSignatureData{
SignMode: txsigning.SignMode_SIGN_MODE_DIRECT,
Signature: nil,
},
Sequence: 0,
}
if err := txBuilder.SetSignatures(sig); err != nil {
return nil, err
}
bytesToSign, err := encodingConfig.TxConfig.SignModeHandler().GetSignBytes(
txsigning.SignMode_SIGN_MODE_DIRECT,
signerData,
txBuilder.GetTx(),
)
if err != nil {
return nil, err
}
sigBytes, err := v.privateKey.Sign(bytesToSign)
if err != nil {
return nil, err
}
sig = txsigning.SignatureV2{
PubKey: pubKey,
Data: &txsigning.SingleSignatureData{
SignMode: txsigning.SignMode_SIGN_MODE_DIRECT,
Signature: sigBytes,
},
Sequence: 0,
}
if err := txBuilder.SetSignatures(sig); err != nil {
return nil, err
}
signedTx := txBuilder.GetTx()
bz, err := encodingConfig.TxConfig.TxEncoder()(signedTx)
if err != nil {
return nil, err
}
return decodeTx(bz)
}