package app import ( "context" "encoding/json" "testing" "time" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/mock" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/stretchr/testify/require" tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tharsis/ethermint/crypto/ethsecp256k1" "github.com/tharsis/ethermint/encoding" ethermint "github.com/tharsis/ethermint/types" "github.com/cosmos/cosmos-sdk/db/memdb" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" ) // GenesisState of the blockchain is represented here as a map of raw json // messages key'd by a identifier string. // The identifier is used to determine which module genesis information belongs // to so it may be appropriately routed during init chain. // Within this application default genesis information is retrieved from // the ModuleBasicManager which populates json from each BasicModule // object provided to it during init. // DefaultConsensusParams defines the default Tendermint consensus params used in // EthermintApp testing. var DefaultConsensusParams = &tmproto.ConsensusParams{ Block: &tmproto.BlockParams{ MaxBytes: 200000, MaxGas: -1, // no limit }, Evidence: &tmproto.EvidenceParams{ MaxAgeNumBlocks: 302400, MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration MaxBytes: 10000, }, Validator: &tmproto.ValidatorParams{ PubKeyTypes: []string{ tmtypes.ABCIPubKeyTypeEd25519, }, }, } // SetupOptions defines arguments that are passed into `Simapp` constructor. type SetupOptions struct { Logger log.Logger DB *memdb.MemDB InvCheckPeriod uint HomePath string SkipUpgradeHeights map[int64]bool EncConfig params.EncodingConfig AppOpts types.AppOptions } func NewTestAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptions) *EthermintApp { t.Helper() privVal := mock.NewPV() pubKey, err := privVal.GetPubKey(context.TODO()) require.NoError(t, err) // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) // generate genesis account priv, err := ethsecp256k1.GenerateKey() require.NoError(t, err) address := common.BytesToAddress(priv.PubKey().Address().Bytes()) acc := ðermint.EthAccount{ BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(address.Bytes()), nil, 0, 0), CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(), } balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } app := NewEthermintApp(options.Logger, options.DB, nil, true, options.SkipUpgradeHeights, options.HomePath, options.InvCheckPeriod, options.EncConfig, options.AppOpts) genesisState := NewDefaultGenesisState(app.appCodec) genesisState = genesisStateWithValSet(t, app, genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) if !isCheckTx { // init chain must be called to stop deliverState from being nil stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") require.NoError(t, err) // Initialize the chain app.InitChain( abci.RequestInitChain{ ChainId: "ethermint_9000-1", Validators: []abci.ValidatorUpdate{}, ConsensusParams: DefaultConsensusParams, AppStateBytes: stateBytes, }, ) } return app } // Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp. func Setup(t *testing.T, isCheckTx bool, patchGenesis func(*EthermintApp, simapp.GenesisState) simapp.GenesisState) *EthermintApp { t.Helper() privVal := mock.NewPV() pubKey, err := privVal.GetPubKey(context.TODO()) require.NoError(t, err) // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) priv, err := ethsecp256k1.GenerateKey() require.NoError(t, err) address := common.BytesToAddress(priv.PubKey().Address().Bytes()) acc := ðermint.EthAccount{ BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(address.Bytes()), nil, 0, 0), CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(), } balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } app := SetupWithGenesisValSet(t, valSet, patchGenesis, []authtypes.GenesisAccount{acc}, balance) return app } func setup(withGenesis bool, invCheckPeriod uint, patchGenesis func(*EthermintApp, simapp.GenesisState) simapp.GenesisState) (*EthermintApp, simapp.GenesisState) { encCdc := encoding.MakeConfig(ModuleBasics) app := NewEthermintApp(log.NewNopLogger(), memdb.NewDB(), nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) if withGenesis { genesisState := NewDefaultGenesisState(encCdc.Codec) if patchGenesis != nil { genesisState = patchGenesis(app, genesisState) } return app, genesisState } return app, simapp.GenesisState{} } // SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts // that also act as delegators. For simplicity, each validator is bonded with a delegation // of one consensus engine unit in the default token of the simapp from first genesis // account. A Nop logger is set in SimApp. func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, patchGenesis func(*EthermintApp, simapp.GenesisState) simapp.GenesisState, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *EthermintApp { t.Helper() app, genesisState := setup(true, 5, patchGenesis) genesisState = genesisStateWithValSet(t, app, genesisState, valSet, genAccs, balances...) stateBytes, err := json.MarshalIndent(genesisState, "", " ") require.NoError(t, err) // init chain will set the validator set and initialize the genesis accounts app.InitChain( abci.RequestInitChain{ ChainId: "ethermint_9000-1", Validators: []abci.ValidatorUpdate{}, ConsensusParams: DefaultConsensusParams, AppStateBytes: stateBytes, }, ) // commit genesis changes // app.Commit() // app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ // ChainID: "ethermint_9000-1", // Height: app.LastBlockHeight() + 1, // AppHash: app.LastCommitID().Hash, // ValidatorsHash: valSet.Hash(), // NextValidatorsHash: valSet.Hash(), // }}) return app } func genesisStateWithValSet(t *testing.T, app *EthermintApp, genesisState simapp.GenesisState, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance, ) simapp.GenesisState { // set genesis accounts authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) bondAmt := sdk.DefaultPowerReduction for _, val := range valSet.Validators { pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) require.NoError(t, err) pkAny, err := codectypes.NewAnyWithValue(pk) require.NoError(t, err) validator := stakingtypes.Validator{ OperatorAddress: sdk.ValAddress(val.Address).String(), ConsensusPubkey: pkAny, Jailed: false, Status: stakingtypes.Bonded, Tokens: bondAmt, DelegatorShares: sdk.OneDec(), Description: stakingtypes.Description{}, UnbondingHeight: int64(0), UnbondingTime: time.Unix(0, 0).UTC(), Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), MinSelfDelegation: sdk.ZeroInt(), } validators = append(validators, validator) delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) } // set validators and delegations stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) totalSupply := sdk.NewCoins() for _, b := range balances { // add genesis acc tokens to total supply totalSupply = totalSupply.Add(b.Coins...) } for range delegations { // add delegated tokens to total supply totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) } // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, }) // update total supply bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) return genesisState } // CreateRandomAccounts will generate random accounts func CreateRandomAccounts(accNum int) []sdk.AccAddress { // createRandomAccounts is a strategy used by addTestAddrs() in order to generated addresses in random order. testAddrs := make([]sdk.AccAddress, accNum) for i := 0; i < accNum; i++ { pk := ed25519.GenPrivKey().PubKey() testAddrs[i] = sdk.AccAddress(pk.Address()) } return testAddrs } // EmptyAppOptions is a stub implementing AppOptions type EmptyAppOptions struct{} // Get implements AppOptions func (ao EmptyAppOptions) Get(o string) interface{} { return nil }