Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: consistent appHash between commits #3513

Merged
merged 20 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions app/test/consistent_apphash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package app_test

import (
"fmt"
"testing"

"github.com/celestiaorg/celestia-app/v2/app"
"github.com/celestiaorg/celestia-app/v2/app/encoding"
"github.com/celestiaorg/celestia-app/v2/pkg/appconsts"
"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/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/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/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,
// 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}

// Initialize testApp
testApp := testutil.NewTestApp()

enc := encoding.MakeConfig(app.ModuleEncodingRegisters...)
// Create deterministic keys
kr, pubKeys := deterministicKeyRing(enc.Codec)

recs, err := kr.List()
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)
}

// Apply genesis state to the app.
_, _, err = testutil.SetupDeterministicGenesisState(testApp, pubKeys, 1_000_000_000, app.DefaultInitialConsensusParams())
require.NoError(t, err)

// 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)
}

// Create a signer with keyring 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)))

// 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(),
}

// Create a Blob Tx
blobTx := BlobTx{
author: accountNames[2],
blobs: []*blob.Blob{blob.New(fixedNamespace(), []byte{1}, appconsts.DefaultShareVersion)},
txOptions: blobfactory.DefaultTxOpts(),
}

// Create SDK Tx
rawSdkTx, err := signer.CreateTx(sdkTx.sdkMsgs, sdkTx.txOptions...)
require.NoError(t, err)

// Create Blob Tx
rawBlobTx, _, err := signer.CreatePayForBlobs(blobTx.author, blobTx.blobs, blobTx.txOptions...)
require.NoError(t, err)

// BeginBlock
header := tmproto.Header{
Version: version.Consensus{App: 1},
Height: testApp.LastBlockHeight() + 1,
}
testApp.BeginBlock(abci.RequestBeginBlock{Header: header})

// Deliver SDK Tx
resp := testApp.DeliverTx(abci.RequestDeliverTx{Tx: rawSdkTx})
require.EqualValues(t, 0, resp.Code, resp.Log)

// 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)

// EndBlock
testApp.EndBlock(abci.RequestEndBlock{Height: header.Height})

// Commit the state
testApp.Commit()

// Get the app hash
appHash := testApp.LastCommitID().Hash

// Require that the app hash is equal to the app hash produced on a different commit
require.Equal(t, expectedAppHash, appHash)
}

// fixedNamespace returns a hardcoded namespace
func fixedNamespace() appns.Namespace {
return appns.Namespace{
Version: 0,
ID: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 67, 154, 200, 228, 130, 74, 147, 162, 11},
}
}

// deterministicKeyRing returns a deterministic keyring and a list of deterministic public keys
func deterministicKeyRing(cdc codec.Codec) (keyring.Keyring, []types.PubKey) {
mnemonics := []string{
"great myself congress genuine scale muscle view uncover pipe miracle sausage broccoli lonely swap table foam brand turtle comic gorilla firm mad grunt hazard",
"cheap job month trigger flush cactus chest juice dolphin people limit crunch curious secret object beach shield snake hunt group sketch cousin puppy fox",
"oil suffer bamboo one better attack exist dolphin relief enforce cat asset raccoon lava regret found love certain plunge grocery accuse goat together kiss",
"giraffe busy subject doll jump drama sea daring again club spend toe mind organ real liar permit refuse change opinion donkey job cricket speed",
"fee vapor thing fish fan memory negative raven cram win quantum ozone job mirror shoot sting quiz black apart funny sort cancel friend curtain",
"skin beef review pilot tooth act any alarm there only kick uniform ticket material cereal radar ethics list unlock method coral smooth street frequent",
"ecology scout core guard load oil school effort near alcohol fancy save cereal owner enforce impact sand husband trophy solve amount fish festival sell",
"used describe angle twin amateur pyramid bitter pool fluid wing erode rival wife federal curious drink battle put elbow mandate another token reveal tone",
"reason fork target chimney lift typical fine divorce mixture web robot kiwi traffic stove miss crane welcome camp bless fuel october riot pluck ordinary",
"undo logic mobile modify master force donor rose crumble forget plate job canal waste turn damp sure point deposit hazard quantum car annual churn",
"charge subway treat loop donate place loan want grief leg message siren joy road exclude match empty enforce vote meadow enlist vintage wool involve",
}
kb := keyring.NewInMemory(cdc)
pubKeys := make([]types.PubKey, len(mnemonics))
for idx, mnemonic := range mnemonics {
rec, err := kb.NewAccount(fmt.Sprintf("account-%d", idx), mnemonic, "", "", hd.Secp256k1)
if err != nil {
panic(err)
}
pubKey, err := rec.GetPubKey()
if err != nil {
panic(err)
}
pubKeys[idx] = pubKey
}
return kb, pubKeys
}
Comment on lines +144 to +173
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace panic with proper error handling in deterministicKeyRing.

