From 891e0d3483789de1bcd967bb5e948136a5912fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nina=20/=20=E1=83=9C=E1=83=98=E1=83=9C=E1=83=90?= Date: Tue, 23 Jul 2024 14:12:12 +0200 Subject: [PATCH] test: expand apphash test with all state machine msgs (#3606) ## Overview Resolves #3540 & Resolves #3626 Expected AppHash generated from https://github.com/celestiaorg/celestia-app/pull/3665 --- app/test/consistent_apphash_test.go | 433 ++++++++++++++++++++++++---- test/util/common.go | 28 ++ test/util/genesis/modifier.go | 11 + test/util/test_app.go | 98 ++++--- test/util/testfactory/common.go | 17 +- 5 files changed, 475 insertions(+), 112 deletions(-) diff --git a/app/test/consistent_apphash_test.go b/app/test/consistent_apphash_test.go index c3133ba98c..5d1928935c 100644 --- a/app/test/consistent_apphash_test.go +++ b/app/test/consistent_apphash_test.go @@ -3,6 +3,7 @@ package app_test import ( "fmt" "testing" + "time" "github.com/celestiaorg/celestia-app/v2/app" "github.com/celestiaorg/celestia-app/v2/app/encoding" @@ -10,127 +11,292 @@ import ( "github.com/celestiaorg/celestia-app/v2/pkg/user" testutil "github.com/celestiaorg/celestia-app/v2/test/util" "github.com/celestiaorg/celestia-app/v2/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/v2/test/util/testfactory" + blobtypes "github.com/celestiaorg/celestia-app/v2/x/blob/types" "github.com/celestiaorg/go-square/blob" appns "github.com/celestiaorg/go-square/namespace" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + crisisTypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distribution "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proto/tendermint/version" ) -type SdkTx struct { - sdkMsgs []sdk.Msg - txOptions []user.TxOption -} - type BlobTx struct { author string blobs []*blob.Blob txOptions []user.TxOption } -// TestConsistentAppHash executes a set of txs, generates an app hash, +// TestConsistentAppHash executes all state machine messages, generates an app hash, // and compares it against a previously generated hash from the same set of transactions. // App hashes across different commits should be consistent. func TestConsistentAppHash(t *testing.T) { - // Expected app hash produced by v1.x - TODO: link to the test producing the hash - expectedAppHash := []byte{9, 208, 117, 101, 108, 61, 146, 58, 26, 190, 199, 124, 76, 178, 84, 74, 54, 159, 76, 187, 2, 169, 128, 87, 70, 78, 8, 192, 28, 144, 116, 117} + // Expected app hash produced by v1.x - https://github.com/celestiaorg/celestia-app/blob/v1.x/app/consistent_apphash_test.go + expectedAppHash := []byte{84, 216, 210, 48, 113, 204, 234, 21, 150, 236, 97, 87, 242, 184, 45, 248, 116, 127, 49, 88, 134, 197, 202, 125, 44, 210, 67, 144, 107, 51, 145, 65} + expectedDataRoot := []byte{100, 59, 112, 241, 238, 49, 50, 64, 105, 90, 209, 211, 49, 254, 211, 83, 133, 88, 5, 89, 221, 116, 141, 72, 33, 110, 16, 78, 5, 48, 118, 72} // Initialize testApp testApp := testutil.NewTestApp() - enc := encoding.MakeConfig(app.ModuleEncodingRegisters...) + // Create deterministic keys kr, pubKeys := deterministicKeyRing(enc.Codec) - recs, err := kr.List() + // Apply genesis state to the app. + valKeyRing, _, err := testutil.SetupDeterministicGenesisState(testApp, pubKeys, 20_000_000_000, app.DefaultInitialConsensusParams()) require.NoError(t, err) - accountNames := make([]string, 0, len(recs)) - // Get the name of the records - for _, rec := range recs { - accountNames = append(accountNames, rec.Name) - } + // ------------ Genesis User Accounts ------------ - // Apply genesis state to the app. - _, _, err = testutil.SetupDeterministicGenesisState(testApp, pubKeys, 1_000_000_000, app.DefaultInitialConsensusParams()) - require.NoError(t, err) + // Get account names and addresses from the keyring + accountNames := testfactory.GetAccountNames(kr) + accountAddresses := testfactory.GetAddresses(kr) // Query keyring account infos accountInfos := queryAccountInfo(testApp, accountNames, kr) // Create accounts for the signer - accounts := make([]*user.Account, 0, len(accountInfos)) - for i, accountInfo := range accountInfos { - account := user.NewAccount(accountNames[i], accountInfo.AccountNum, accountInfo.Sequence) - accounts = append(accounts, account) - } + accounts := createAccounts(accountInfos, accountNames) - // Create a signer with keyring accounts + // Create a signer with accounts signer, err := user.NewSigner(kr, enc.TxConfig, testutil.ChainID, app.DefaultInitialVersion, accounts...) require.NoError(t, err) - amount := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1000))) + // ------------ Genesis Validator Accounts ------------ - // Create an SDK Tx - sdkTx := SdkTx{ - sdkMsgs: []sdk.Msg{ - banktypes.NewMsgSend(signer.Account(accountNames[0]).Address(), - signer.Account(accountNames[1]).Address(), - amount), - }, - txOptions: blobfactory.DefaultTxOpts(), + // Validators from genesis state + genValidators := testApp.StakingKeeper.GetAllValidators(testApp.NewContext(false, tmproto.Header{})) + + // Get validator account names from the validator keyring + valAccountNames := testfactory.GetAccountNames(valKeyRing) + + // Query validator account infos + valAccountInfos := queryAccountInfo(testApp, valAccountNames, valKeyRing) + + // Create accounts for the validators' signer + valAccounts := createAccounts(valAccountInfos, valAccountNames) + + // Create a signer with validator accounts + valSigner, err := user.NewSigner(valKeyRing, enc.TxConfig, testutil.ChainID, app.DefaultInitialVersion, valAccounts...) + require.NoError(t, err) + + // ----------- Create SDK Messages ------------ + + amount := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1_000))) + // Minimum deposit required for a gov proposal to become active + depositAmount := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(10000000000))) + twoInt := sdk.NewInt(2) + + // ---------------- First Block ------------ + var firstBlockSdkMsgs []sdk.Msg + + // NewMsgSend - sends funds from account-0 to account-1 + sendFundsMsg := banktypes.NewMsgSend(accountAddresses[0], accountAddresses[1], amount) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, sendFundsMsg) + + // MultiSend - creates a multi-send transaction from account-0 to account-1 + multiSendFundsMsg := banktypes.NewMsgMultiSend([]banktypes.Input{ + banktypes.NewInput( + accountAddresses[0], + amount, + ), + }, + []banktypes.Output{ + banktypes.NewOutput( + accountAddresses[1], + amount, + ), + }) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, multiSendFundsMsg) + + // NewMsgGrant - grants authorization to account-1 + grantExpiration := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC) + authorization := authz.NewGenericAuthorization(blobtypes.URLMsgPayForBlobs) + msgGrant, err := authz.NewMsgGrant( + accountAddresses[0], + accountAddresses[1], + authorization, + &grantExpiration, + ) + require.NoError(t, err) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, msgGrant) + + // MsgVerifyInvariant - verifies the nonnegative-outstanding invariant within the bank module for the account-0 + msgVerifyInvariant := crisisTypes.NewMsgVerifyInvariant(accountAddresses[0], banktypes.ModuleName, "nonnegative-outstanding") + firstBlockSdkMsgs = append(firstBlockSdkMsgs, msgVerifyInvariant) + + // MsgGrantAllowance - creates a grant allowance for account-1 + basicAllowance := feegrant.BasicAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1000))), } + feegrantMsg, err := feegrant.NewMsgGrantAllowance(&basicAllowance, accountAddresses[0], accountAddresses[1]) + require.NoError(t, err) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, feegrantMsg) - // Create a Blob Tx - blobTx := BlobTx{ - author: accountNames[2], - blobs: []*blob.Blob{blob.New(fixedNamespace(), []byte{1}, appconsts.DefaultShareVersion)}, - txOptions: blobfactory.DefaultTxOpts(), + // NewMsgSubmitProposal - submits a proposal to send funds from the governance account to account-1 + govAccount := testApp.GovKeeper.GetGovernanceAccount(testApp.NewContext(false, tmproto.Header{})).GetAddress() + msgSend := banktypes.MsgSend{ + FromAddress: govAccount.String(), + ToAddress: accountAddresses[1].String(), + Amount: amount, } + proposal, err := govtypes.NewMsgSubmitProposal([]sdk.Msg{&msgSend}, amount, accountAddresses[0].String(), "") + require.NoError(t, err) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, proposal) - // Create SDK Tx - rawSdkTx, err := signer.CreateTx(sdkTx.sdkMsgs, sdkTx.txOptions...) + // NewMsgDeposit - deposits funds to a governance proposal + msgDeposit := govtypes.NewMsgDeposit(accountAddresses[0], 1, depositAmount) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, msgDeposit) + + // NewMsgCreateValidator - creates a new validator + msgCreateValidator, err := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(accountAddresses[6]), + ed25519.GenPrivKeyFromSecret([]byte("validator")).PubKey(), + amount[0], + 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.OneInt()) require.NoError(t, err) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, msgCreateValidator) + + // NewMsgDelegate - delegates funds to validator-0 + msgDelegate := stakingtypes.NewMsgDelegate(accountAddresses[0], genValidators[0].GetOperator(), amount[0]) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, msgDelegate) + + // NewMsgBeginRedelegate - re-delegates funds from validator-0 to validator-1 + msgBeginRedelegate := stakingtypes.NewMsgBeginRedelegate(accountAddresses[0], genValidators[0].GetOperator(), genValidators[1].GetOperator(), amount[0]) + firstBlockSdkMsgs = append(firstBlockSdkMsgs, msgBeginRedelegate) + + // ------------ Second Block ------------ + + var secondBlockSdkMsgs []sdk.Msg + + // NewMsgVote - votes yes on a governance proposal + msgVote := govtypes.NewMsgVote(accountAddresses[0], 1, govtypes.VoteOption_VOTE_OPTION_YES, "") + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgVote) + + // NewMsgRevoke - revokes authorization from account-1 + msgRevoke := authz.NewMsgRevoke( + accountAddresses[0], + accountAddresses[1], + blobtypes.URLMsgPayForBlobs, + ) + + // NewMsgExec - executes the revoke authorization message + msgExec := authz.NewMsgExec(accountAddresses[0], []sdk.Msg{&msgRevoke}) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, &msgExec) + + // NewMsgVoteWeighted - votes with a weighted vote + msgVoteWeighted := govtypes.NewMsgVoteWeighted( + accountAddresses[0], + 1, + govtypes.WeightedVoteOptions([]*govtypes.WeightedVoteOption{{Option: govtypes.OptionYes, Weight: "1.0"}}), // Cast the slice to the expected type + "", + ) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgVoteWeighted) + + // NewMsgEditValidator - edits the newly created validator's description + msgEditValidator := stakingtypes.NewMsgEditValidator(sdk.ValAddress(accountAddresses[6]), stakingtypes.NewDescription("add", "new", "val", "desc", "."), nil, &twoInt) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgEditValidator) + + // NewMsgUndelegate - undelegates funds from validator-1 + msgUndelegate := stakingtypes.NewMsgUndelegate(accountAddresses[0], genValidators[1].GetOperator(), amount[0]) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgUndelegate) + + // NewMsgDelegate - delegates funds to validator-0 + msgDelegate = stakingtypes.NewMsgDelegate(accountAddresses[0], genValidators[0].GetOperator(), amount[0]) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgDelegate) + + // Block 2 height + blockHeight := testApp.LastBlockHeight() + 2 + // NewMsgCancelUnbondingDelegation - cancels unbonding delegation from validator-1 + msgCancelUnbondingDelegation := stakingtypes.NewMsgCancelUnbondingDelegation(accountAddresses[0], genValidators[1].GetOperator(), blockHeight, amount[0]) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgCancelUnbondingDelegation) + + // NewMsgSetWithdrawAddress - sets the withdraw address for account-0 + msgSetWithdrawAddress := distribution.NewMsgSetWithdrawAddress(accountAddresses[0], accountAddresses[1]) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgSetWithdrawAddress) - // Create Blob Tx - rawBlobTx, _, err := signer.CreatePayForBlobs(blobTx.author, blobTx.blobs, blobTx.txOptions...) + // NewMsgRevokeAllowance - revokes the allowance granted to account-1 + msgRevokeAllowance := feegrant.NewMsgRevokeAllowance(accountAddresses[0], accountAddresses[1]) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, &msgRevokeAllowance) + + // NewMsgFundCommunityPool - funds the community pool + msgFundCommunityPool := distribution.NewMsgFundCommunityPool(amount, accountAddresses[0]) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgFundCommunityPool) + + // NewMsgWithdrawDelegatorReward - withdraws delegator rewards + msgWithdrawDelegatorReward := distribution.NewMsgWithdrawDelegatorReward(accountAddresses[0], genValidators[0].GetOperator()) + secondBlockSdkMsgs = append(secondBlockSdkMsgs, msgWithdrawDelegatorReward) + + // ------------ Third Block ------------ + + // Txs within the third block are signed by the validator's signer + var thirdBlockSdkMsgs []sdk.Msg + + // NewMsgWithdrawValidatorCommission - withdraws validator-0's commission + msgWithdrawValidatorCommission := distribution.NewMsgWithdrawValidatorCommission(genValidators[0].GetOperator()) + thirdBlockSdkMsgs = append(thirdBlockSdkMsgs, msgWithdrawValidatorCommission) + + // NewMsgUnjail - unjails validator-3 + msgUnjail := slashingtypes.NewMsgUnjail(genValidators[3].GetOperator()) + thirdBlockSdkMsgs = append(thirdBlockSdkMsgs, msgUnjail) + + // ------------ Construct Txs ------------ + + // Create SDK transactions from the list of messages + // and separate them into 3 different blocks + firstBlockEncodedTxs, err := processSdkMessages(signer, firstBlockSdkMsgs) require.NoError(t, err) - // BeginBlock - header := tmproto.Header{ - Version: version.Consensus{App: 1}, - Height: testApp.LastBlockHeight() + 1, - } - testApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + secondBlockEncodedTxs, err := processSdkMessages(signer, secondBlockSdkMsgs) + require.NoError(t, err) - // Deliver SDK Tx - resp := testApp.DeliverTx(abci.RequestDeliverTx{Tx: rawSdkTx}) - require.EqualValues(t, 0, resp.Code, resp.Log) + thirdBlockEncodedTxs, err := processSdkMessages(valSigner, thirdBlockSdkMsgs) + require.NoError(t, err) - // Deliver Blob Tx - blob, isBlobTx := blob.UnmarshalBlobTx(rawBlobTx) - require.True(t, isBlobTx) - resp = testApp.DeliverTx(abci.RequestDeliverTx{Tx: blob.Tx}) - require.EqualValues(t, 0, resp.Code, resp.Log) + // Create a Blob Tx + blobTx := BlobTx{ + author: accountNames[1], + blobs: []*blob.Blob{blob.New(fixedNamespace(), []byte{1}, appconsts.DefaultShareVersion)}, + txOptions: blobfactory.DefaultTxOpts(), + } + encodedBlobTx, _, err := signer.CreatePayForBlobs(blobTx.author, blobTx.blobs, blobTx.txOptions...) + require.NoError(t, err) - // EndBlock - testApp.EndBlock(abci.RequestEndBlock{Height: header.Height}) + // Convert validators to ABCI validators + abciValidators, err := convertToABCIValidators(genValidators) + require.NoError(t, err) - // Commit the state - testApp.Commit() + // Execute the first block + _, firstBlockAppHash, err := executeTxs(testApp, []byte{}, firstBlockEncodedTxs, abciValidators, testApp.LastCommitID().Hash) + require.NoError(t, err) - // Get the app hash - appHash := testApp.LastCommitID().Hash + // Execute the second block + _, secondBlockAppHash, err := executeTxs(testApp, encodedBlobTx, secondBlockEncodedTxs, abciValidators, firstBlockAppHash) + require.NoError(t, err) + + // Execute the final block and get the data root alongside the final app hash + finalDataRoot, finalAppHash, err := executeTxs(testApp, []byte{}, thirdBlockEncodedTxs, abciValidators, secondBlockAppHash) + require.NoError(t, err) // Require that the app hash is equal to the app hash produced on a different commit - require.Equal(t, expectedAppHash, appHash) + require.Equal(t, expectedAppHash, finalAppHash) + // Require that the data root is equal to the data root produced on a different commit + require.Equal(t, expectedDataRoot, finalDataRoot) } // fixedNamespace returns a hardcoded namespace @@ -171,3 +337,144 @@ func deterministicKeyRing(cdc codec.Codec) (keyring.Keyring, []types.PubKey) { } return kb, pubKeys } + +// processSdkMessages takes a list of sdk messages, forms transactions, signs them +// and returns a list of encoded transactions +func processSdkMessages(signer *user.Signer, sdkMessages []sdk.Msg) ([][]byte, error) { + encodedTxs := make([][]byte, 0, len(sdkMessages)) + for _, msg := range sdkMessages { + encodedTx, err := signer.CreateTx([]sdk.Msg{msg}, blobfactory.DefaultTxOpts()...) + if err != nil { + return nil, err + } + + signerAddress := msg.GetSigners()[0] + signerAccount := signer.AccountByAddress(signerAddress) + err = signer.SetSequence(signerAccount.Name(), signerAccount.Sequence()+1) + if err != nil { + return nil, err + } + + encodedTxs = append(encodedTxs, encodedTx) + } + return encodedTxs, nil +} + +// executeTxs executes a set of transactions and returns the data hash and app hash +func executeTxs(testApp *app.App, encodedBlobTx []byte, encodedSdkTxs [][]byte, validators []abci.Validator, lastCommitHash []byte) ([]byte, []byte, error) { + height := testApp.LastBlockHeight() + 1 + chainID := testApp.GetChainID() + + genesisTime := testutil.GenesisTime + + // Prepare Proposal + resPrepareProposal := testApp.PrepareProposal(abci.RequestPrepareProposal{ + BlockData: &tmproto.Data{ + Txs: encodedSdkTxs, + }, + ChainId: chainID, + Height: height, + // Dynamically increase time so the validator can be unjailed (1m duration) + Time: genesisTime.Add(time.Duration(height) * time.Minute), + }) + + dataHash := resPrepareProposal.BlockData.Hash + + header := tmproto.Header{ + Version: version.Consensus{App: 1}, + DataHash: resPrepareProposal.BlockData.Hash, + ChainID: chainID, + Time: genesisTime.Add(time.Duration(height) * time.Minute), + Height: height, + LastCommitHash: lastCommitHash, + } + + // Process Proposal + resProcessProposal := testApp.ProcessProposal(abci.RequestProcessProposal{ + BlockData: resPrepareProposal.BlockData, + Header: header, + }, + ) + if abci.ResponseProcessProposal_ACCEPT != resProcessProposal.Result { + return nil, nil, fmt.Errorf("ProcessProposal failed: %v", resProcessProposal.Result) + } + + // Begin block + validator3Signed := height == 2 // Validator 3 signs only the first block + testApp.BeginBlock(abci.RequestBeginBlock{ + Header: header, + LastCommitInfo: abci.LastCommitInfo{ + Votes: []abci.VoteInfo{ + // In order to withdraw commission for this validator + { + Validator: validators[0], + SignedLastBlock: true, + }, + // In order to jail this validator + { + Validator: validators[3], + SignedLastBlock: validator3Signed, + }, + }, + }, + }) + + // Deliver SDK Txs + for i, tx := range encodedSdkTxs { + resp := testApp.DeliverTx(abci.RequestDeliverTx{Tx: tx}) + if resp.Code != abci.CodeTypeOK { + return nil, nil, fmt.Errorf("DeliverTx failed for the message at index %d: %s", i, resp.Log) + } + } + + // Deliver Blob Txs + if len(encodedBlobTx) != 0 { + // Deliver Blob Tx + blob, isBlobTx := blob.UnmarshalBlobTx(encodedBlobTx) + if !isBlobTx { + return nil, nil, fmt.Errorf("Not a valid BlobTx") + } + + respDeliverTx := testApp.DeliverTx(abci.RequestDeliverTx{Tx: blob.Tx}) + if respDeliverTx.Code != uint32(0) { + return nil, nil, fmt.Errorf("DeliverTx failed for the BlobTx: %s", respDeliverTx.Log) + } + } + + // EndBlock + testApp.EndBlock(abci.RequestEndBlock{Height: header.Height}) + + // Commit the state + testApp.Commit() + + // Get the app hash + appHash := testApp.LastCommitID().Hash + + return dataHash, appHash, nil +} + +// createAccounts creates a list of user.Accounts from a list of accountInfos +func createAccounts(accountInfos []blobfactory.AccountInfo, accountNames []string) []*user.Account { + accounts := make([]*user.Account, 0, len(accountInfos)) + for i, accountInfo := range accountInfos { + account := user.NewAccount(accountNames[i], accountInfo.AccountNum, accountInfo.Sequence) + accounts = append(accounts, account) + } + return accounts +} + +// convertToABCIValidators converts a list of staking.Validator to a list of abci.Validator +func convertToABCIValidators(genValidators []stakingtypes.Validator) ([]abci.Validator, error) { + abciValidators := make([]abci.Validator, 0, len(genValidators)) + for _, val := range genValidators { + consAddr, err := val.GetConsAddr() + if err != nil { + return nil, err + } + abciValidators = append(abciValidators, abci.Validator{ + Address: consAddr, + Power: 100, + }) + } + return abciValidators, nil +} diff --git a/test/util/common.go b/test/util/common.go index b969400c2c..e4fa050661 100644 --- a/test/util/common.go +++ b/test/util/common.go @@ -40,6 +40,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + tmed "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" @@ -59,6 +60,33 @@ var ( MinCommissionRate: sdk.NewDecWithPrec(0, 0), } + // HardcodedConsensusPrivKeys + FixedConsensusPrivKeys = []tmed.PrivKey{ + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456389012")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456389013")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456389014")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456389015")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456389016")), + } + + FixedNetworkPrivKeys = []tmed.PrivKey{ + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456786012")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456786013")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456786014")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456786015")), + tmed.GenPrivKeyFromSecret([]byte("12345678901234567890123456786016")), + } + + // FixedMnemonics is a set of fixed mnemonics for testing. + // Account names are: validator1, validator2, validator3, validator4, validator5 + FixedMnemonics = []string{ + "body world north giggle crop reduce height copper damp next verify orphan lens loan adjust inform utility theory now ranch motion opinion crowd fun", + "body champion street fat bone above office guess waste vivid gift around approve elevator depth fiber alarm usual skirt like organ space antique silk", + "cheap alpha render punch clap prize duty drive steel situate person radar smooth elegant over chronic wait danger thumb soft letter spatial acquire rough", + "outdoor ramp suspect office disagree world attend vanish small wish capable fall wall soon damp session emotion chest toss viable meat host clerk truth", + "ability evidence casino cram weasel chest brush bridge sister blur onion found glad own mansion amateur expect force fun dragon famous alien appear open", + } + // ConsPrivKeys generate ed25519 ConsPrivKeys to be used for validator operator keys ConsPrivKeys = []ccrypto.PrivKey{ ed25519.GenPrivKey(), diff --git a/test/util/genesis/modifier.go b/test/util/genesis/modifier.go index 20c62fb768..b7a419b848 100644 --- a/test/util/genesis/modifier.go +++ b/test/util/genesis/modifier.go @@ -13,6 +13,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // Modifier allows for arbitrary changes to be made on the genesis state @@ -30,6 +31,16 @@ func SetBlobParams(codec codec.Codec, params blobtypes.Params) Modifier { } } +// SetSlashingParams will set the provided slashing params as genesis state. +func SetSlashingParams(codec codec.Codec, parans slashingtypes.Params) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + slashingGenState := slashingtypes.DefaultGenesisState() + slashingGenState.Params = parans + state[slashingtypes.ModuleName] = codec.MustMarshalJSON(slashingGenState) + return state + } +} + // ImmediateProposals sets the thresholds for getting a gov proposal to very low // levels. func ImmediateProposals(codec codec.Codec) Modifier { diff --git a/test/util/test_app.go b/test/util/test_app.go index e1d50722e2..a4ca8e6208 100644 --- a/test/util/test_app.go +++ b/test/util/test_app.go @@ -36,11 +36,14 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) const ChainID = testfactory.ChainID +var GenesisTime = time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC() + // Get flags every time the simulator is run func init() { simapp.GetSimulatorFlags() @@ -96,13 +99,16 @@ func NewTestApp() *app.App { // SetupDeterministicGenesisState sets genesis on initialized testApp with the provided arguments. func SetupDeterministicGenesisState(testApp *app.App, pubKeys []cryptotypes.PubKey, balance int64, cparams *tmproto.ConsensusParams) (keyring.Keyring, []genesis.Account, error) { - // create genesis + slashingParams := slashingtypes.NewParams(2, sdk.OneDec(), time.Minute, sdk.OneDec(), sdk.OneDec()) + + // Create genesis gen := genesis.NewDefaultGenesis(). WithChainID(ChainID). WithConsensusParams(cparams). - WithGenesisTime(time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC()) + WithModifiers(genesis.SetSlashingParams(testApp.AppCodec(), slashingParams)). + WithGenesisTime(GenesisTime) - // add accounts to genesis + // Add accounts to genesis for i, pk := range pubKeys { err := gen.AddAccount(genesis.Account{ PubKey: pk, @@ -114,8 +120,8 @@ func SetupDeterministicGenesisState(testApp *app.App, pubKeys []cryptotypes.PubK } } - // add validator to genesis - err := AddDeterministicValidatorToGenesis(gen) + // Add validators to genesis + err := AddDeterministicValidatorsToGenesis(gen) if err != nil { return nil, nil, fmt.Errorf("failed to add validator: %w", err) } @@ -125,12 +131,12 @@ func SetupDeterministicGenesisState(testApp *app.App, pubKeys []cryptotypes.PubK return nil, nil, fmt.Errorf("failed to export genesis doc: %w", err) } - // initialise test app against genesis + // Initialise test app against genesis testApp.Info(abci.RequestInfo{}) abciParams := &abci.ConsensusParams{ Block: &abci.BlockParams{ - // choose some value large enough to not bottleneck the max square + // Choose some value large enough to not bottleneck the max square // size MaxBytes: int64(appconsts.DefaultSquareSizeUpperBound*appconsts.DefaultSquareSizeUpperBound) * appconsts.ContinuationSparseShareContentSize, MaxGas: cparams.Block.MaxGas, @@ -140,7 +146,7 @@ func SetupDeterministicGenesisState(testApp *app.App, pubKeys []cryptotypes.PubK Version: &cparams.Version, } - // init chain will set the validator set and initialize the genesis accounts + // Init chain will set the validator set and initialize the genesis accounts testApp.InitChain( abci.RequestInitChain{ Time: gen.GenesisTime, @@ -151,7 +157,7 @@ func SetupDeterministicGenesisState(testApp *app.App, pubKeys []cryptotypes.PubK }, ) - // commit genesis changes + // Commit genesis changes testApp.Commit() testApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ ChainID: ChainID, @@ -189,14 +195,12 @@ func NewTestAppWithGenesisSet(cparams *tmproto.ConsensusParams, genAccounts ...s Version: &cparams.Version, } - genesisTime := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC() - testApp.Info(abci.RequestInfo{}) // init chain will set the validator set and initialize the genesis accounts testApp.InitChain( abci.RequestInitChain{ - Time: genesisTime, + Time: GenesisTime, Validators: []abci.ValidatorUpdate{}, ConsensusParams: abciParams, AppStateBytes: stateBytes, @@ -206,47 +210,47 @@ func NewTestAppWithGenesisSet(cparams *tmproto.ConsensusParams, genAccounts ...s return testApp, valSet, kr } -// AddDeterministicValidatorToGenesis adds a single deterministic validator to the genesis. -func AddDeterministicValidatorToGenesis(g *genesis.Genesis) error { - // hardcoded keys for deterministic account creation - mnemo := "body world north giggle crop reduce height copper damp next verify orphan lens loan adjust inform utility theory now ranch motion opinion crowd fun" - consensusKey := ed25519.GenPrivKeyFromSecret([]byte("12345678901234567890123456389012")) - networkKey := ed25519.GenPrivKeyFromSecret([]byte("12345678901234567890123456786012")) - - val := genesis.Validator{ - KeyringAccount: genesis.KeyringAccount{ - Name: "validator1", - InitialTokens: 1_000_000_000, - }, - Stake: 1_000_000, - ConsensusKey: consensusKey, - NetworkKey: networkKey, - } +// AddDeterministicValidatorToGenesis adds a set of five validators to the genesis. +func AddDeterministicValidatorsToGenesis(g *genesis.Genesis) error { + for i := range FixedMnemonics { + val := genesis.Validator{ + KeyringAccount: genesis.KeyringAccount{ + Name: "validator" + fmt.Sprint(i), + InitialTokens: 5_000_000_000, + }, + Stake: 1_000_000_000, + ConsensusKey: FixedConsensusPrivKeys[i], + NetworkKey: FixedNetworkPrivKeys[i], + } - // initialize the validator's genesis account in the keyring - rec, err := g.Keyring().NewAccount(val.Name, mnemo, "", "", hd.Secp256k1) - if err != nil { - return fmt.Errorf("failed to create account: %w", err) - } + // Initialize the validator's genesis account in the keyring + rec, err := g.Keyring().NewAccount(val.Name, FixedMnemonics[i], "", "", hd.Secp256k1) + if err != nil { + return fmt.Errorf("failed to create account: %w", err) + } - validatorPubKey, err := rec.GetPubKey() - if err != nil { - return fmt.Errorf("failed to get pubkey: %w", err) - } + validatorPubKey, err := rec.GetPubKey() + if err != nil { + return fmt.Errorf("failed to get pubkey: %w", err) + } - // make account from keyring account - account := genesis.Account{ - PubKey: validatorPubKey, - Balance: val.KeyringAccount.InitialTokens, - Name: val.Name, - } + // Construct account from keyring account + account := genesis.Account{ + PubKey: validatorPubKey, + Balance: val.KeyringAccount.InitialTokens, + Name: val.Name, + } - // add the validator's account to the genesis - if err := g.AddAccount(account); err != nil { - return fmt.Errorf("failed to add account: %w", err) + // Add the validator's account to the genesis + if err := g.AddAccount(account); err != nil { + return fmt.Errorf("failed to add account: %w", err) + } + if err := g.AddValidator(val); err != nil { + return fmt.Errorf("failed to add validator: %w", err) + } } - return g.AddValidator(val) + return nil } // AddAccount mimics the cli addAccount command, providing an diff --git a/test/util/testfactory/common.go b/test/util/testfactory/common.go index 8edb003aa8..a25a9002b8 100644 --- a/test/util/testfactory/common.go +++ b/test/util/testfactory/common.go @@ -71,15 +71,28 @@ func GetAddresses(keys keyring.Keyring) []sdk.AccAddress { panic(err) } addresses := make([]sdk.AccAddress, 0, len(recs)) - for idx, rec := range recs { - addresses[idx], err = rec.GetAddress() + for _, rec := range recs { + address, err := rec.GetAddress() if err != nil { panic(err) } + addresses = append(addresses, address) } return addresses } +func GetAccountNames(keys keyring.Keyring) []string { + recs, err := keys.List() + if err != nil { + panic(err) + } + names := make([]string, 0, len(recs)) + for _, rec := range recs { + names = append(names, rec.Name) + } + return names +} + func GetAddress(keys keyring.Keyring, account string) sdk.AccAddress { rec, err := keys.Key(account) if err != nil {