diff --git a/simulation/state.go b/simulation/state.go index f775a19387..d1c6f25065 100644 --- a/simulation/state.go +++ b/simulation/state.go @@ -24,6 +24,7 @@ import ( evmtypes "github.com/zeta-chain/ethermint/x/evm/types" zetachains "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/crypto" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" zetaapp "github.com/zeta-chain/node/app" @@ -108,6 +109,7 @@ func updateObserverState(t *testing.T, rawState map[string]json.RawMessage, cdc observerState := new(observertypes.GenesisState) cdc.MustUnmarshalJSON(observerStateBz, observerState) + // Create an observer set as a subset of the current validator set observers := make([]string, 0) for _, validator := range validators { accAddress, err := observertypes.GetAccAddressFromOperatorAddress(validator.OperatorAddress) @@ -127,17 +129,27 @@ func updateObserverState(t *testing.T, rawState map[string]json.RawMessage, cdc } observers = observers[:numObservers] - observerState.Observers.ObserverList = observers - observerState.CrosschainFlags.IsInboundEnabled = true - observerState.CrosschainFlags.IsOutboundEnabled = true + // Create node account list for the observers set + nodeAccounts := make([]*observertypes.NodeAccount, len(observers)) + for i, observer := range observers { + nodeAccounts[i] = &observertypes.NodeAccount{ + Operator: observer, + GranteeAddress: observer, + GranteePubkey: &crypto.PubKeySet{}, + NodeStatus: observertypes.NodeStatus_Active, + } + //fmt.Println(nodeAccounts[i].GranteePubkey) + } + // Create a random tss tss, err := sample.TSSFromRand(r) require.NoError(t, err) tss.OperatorAddressList = observers - observerState.Tss = &tss + // Create a tss history tssHistory := make([]observertypes.TSS, 0) tssHistory = append(tssHistory, tss) + // Create chainnonces and pendingnonces chains := zetachains.DefaultChainsList() chainsNonces := make([]observertypes.ChainNonces, 0) pendingNonces := make([]observertypes.PendingNonces, 0) @@ -156,6 +168,11 @@ func updateObserverState(t *testing.T, rawState map[string]json.RawMessage, cdc pendingNonces = append(pendingNonces, pendingNonce) } + observerState.Tss = &tss + observerState.Observers.ObserverList = observers + observerState.NodeAccountList = nodeAccounts + observerState.CrosschainFlags.IsInboundEnabled = true + observerState.CrosschainFlags.IsOutboundEnabled = true observerState.ChainNonces = chainsNonces observerState.PendingNonces = pendingNonces observerState.TssHistory = tssHistory @@ -370,7 +387,6 @@ func AppStateRandomizedFn( if err != nil { panic(err) } - return appState, accs } diff --git a/testutil/sample/crypto.go b/testutil/sample/crypto.go index e17eaa849f..4f47f0a396 100644 --- a/testutil/sample/crypto.go +++ b/testutil/sample/crypto.go @@ -33,6 +33,33 @@ func PubKeySet() *crypto.PubKeySet { return &pubKeySet } +func Secp256PrivateKeyFromRand(r *rand.Rand) secp256k1.PrivKey { + randomBytes := make([]byte, 32) + _, err := r.Read(randomBytes) + if err != nil { + panic(err) + } + return secp256k1.GenPrivKeySecp256k1(randomBytes) +} + +func Ed25519PrivateKeyFromRand(r *rand.Rand) *ed25519.PrivKey { + randomBytes := make([]byte, 32) + _, err := r.Read(randomBytes) + if err != nil { + panic(err) + } + return ed25519.GenPrivKeyFromSecret(randomBytes) + +} + +func PubKeySetFromRand(r *rand.Rand) *crypto.PubKeySet { + pubKeySet := crypto.PubKeySet{ + Secp256k1: crypto.PubKey(Secp256PrivateKeyFromRand(r).PubKey().Bytes()), + Ed25519: crypto.PubKey(Ed25519PrivateKeyFromRand(r).PubKey().String()), + } + return &pubKeySet +} + // PubKeyString returns a sample public key string func PubKeyString() string { priKey := ed25519.GenPrivKey() @@ -47,6 +74,19 @@ func PubKeyString() string { return pubkey.String() } +func PubkeyStringFromRand(r *rand.Rand) string { + priKey := Ed25519PrivateKeyFromRand(r) + s, err := cosmos.Bech32ifyPubKey(cosmos.Bech32PubKeyTypeAccPub, priKey.PubKey()) + if err != nil { + panic(err) + } + pubkey, err := crypto.NewPubKey(s) + if err != nil { + panic(err) + } + return pubkey.String() +} + // PrivKeyAddressPair returns a private key, address pair func PrivKeyAddressPair() (*ed25519.PrivKey, sdk.AccAddress) { privKey := ed25519.GenPrivKey() diff --git a/testutil/sample/observer.go b/testutil/sample/observer.go index c6ade5b58f..ceb751fd5a 100644 --- a/testutil/sample/observer.go +++ b/testutil/sample/observer.go @@ -4,6 +4,7 @@ import ( "fmt" "math/rand" "testing" + "time" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/testutil/testdata" @@ -320,3 +321,14 @@ func GasPriceIncreaseFlags() types.GasPriceIncreaseFlags { MaxPendingCctxs: 100, } } + +func GasPriceIncreaseFlagsFromRand(r *rand.Rand) types.GasPriceIncreaseFlags { + minValue := 1 + maxValue := 100 + return types.GasPriceIncreaseFlags{ + EpochLength: int64(r.Intn(maxValue-minValue) + minValue), + RetryInterval: time.Duration(r.Intn(maxValue-minValue) + minValue), + GasPriceIncreasePercent: 1, + MaxPendingCctxs: 100, + } +} diff --git a/x/crosschain/simulation/operation_add_outbound_tracker.go b/x/crosschain/simulation/operation_add_outbound_tracker.go index 343efe1c9b..9fb74f4e5c 100644 --- a/x/crosschain/simulation/operation_add_outbound_tracker.go +++ b/x/crosschain/simulation/operation_add_outbound_tracker.go @@ -63,7 +63,7 @@ func SimulateMsgAddOutboundTracker(k keeper.Keeper) simtypes.Operation { } // pick a random nonce from the pending nonces between 0 and nonceLow - // Ih nonce low is the same as nonce high, it means that there are no pending nonces to add trackers for + // If nonce low is the same as nonce high, it means that there are no pending nonces to add trackers for if pendingNonces.NonceLow == pendingNonces.NonceHigh { return simtypes.NoOpMsg( types.ModuleName, @@ -91,6 +91,26 @@ func SimulateMsgAddOutboundTracker(k keeper.Keeper) simtypes.Operation { "tracker is maxed", ), nil, nil } + + // Verify the nonceToCCTX exists + nonceToCCTX, found := k.GetObserverKeeper().GetNonceToCctx(ctx, tss.TssPubkey, chainID, int64(nonce)) + if !found { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgAddOutboundTracker, + "no nonce to cctx found", + ), nil, nil + } + + // Verify the cctx exists + _, found = k.GetCrossChainTx(ctx, nonceToCCTX.CctxIndex) + if !found { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgAddOutboundTracker, + "no cctx found for nonce", + ), nil, nil + } // Add a new inbound Tracker msg := types.MsgAddOutboundTracker{ Creator: randomObserver, diff --git a/x/crosschain/simulation/operations.go b/x/crosschain/simulation/operations.go index 208747e0d6..61a242de00 100644 --- a/x/crosschain/simulation/operations.go +++ b/x/crosschain/simulation/operations.go @@ -239,7 +239,21 @@ func GetRandomAccountAndObserver( return simtypes.Account{}, "", fmt.Errorf("no observers present in observer set found") } - randomObserver := GetRandomObserver(r, observers.ObserverList) + randomObserver := "" + foundObserver := false + for i := 0; i < 10; i++ { + randomObserver = GetRandomObserver(r, observers.ObserverList) + ok := k.GetObserverKeeper().IsNonTombstonedObserver(ctx, randomObserver) + if ok { + foundObserver = true + break + } + } + + if !foundObserver { + return simtypes.Account{}, "", fmt.Errorf("no observer found") + } + simAccount, err := GetObserverAccount(randomObserver, accounts) if err != nil { return simtypes.Account{}, "", err diff --git a/x/observer/genesis.go b/x/observer/genesis.go index aad05b64ce..4de975c5ae 100644 --- a/x/observer/genesis.go +++ b/x/observer/genesis.go @@ -11,6 +11,7 @@ import ( // InitGenesis initializes the observer module's state from a provided genesis // state. func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + if genState.Observers.Len() > 0 { k.SetObserverSet(ctx, genState.Observers) } else { diff --git a/x/observer/keeper/msg_server_update_observer.go b/x/observer/keeper/msg_server_update_observer.go index 2e60b291fb..c1cb118ea4 100644 --- a/x/observer/keeper/msg_server_update_observer.go +++ b/x/observer/keeper/msg_server_update_observer.go @@ -36,6 +36,7 @@ func (k msgServer) UpdateObserver( "Observer address is not authorized : %s", msg.OldObserverAddress) } + // The New address should be a validator, not jailed and bonded err = k.IsValidator(ctx, msg.NewObserverAddress) if err != nil { return nil, errorsmod.Wrap(types.ErrUpdateObserver, err.Error()) @@ -72,7 +73,7 @@ func (k msgServer) UpdateObserver( return nil, errorsmod.Wrap(types.ErrLastObserverCountNotFound, "Observer count not found") } if lastBlockCount.Count != totalObserverCountCurrentBlock { - return nil, errorsmod.Wrap(types.ErrUpdateObserver, "Observer count mismatch") + return nil, errorsmod.Wrapf(types.ErrUpdateObserver, "Observer count mismatch current block : %d , last block : %d", totalObserverCountCurrentBlock, lastBlockCount.Count) } return &types.MsgUpdateObserverResponse{}, nil } diff --git a/x/observer/simulation/operation_add_observer.go b/x/observer/simulation/operation_add_observer.go new file mode 100644 index 0000000000..d03a5e991b --- /dev/null +++ b/x/observer/simulation/operation_add_observer.go @@ -0,0 +1,90 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/x/observer/keeper" + "github.com/zeta-chain/node/x/observer/types" +) + +// SimulateMsgAddObserver generates a TypeMsgAddObserver and delivers it. The message adds an observer to the observer set +func SimulateMsgAddObserver(k keeper.Keeper) simtypes.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []simtypes.Account, _ string, + ) (OperationMsg simtypes.OperationMsg, futureOps []simtypes.FutureOperation, err error) { + policyAccount, err := GetPolicyAccount(ctx, k.GetAuthorityKeeper(), accounts) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgAddObserver, err.Error()), nil, nil + } + + authAccount := k.GetAuthKeeper().GetAccount(ctx, policyAccount.Address) + spendable := k.GetBankKeeper().SpendableCoins(ctx, authAccount.GetAddress()) + + observerSet, found := k.GetObserverSet(ctx) + if !found { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgAddObserver, + "no observer set found", + ), nil, nil + } + + observerMap := make(map[string]bool) + for _, observer := range observerSet.ObserverList { + observerMap[observer] = true + } + + nodeAccounts := k.GetAllNodeAccount(ctx) + + // Pick a random observer which part of the node account but not in the observer set + var newObserver string + foundNA := false + for i := 0; i < 10; i++ { + newObserver = nodeAccounts[r.Intn(len(nodeAccounts))].Operator + if _, found := observerMap[newObserver]; !found { + foundNA = true + break + } + } + if !foundNA { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgAddObserver, + "no node accounts available which can be added as observer", + ), nil, nil + } + + msg := types.MsgAddObserver{ + Creator: policyAccount.Address.String(), + ObserverAddress: newObserver, + ZetaclientGranteePubkey: sample.PubkeyStringFromRand(r), + AddNodeAccountOnly: false, + } + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), err.Error()), nil, err + } + + txCtx := simulation.OperationInput{ + R: r, + App: app, + TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: &msg, + MsgType: msg.Type(), + Context: ctx, + SimAccount: policyAccount, + AccountKeeper: k.GetAuthKeeper(), + Bankkeeper: k.GetBankKeeper(), + ModuleName: types.ModuleName, + CoinsSpentInMsg: spendable, + } + + return simulation.GenAndDeliverTxWithRandFees(txCtx) + } +} diff --git a/x/observer/simulation/operation_add_observer_node_account.go b/x/observer/simulation/operation_add_observer_node_account.go new file mode 100644 index 0000000000..4547d9faf8 --- /dev/null +++ b/x/observer/simulation/operation_add_observer_node_account.go @@ -0,0 +1,95 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/x/observer/keeper" + "github.com/zeta-chain/node/x/observer/types" +) + +// SimulateMsgAddObserverNodeAccount generates a TypeMsgAddObserver and delivers it. +func SimulateMsgAddObserverNodeAccount(k keeper.Keeper) simtypes.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []simtypes.Account, _ string, + ) (OperationMsg simtypes.OperationMsg, futureOps []simtypes.FutureOperation, err error) { + policyAccount, err := GetPolicyAccount(ctx, k.GetAuthorityKeeper(), accounts) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgAddObserver, err.Error()), nil, nil + } + + authAccount := k.GetAuthKeeper().GetAccount(ctx, policyAccount.Address) + spendable := k.GetBankKeeper().SpendableCoins(ctx, authAccount.GetAddress()) + + observerSet, found := k.GetObserverSet(ctx) + if !found { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgAddObserver, + "no observer set found", + ), nil, nil + } + + observerMap := make(map[string]bool) + for _, observer := range observerSet.ObserverList { + observerMap[observer] = true + } + + validators := k.GetStakingKeeper().GetAllValidators(ctx) + if len(validators) == 0 { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgUpdateObserver, + "no validators found", + ), nil, nil + } + newObserver := "" + for { + randomValidator := validators[r.Intn(len(validators))] + randomValidatorOperatorAddress, err := types.GetAccAddressFromOperatorAddress(randomValidator.OperatorAddress) + if err != nil { + continue + } + newObserver = randomValidatorOperatorAddress.String() + err = k.IsValidator(ctx, newObserver) + if err != nil { + continue + } + if _, ok := observerMap[newObserver]; !ok { + break + } + } + + msg := types.MsgAddObserver{ + Creator: policyAccount.Address.String(), + ObserverAddress: newObserver, + ZetaclientGranteePubkey: sample.PubkeyStringFromRand(r), + AddNodeAccountOnly: true, + } + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), err.Error()), nil, err + } + + txCtx := simulation.OperationInput{ + R: r, + App: app, + TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: &msg, + MsgType: msg.Type(), + Context: ctx, + SimAccount: policyAccount, + AccountKeeper: k.GetAuthKeeper(), + Bankkeeper: k.GetBankKeeper(), + ModuleName: types.ModuleName, + CoinsSpentInMsg: spendable, + } + + return simulation.GenAndDeliverTxWithRandFees(txCtx) + } +} diff --git a/x/observer/simulation/operation_reset_chain_nonces.go b/x/observer/simulation/operation_reset_chain_nonces.go index 372107cb39..17f9fce623 100644 --- a/x/observer/simulation/operation_reset_chain_nonces.go +++ b/x/observer/simulation/operation_reset_chain_nonces.go @@ -25,7 +25,7 @@ func SimulateMsgResetChainNonces(k keeper.Keeper) simtypes.Operation { authAccount := k.GetAuthKeeper().GetAccount(ctx, policyAccount.Address) spendable := k.GetBankKeeper().SpendableCoins(ctx, authAccount.GetAddress()) - randomChain, err := GetExternalChain(ctx, k, r, 100) + randomChain, err := GetExternalChain(ctx, k, r, 10) if err != nil { return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgResetChainNonces, err.Error()), nil, fmt.Errorf("error getting external chain") } diff --git a/x/observer/simulation/operation_update_chain_params.go b/x/observer/simulation/operation_update_chain_params.go index 33cf899562..78855334c9 100644 --- a/x/observer/simulation/operation_update_chain_params.go +++ b/x/observer/simulation/operation_update_chain_params.go @@ -25,7 +25,7 @@ func SimulateMsgUpdateChainParams(k keeper.Keeper) simtypes.Operation { authAccount := k.GetAuthKeeper().GetAccount(ctx, policyAccount.Address) spendable := k.GetBankKeeper().SpendableCoins(ctx, authAccount.GetAddress()) - randomChain, err := GetExternalChain(ctx, k, r, 100) + randomChain, err := GetExternalChain(ctx, k, r, 10) if err != nil { return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUpdateChainParams, err.Error()), nil, nil } diff --git a/x/observer/simulation/operation_update_gas_price_increase_flags.go b/x/observer/simulation/operation_update_gas_price_increase_flags.go new file mode 100644 index 0000000000..89d20be901 --- /dev/null +++ b/x/observer/simulation/operation_update_gas_price_increase_flags.go @@ -0,0 +1,57 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/x/observer/keeper" + "github.com/zeta-chain/node/x/observer/types" +) + +// SimulateMsgUpdateGasPriceIncreaseFlags generates a MsgUpdateGasPriceIncreaseFlags with random values +func SimulateMsgUpdateGasPriceIncreaseFlags(k keeper.Keeper) simtypes.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []simtypes.Account, _ string, + ) (OperationMsg simtypes.OperationMsg, futureOps []simtypes.FutureOperation, err error) { + policyAccount, err := GetPolicyAccount(ctx, k.GetAuthorityKeeper(), accounts) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUpdateGasPriceIncreaseFlags, err.Error()), nil, nil + } + + authAccount := k.GetAuthKeeper().GetAccount(ctx, policyAccount.Address) + spendable := k.GetBankKeeper().SpendableCoins(ctx, authAccount.GetAddress()) + + gp := sample.GasPriceIncreaseFlagsFromRand(r) + + msg := types.MsgUpdateGasPriceIncreaseFlags{ + Creator: policyAccount.Address.String(), + GasPriceIncreaseFlags: gp, + } + + err = msg.ValidateBasic() + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), err.Error()), nil, err + } + + txCtx := simulation.OperationInput{ + R: r, + App: app, + TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: &msg, + MsgType: msg.Type(), + Context: ctx, + SimAccount: policyAccount, + AccountKeeper: k.GetAuthKeeper(), + Bankkeeper: k.GetBankKeeper(), + ModuleName: types.ModuleName, + CoinsSpentInMsg: spendable, + } + + return simulation.GenAndDeliverTxWithRandFees(txCtx) + } +} diff --git a/x/observer/simulation/operation_update_observer.go b/x/observer/simulation/operation_update_observer.go new file mode 100644 index 0000000000..1fe526948e --- /dev/null +++ b/x/observer/simulation/operation_update_observer.go @@ -0,0 +1,104 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/zeta-chain/node/x/observer/keeper" + "github.com/zeta-chain/node/x/observer/types" +) + +// SimulateMsgUpdateObserver generates a TypeMsgUpdateObserver and delivers it. +func SimulateMsgUpdateObserver(k keeper.Keeper) simtypes.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []simtypes.Account, _ string, + ) (OperationMsg simtypes.OperationMsg, futureOps []simtypes.FutureOperation, err error) { + policyAccount, err := GetPolicyAccount(ctx, k.GetAuthorityKeeper(), accounts) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUpdateObserver, err.Error()), nil, nil + } + + authAccount := k.GetAuthKeeper().GetAccount(ctx, policyAccount.Address) + spendable := k.GetBankKeeper().SpendableCoins(ctx, authAccount.GetAddress()) + + _, randomObserver, err, observerList := GetRandomAccountAndObserver(r, ctx, k, accounts) + + observerMap := make(map[string]bool) + for _, observer := range observerList { + observerMap[observer] = true + } + + validators := k.GetStakingKeeper().GetAllValidators(ctx) + if len(validators) == 0 { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgUpdateObserver, + "no validators found", + ), nil, nil + } + newObserver := "" + for { + randomValidator := validators[r.Intn(len(validators))] + nO, err := types.GetAccAddressFromOperatorAddress(randomValidator.OperatorAddress) + if err != nil { + continue + } + newObserver = nO.String() + err = k.IsValidator(ctx, newObserver) + if err != nil { + continue + } + if _, ok := observerMap[newObserver]; !ok { + break + } + + } + + lastBlockCount, found := k.GetLastObserverCount(ctx) + if !found { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgUpdateObserver, + "no last block count found", + ), nil, nil + } + if int(lastBlockCount.Count) != len(observerList) { + return simtypes.NoOpMsg( + types.ModuleName, + types.TypeMsgUpdateObserver, + "observer count mismatch", + ), nil, nil + } + + msg := types.MsgUpdateObserver{ + Creator: policyAccount.Address.String(), + OldObserverAddress: randomObserver, + NewObserverAddress: newObserver, + UpdateReason: types.ObserverUpdateReason_AdminUpdate, + } + + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), err.Error()), nil, err + } + + txCtx := simulation.OperationInput{ + R: r, + App: app, + TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: &msg, + MsgType: msg.Type(), + Context: ctx, + SimAccount: policyAccount, + AccountKeeper: k.GetAuthKeeper(), + Bankkeeper: k.GetBankKeeper(), + ModuleName: types.ModuleName, + CoinsSpentInMsg: spendable, + } + + return simulation.GenAndDeliverTxWithRandFees(txCtx) + } +} diff --git a/x/observer/simulation/operations.go b/x/observer/simulation/operations.go index d137de17c6..28f09ea58e 100644 --- a/x/observer/simulation/operations.go +++ b/x/observer/simulation/operations.go @@ -45,7 +45,7 @@ const ( DefaultWeightMsgTypeMsgRemoveChainParams = 10 DefaultWeightMsgTypeMsgResetChainNonces = 5 DefaultWeightMsgTypeMsgUpdateGasPriceIncreaseFlags = 10 - DefaultWeightMsgTypeMsgAddObserver = 10 + DefaultWeightMsgTypeMsgAddObserver = 5 ) // WeightedOperations for observer module @@ -145,6 +145,26 @@ func WeightedOperations( weightMsgTypeMsgResetChainNonces, SimulateMsgResetChainNonces(k), ), + + simulation.NewWeightedOperation( + weightMsgTypeMsgUpdateGasPriceIncreaseFlags, + SimulateMsgUpdateGasPriceIncreaseFlags(k), + ), + + simulation.NewWeightedOperation( + weightMsgTypeMsgAddObserver, + SimulateMsgUpdateObserver(k), + ), + + simulation.NewWeightedOperation( + weightMsgTypeMsgAddObserver, + SimulateMsgAddObserverNodeAccount(k), + ), + + simulation.NewWeightedOperation( + weightMsgTypeMsgAddObserver, + SimulateMsgAddObserver(k), + ), } } @@ -184,3 +204,71 @@ func GetExternalChain(ctx sdk.Context, k keeper.Keeper, r *rand.Rand, retryCount } return chains.Chain{}, fmt.Errorf("no external chain found") } + +// GetRandomAccountAndObserver returns a random account and the associated observer address +func GetRandomAccountAndObserver( + r *rand.Rand, + ctx sdk.Context, + k keeper.Keeper, + accounts []simtypes.Account, +) (simtypes.Account, string, []string, error) { + observerList := []string{} + observers, found := k.GetObserverSet(ctx) + if !found { + return simtypes.Account{}, "", observerList, fmt.Errorf("observer set not found") + } + + observerList = observers.ObserverList + + if len(observers.ObserverList) == 0 { + return simtypes.Account{}, "", observerList, fmt.Errorf("no observers present in observer set found") + } + + randomObserver := "" + foundObserver := false + for i := 0; i < 10; i++ { + randomObserver = GetRandomObserver(r, observers.ObserverList) + _, foundNodeAccount := k.GetNodeAccount(ctx, randomObserver) + if !foundNodeAccount { + continue + } + ok := k.IsNonTombstonedObserver(ctx, randomObserver) + if ok { + foundObserver = true + break + } + } + + if !foundObserver { + return simtypes.Account{}, "", observerList, fmt.Errorf("no observer found") + } + + simAccount, err := GetObserverAccount(randomObserver, accounts) + if err != nil { + return simtypes.Account{}, "", observerList, err + } + return simAccount, randomObserver, observerList, nil +} + +func GetRandomObserver(r *rand.Rand, observerList []string) string { + idx := r.Intn(len(observerList)) + return observerList[idx] +} + +// GetObserverAccount returns the account associated with the observer address from the list of accounts provided +// GetObserverAccount can fail if all the observers are removed from the observer set ,this can happen +//if the other modules create transactions which affect the validator +//and triggers any of the staking hooks defined in the observer modules + +func GetObserverAccount(observerAddress string, accounts []simtypes.Account) (simtypes.Account, error) { + operatorAddress, err := types.GetOperatorAddressFromAccAddress(observerAddress) + if err != nil { + return simtypes.Account{}, fmt.Errorf("validator not found for observer ") + } + + simAccount, found := simtypes.FindAccount(accounts, operatorAddress) + if !found { + return simtypes.Account{}, fmt.Errorf("operator account not found") + } + return simAccount, nil +} diff --git a/x/observer/types/expected_keepers.go b/x/observer/types/expected_keepers.go index 2788187c94..15eb476af6 100644 --- a/x/observer/types/expected_keepers.go +++ b/x/observer/types/expected_keepers.go @@ -19,6 +19,7 @@ type StakingKeeper interface { valAddr sdk.ValAddress, ) (delegation stakingtypes.Delegation, found bool) SetValidator(ctx sdk.Context, validator stakingtypes.Validator) + GetAllValidators(ctx sdk.Context) (validators []stakingtypes.Validator) } type SlashingKeeper interface {