Using panic for error handling in test setups can lead to abrupt test terminations that are hard to debug. Consider returning an error from the function and using require.NoError in the test to handle these errors gracefully.

-			panic(err)
+			return nil, err
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// deterministicKeyRing returns a deterministic keyring and a list of deterministic public keys
func deterministicKeyRing(cdc codec.Codec) (keyring.Keyring, []types.PubKey) {
mnemonics := []string{
"great myself congress genuine scale muscle view uncover pipe miracle sausage broccoli lonely swap table foam brand turtle comic gorilla firm mad grunt hazard",
"cheap job month trigger flush cactus chest juice dolphin people limit crunch curious secret object beach shield snake hunt group sketch cousin puppy fox",
"oil suffer bamboo one better attack exist dolphin relief enforce cat asset raccoon lava regret found love certain plunge grocery accuse goat together kiss",
"giraffe busy subject doll jump drama sea daring again club spend toe mind organ real liar permit refuse change opinion donkey job cricket speed",
"fee vapor thing fish fan memory negative raven cram win quantum ozone job mirror shoot sting quiz black apart funny sort cancel friend curtain",
"skin beef review pilot tooth act any alarm there only kick uniform ticket material cereal radar ethics list unlock method coral smooth street frequent",
"ecology scout core guard load oil school effort near alcohol fancy save cereal owner enforce impact sand husband trophy solve amount fish festival sell",
"used describe angle twin amateur pyramid bitter pool fluid wing erode rival wife federal curious drink battle put elbow mandate another token reveal tone",
"reason fork target chimney lift typical fine divorce mixture web robot kiwi traffic stove miss crane welcome camp bless fuel october riot pluck ordinary",
"undo logic mobile modify master force donor rose crumble forget plate job canal waste turn damp sure point deposit hazard quantum car annual churn",
"charge subway treat loop donate place loan want grief leg message siren joy road exclude match empty enforce vote meadow enlist vintage wool involve",
}
kb := keyring.NewInMemory(cdc)
pubKeys := make([]types.PubKey, len(mnemonics))
for idx, mnemonic := range mnemonics {
rec, err := kb.NewAccount(fmt.Sprintf("account-%d", idx), mnemonic, "", "", hd.Secp256k1)
if err != nil {
panic(err)
}
pubKey, err := rec.GetPubKey()
if err != nil {
panic(err)
}
pubKeys[idx] = pubKey
}
return kb, pubKeys
}
// deterministicKeyRing returns a deterministic keyring and a list of deterministic public keys
func deterministicKeyRing(cdc codec.Codec) (keyring.Keyring, []types.PubKey, error) {
mnemonics := []string{
"great myself congress genuine scale muscle view uncover pipe miracle sausage broccoli lonely swap table foam brand turtle comic gorilla firm mad grunt hazard",
"cheap job month trigger flush cactus chest juice dolphin people limit crunch curious secret object beach shield snake hunt group sketch cousin puppy fox",
"oil suffer bamboo one better attack exist dolphin relief enforce cat asset raccoon lava regret found love certain plunge grocery accuse goat together kiss",
"giraffe busy subject doll jump drama sea daring again club spend toe mind organ real liar permit refuse change opinion donkey job cricket speed",
"fee vapor thing fish fan memory negative raven cram win quantum ozone job mirror shoot sting quiz black apart funny sort cancel friend curtain",
"skin beef review pilot tooth act any alarm there only kick uniform ticket material cereal radar ethics list unlock method coral smooth street frequent",
"ecology scout core guard load oil school effort near alcohol fancy save cereal owner enforce impact sand husband trophy solve amount fish festival sell",
"used describe angle twin amateur pyramid bitter pool fluid wing erode rival wife federal curious drink battle put elbow mandate another token reveal tone",
"reason fork target chimney lift typical fine divorce mixture web robot kiwi traffic stove miss crane welcome camp bless fuel october riot pluck ordinary",
"undo logic mobile modify master force donor rose crumble forget plate job canal waste turn damp sure point deposit hazard quantum car annual churn",
"charge subway treat loop donate place loan want grief leg message siren joy road exclude match empty enforce vote meadow enlist vintage wool involve",
}
kb := keyring.NewInMemory(cdc)
pubKeys := make([]types.PubKey, len(mnemonics))
for idx, mnemonic := range mnemonics {
rec, err := kb.NewAccount(fmt.Sprintf("account-%d", idx), mnemonic, "", "", hd.Secp256k1)
if err != nil {
return nil, nil, err
}
pubKey, err := rec.GetPubKey()
if err != nil {
return nil, nil, err
}
pubKeys[idx] = pubKey
}
return kb, pubKeys, nil
}

2 changes: 1 addition & 1 deletion test/e2e/testnet/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (t *Testnet) CreateGenesisNode(version string, selfDelegation, upgradeHeigh
if err != nil {
return err
}
if err := t.genesis.AddValidator(node.GenesisValidator()); err != nil {
if err := t.genesis.NewValidator(node.GenesisValidator()); err != nil {
return err
}
t.nodes = append(t.nodes, node)
Expand Down
49 changes: 33 additions & 16 deletions test/util/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ type Genesis struct {
genOps []Modifier
}

// Accounts getter
func (g *Genesis) Accounts() []Account {
return g.accounts
}

// Keyring getter
func (g *Genesis) Keyring() keyring.Keyring {
return g.kr
}

// Validators getter
func (g *Genesis) Validators() []Validator {
return g.validators
}

// NewDefaultGenesis creates a new default genesis with no accounts or validators.
func NewDefaultGenesis() *Genesis {
ecfg := encoding.MakeConfig(app.ModuleBasics)
Expand All @@ -58,29 +73,34 @@ func NewDefaultGenesis() *Genesis {
return g
}

// WithModifier adds a genesis modifier to the genesis.
func (g *Genesis) WithModifiers(ops ...Modifier) *Genesis {
g.genOps = append(g.genOps, ops...)
return g
}

// WithConsensusParams sets the consensus parameters of the genesis.
func (g *Genesis) WithConsensusParams(params *tmproto.ConsensusParams) *Genesis {
g.ConsensusParams = params
return g
}

// WithChainID sets the chain ID of the genesis.
func (g *Genesis) WithChainID(chainID string) *Genesis {
g.ChainID = chainID
return g
}

// WithGenesisTime sets the genesis time of the genesis.
func (g *Genesis) WithGenesisTime(genesisTime time.Time) *Genesis {
g.GenesisTime = genesisTime
return g
}

// WithAccounts adds the given validators to the genesis.
func (g *Genesis) WithValidators(vals ...Validator) *Genesis {
for _, val := range vals {
err := g.AddValidator(val)
err := g.NewValidator(val)
if err != nil {
panic(err)
}
Expand All @@ -100,6 +120,7 @@ func (g *Genesis) WithKeyringAccounts(accs ...KeyringAccount) *Genesis {
return g
}

// AddAccount adds an existing account to the genesis.
func (g *Genesis) AddAccount(account Account) error {
for _, acc := range g.accounts {
if bytes.Equal(acc.PubKey.Bytes(), account.PubKey.Bytes()) {
Expand All @@ -110,6 +131,7 @@ func (g *Genesis) AddAccount(account Account) error {
return nil
}

// NewAccount creates a new account and adds it to the genesis.
func (g *Genesis) NewAccount(acc KeyringAccount) error {
if err := acc.ValidateBasic(); err != nil {
return err
Expand Down Expand Up @@ -139,16 +161,12 @@ func (g *Genesis) NewAccount(acc KeyringAccount) error {
return nil
}

// AddValidator verifies and adds a given validator to the genesis.
func (g *Genesis) AddValidator(val Validator) error {
if err := val.ValidateBasic(); err != nil {
return err
}

// Add the validator's genesis account
if err := g.NewAccount(val.KeyringAccount); err != nil {
return err
}

// Add the validator's genesis transaction
gentx, err := val.GenTx(g.ecfg, g.kr, g.ChainID)
if err != nil {
Expand All @@ -161,10 +179,17 @@ func (g *Genesis) AddValidator(val Validator) error {
return nil
}

func (g *Genesis) Accounts() []Account {
return g.accounts
// Creates a new validator account and adds it to the genesis.
func (g *Genesis) NewValidator(val Validator) error {
// Add the validator's genesis account
if err := g.NewAccount(val.KeyringAccount); err != nil {
return err
}

return g.AddValidator(val)
}

// Export returns the genesis document of the network.
func (g *Genesis) Export() (*coretypes.GenesisDoc, error) {
gentxs := make([]json.RawMessage, 0, len(g.genTxs))
for _, genTx := range g.genTxs {
Expand All @@ -186,14 +211,6 @@ func (g *Genesis) Export() (*coretypes.GenesisDoc, error) {
)
}

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) {
Expand Down
Loading
Loading