diff --git a/app/app.go b/app/app.go index 63b889fa9c..0625df5f08 100644 --- a/app/app.go +++ b/app/app.go @@ -10,9 +10,6 @@ import ( "github.com/gorilla/mux" "github.com/rakyll/statik/fs" "github.com/spf13/cast" - emissionsModule "github.com/zeta-chain/zetacore/x/emissions" - emissionsModuleKeeper "github.com/zeta-chain/zetacore/x/emissions/keeper" - emissionsModuleTypes "github.com/zeta-chain/zetacore/x/emissions/types" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -97,13 +94,21 @@ import ( "github.com/zeta-chain/zetacore/docs/openapi" srvflags "github.com/zeta-chain/zetacore/server/flags" + authoritymodule "github.com/zeta-chain/zetacore/x/authority" + authoritykeeper "github.com/zeta-chain/zetacore/x/authority/keeper" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + crosschainmodule "github.com/zeta-chain/zetacore/x/crosschain" crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" - fungibleModule "github.com/zeta-chain/zetacore/x/fungible" - fungibleModuleKeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" - fungibleModuleTypes "github.com/zeta-chain/zetacore/x/fungible/types" + emissionsmodule "github.com/zeta-chain/zetacore/x/emissions" + emissionskeeper "github.com/zeta-chain/zetacore/x/emissions/keeper" + emissionstypes "github.com/zeta-chain/zetacore/x/emissions/types" + + fungiblemodule "github.com/zeta-chain/zetacore/x/fungible" + fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" observermodule "github.com/zeta-chain/zetacore/x/observer" observerkeeper "github.com/zeta-chain/zetacore/x/observer/keeper" @@ -175,27 +180,28 @@ var ( vesting.AppModuleBasic{}, evm.AppModuleBasic{}, feemarket.AppModuleBasic{}, + authoritymodule.AppModuleBasic{}, crosschainmodule.AppModuleBasic{}, observermodule.AppModuleBasic{}, - fungibleModule.AppModuleBasic{}, - emissionsModule.AppModuleBasic{}, + fungiblemodule.AppModuleBasic{}, + emissionsmodule.AppModuleBasic{}, groupmodule.AppModuleBasic{}, authzmodule.AppModuleBasic{}, ) // module account permissions maccPerms = map[string][]string{ - authtypes.FeeCollectorName: nil, - distrtypes.ModuleName: nil, - stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, - stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, - govtypes.ModuleName: {authtypes.Burner}, - crosschaintypes.ModuleName: {authtypes.Minter, authtypes.Burner}, - evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, - fungibleModuleTypes.ModuleName: {authtypes.Minter, authtypes.Burner}, - emissionsModuleTypes.ModuleName: nil, - emissionsModuleTypes.UndistributedObserverRewardsPool: nil, - emissionsModuleTypes.UndistributedTssRewardsPool: nil, + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + govtypes.ModuleName: {authtypes.Burner}, + crosschaintypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + fungibletypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + emissionstypes.ModuleName: nil, + emissionstypes.UndistributedObserverRewardsPool: nil, + emissionstypes.UndistributedTssRewardsPool: nil, } // module accounts that are NOT allowed to receive tokens @@ -226,28 +232,34 @@ type App struct { tkeys map[string]*storetypes.TransientStoreKey memKeys map[string]*storetypes.MemoryStoreKey - // keepers - AccountKeeper authkeeper.AccountKeeper - BankKeeper bankkeeper.Keeper - StakingKeeper stakingkeeper.Keeper - SlashingKeeper slashingkeeper.Keeper - DistrKeeper distrkeeper.Keeper - GovKeeper govkeeper.Keeper - CrisisKeeper crisiskeeper.Keeper - UpgradeKeeper upgradekeeper.Keeper - ParamsKeeper paramskeeper.Keeper - EvidenceKeeper evidencekeeper.Keeper + mm *module.Manager + sm *module.SimulationManager + configurator module.Configurator + + // sdk keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + StakingKeeper stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + CrisisKeeper crisiskeeper.Keeper + UpgradeKeeper upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + EvidenceKeeper evidencekeeper.Keeper + GroupKeeper groupkeeper.Keeper + AuthzKeeper authzkeeper.Keeper + + // evm keepers + EvmKeeper *evmkeeper.Keeper + FeeMarketKeeper feemarketkeeper.Keeper + + // zetachain keepers + AuthorityKeeper authoritykeeper.Keeper CrosschainKeeper crosschainkeeper.Keeper ObserverKeeper *observerkeeper.Keeper - mm *module.Manager - sm *module.SimulationManager - configurator module.Configurator - EvmKeeper *evmkeeper.Keeper - FeeMarketKeeper feemarketkeeper.Keeper - FungibleKeeper fungibleModuleKeeper.Keeper - EmissionsKeeper emissionsModuleKeeper.Keeper - GroupKeeper groupkeeper.Keeper - AuthzKeeper authzkeeper.Keeper + FungibleKeeper fungiblekeeper.Keeper + EmissionsKeeper emissionskeeper.Keeper } // New returns a reference to an initialized ZetaApp. @@ -279,12 +291,14 @@ func New( group.StoreKey, upgradetypes.StoreKey, evidencetypes.StoreKey, + authzkeeper.StoreKey, + evmtypes.StoreKey, + feemarkettypes.StoreKey, + authoritytypes.StoreKey, crosschaintypes.StoreKey, observertypes.StoreKey, - evmtypes.StoreKey, feemarkettypes.StoreKey, - fungibleModuleTypes.StoreKey, - emissionsModuleTypes.StoreKey, - authzkeeper.StoreKey, + fungibletypes.StoreKey, + emissionstypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey) memKeys := sdk.NewMemoryStoreKeys() @@ -316,10 +330,13 @@ func New( maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(), ) + logger.Info("bank keeper blocklist addresses", "addresses", app.BlockedAddrs()) + app.BankKeeper = bankkeeper.NewBaseKeeper( appCodec, keys[banktypes.StoreKey], app.AccountKeeper, app.GetSubspace(banktypes.ModuleName), app.BlockedAddrs(), ) + stakingKeeper := stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName), ) @@ -328,15 +345,25 @@ func New( appCodec, keys[distrtypes.StoreKey], app.GetSubspace(distrtypes.ModuleName), app.AccountKeeper, app.BankKeeper, &stakingKeeper, authtypes.FeeCollectorName, ) + app.SlashingKeeper = slashingkeeper.NewKeeper( appCodec, keys[slashingtypes.StoreKey], &stakingKeeper, app.GetSubspace(slashingtypes.ModuleName), ) + app.CrisisKeeper = crisiskeeper.NewKeeper( app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName, ) + app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.AuthorityKeeper = authoritykeeper.NewKeeper( + appCodec, + keys[authoritytypes.StoreKey], + keys[authoritytypes.MemStoreKey], + authtypes.NewModuleAddress(govtypes.ModuleName), + ) + app.ObserverKeeper = observerkeeper.NewKeeper( appCodec, keys[observertypes.StoreKey], @@ -344,6 +371,7 @@ func New( app.GetSubspace(observertypes.ModuleName), &stakingKeeper, app.SlashingKeeper, + app.AuthorityKeeper, ) // register the staking hooks @@ -356,19 +384,21 @@ func New( keys[authzkeeper.StoreKey], appCodec, app.MsgServiceRouter(), - app.AccountKeeper) + app.AccountKeeper, + ) - app.EmissionsKeeper = *emissionsModuleKeeper.NewKeeper( + app.EmissionsKeeper = *emissionskeeper.NewKeeper( appCodec, - keys[emissionsModuleTypes.StoreKey], - keys[emissionsModuleTypes.MemStoreKey], - app.GetSubspace(emissionsModuleTypes.ModuleName), + keys[emissionstypes.StoreKey], + keys[emissionstypes.MemStoreKey], + app.GetSubspace(emissionstypes.ModuleName), authtypes.FeeCollectorName, app.BankKeeper, app.StakingKeeper, app.ObserverKeeper, app.AccountKeeper, ) + // Create Ethermint keepers tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) feeSs := app.GetSubspace(feemarkettypes.ModuleName) @@ -385,15 +415,16 @@ func New( tracer, evmSs, ) - app.FungibleKeeper = *fungibleModuleKeeper.NewKeeper( + app.FungibleKeeper = *fungiblekeeper.NewKeeper( appCodec, - keys[fungibleModuleTypes.StoreKey], - keys[fungibleModuleTypes.MemStoreKey], - app.GetSubspace(fungibleModuleTypes.ModuleName), + keys[fungibletypes.StoreKey], + keys[fungibletypes.MemStoreKey], + app.GetSubspace(fungibletypes.ModuleName), app.AccountKeeper, app.EvmKeeper, app.BankKeeper, app.ObserverKeeper, + app.AuthorityKeeper, ) app.CrosschainKeeper = *crosschainkeeper.NewKeeper( @@ -406,6 +437,7 @@ func New( app.BankKeeper, app.ObserverKeeper, &app.FungibleKeeper, + app.AuthorityKeeper, ) app.GroupKeeper = groupkeeper.NewKeeper(keys[group.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper, group.Config{ MaxExecutionPeriod: 2 * time.Hour, // Two hours. @@ -476,10 +508,11 @@ func New( groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, interfaceRegistry), evm.NewAppModule(app.EvmKeeper, app.AccountKeeper, evmSs), feemarket.NewAppModule(app.FeeMarketKeeper, feeSs), + authoritymodule.NewAppModule(appCodec, app.AuthorityKeeper), crosschainmodule.NewAppModule(appCodec, app.CrosschainKeeper), observermodule.NewAppModule(appCodec, *app.ObserverKeeper), - fungibleModule.NewAppModule(appCodec, app.FungibleKeeper), - emissionsModule.NewAppModule(appCodec, app.EmissionsKeeper), + fungiblemodule.NewAppModule(appCodec, app.FungibleKeeper), + emissionsmodule.NewAppModule(appCodec, app.EmissionsKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ) @@ -506,9 +539,10 @@ func New( feemarkettypes.ModuleName, crosschaintypes.ModuleName, observertypes.ModuleName, - fungibleModuleTypes.ModuleName, - emissionsModuleTypes.ModuleName, + fungibletypes.ModuleName, + emissionstypes.ModuleName, authz.ModuleName, + authoritytypes.ModuleName, ) app.mm.SetOrderEndBlockers( banktypes.ModuleName, @@ -528,9 +562,10 @@ func New( feemarkettypes.ModuleName, crosschaintypes.ModuleName, observertypes.ModuleName, - fungibleModuleTypes.ModuleName, - emissionsModuleTypes.ModuleName, + fungibletypes.ModuleName, + emissionstypes.ModuleName, authz.ModuleName, + authoritytypes.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -557,9 +592,10 @@ func New( vestingtypes.ModuleName, observertypes.ModuleName, crosschaintypes.ModuleName, - fungibleModuleTypes.ModuleName, - emissionsModuleTypes.ModuleName, + fungibletypes.ModuleName, + emissionstypes.ModuleName, authz.ModuleName, + authoritytypes.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) @@ -769,8 +805,8 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(group.ModuleName) paramsKeeper.Subspace(crosschaintypes.ModuleName) paramsKeeper.Subspace(observertypes.ModuleName) - paramsKeeper.Subspace(fungibleModuleTypes.ModuleName) - paramsKeeper.Subspace(emissionsModuleTypes.ModuleName) + paramsKeeper.Subspace(fungibletypes.ModuleName) + paramsKeeper.Subspace(emissionstypes.ModuleName) return paramsKeeper } diff --git a/changelog.md b/changelog.md index 72b7be97e7..2c8a745b32 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,13 @@ ## Unreleased +### Breaking Changes + +* Admin policies have been moved from `observer` to a new module `authority`. + * Updating admin policies now requires to send a governance proposal executing the `UpdatePolicies` message in the `authority` module. + * The `Policies` query of the `authority` module must be used to get the current admin policies. + * `PolicyType_group1` has been renamed into `PolicyType_groupEmergency` and `PolicyType_group2` has been renamed into `PolicyType_groupAdmin`. + ### Refactor * [1511](https://github.com/zeta-chain/node/pull/1511) - move ballot voting logic from `crosschain` to `observer` @@ -15,6 +22,7 @@ ### Features * [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses +* [1815](https://github.com/zeta-chain/node/pull/1815) - add authority module for authorized actions ### Tests diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index 559f07c862..713627171b 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -118,6 +118,8 @@ then # set admin account zetacored add-genesis-account zeta1srsq755t654agc0grpxj4y3w0znktrpr9tcdgk 100000000000000000000000000azeta zetacored add-genesis-account zeta1n0rn6sne54hv7w2uu93fl48ncyqz97d3kty6sh 100000000000000000000000000azeta # Funds the localnet_gov_admin account + cat $HOME/.zetacored/config/genesis.json | jq '.app_state["authority"]["policies"]["items"][0]["address"]="zeta1srsq755t654agc0grpxj4y3w0znktrpr9tcdgk"' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json + cat $HOME/.zetacored/config/genesis.json | jq '.app_state["authority"]["policies"]["items"][1]["address"]="zeta1srsq755t654agc0grpxj4y3w0znktrpr9tcdgk"' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json cat $HOME/.zetacored/config/genesis.json | jq '.app_state["observer"]["params"]["admin_policy"][0]["address"]="zeta1srsq755t654agc0grpxj4y3w0znktrpr9tcdgk"' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json cat $HOME/.zetacored/config/genesis.json | jq '.app_state["observer"]["params"]["admin_policy"][1]["address"]="zeta1srsq755t654agc0grpxj4y3w0znktrpr9tcdgk"' > $HOME/.zetacored/config/tmp_genesis.json && mv $HOME/.zetacored/config/tmp_genesis.json $HOME/.zetacored/config/genesis.json diff --git a/docs/cli/zetacored/zetacored_query.md b/docs/cli/zetacored/zetacored_query.md index c962a93bbf..cb6eb60fde 100644 --- a/docs/cli/zetacored/zetacored_query.md +++ b/docs/cli/zetacored/zetacored_query.md @@ -27,6 +27,7 @@ zetacored query [flags] * [zetacored](zetacored.md) - Zetacore Daemon (server) * [zetacored query account](zetacored_query_account.md) - Query for account by address * [zetacored query auth](zetacored_query_auth.md) - Querying commands for the auth module +* [zetacored query authority](zetacored_query_authority.md) - Querying commands for the authority module * [zetacored query authz](zetacored_query_authz.md) - Querying commands for the authz module * [zetacored query bank](zetacored_query_bank.md) - Querying commands for the bank module * [zetacored query block](zetacored_query_block.md) - Get verified data for the block at given height diff --git a/docs/cli/zetacored/zetacored_query_authority.md b/docs/cli/zetacored/zetacored_query_authority.md new file mode 100644 index 0000000000..323f9ab3ca --- /dev/null +++ b/docs/cli/zetacored/zetacored_query_authority.md @@ -0,0 +1,29 @@ +# query authority + +Querying commands for the authority module + +``` +zetacored query authority [flags] +``` + +### Options + +``` + -h, --help help for authority +``` + +### Options inherited from parent commands + +``` + --chain-id string The network chain ID + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored query](zetacored_query.md) - Querying subcommands +* [zetacored query authority show-policies](zetacored_query_authority_show-policies.md) - show the policies + diff --git a/docs/cli/zetacored/zetacored_query_authority_show-policies.md b/docs/cli/zetacored/zetacored_query_authority_show-policies.md new file mode 100644 index 0000000000..1a7e553283 --- /dev/null +++ b/docs/cli/zetacored/zetacored_query_authority_show-policies.md @@ -0,0 +1,33 @@ +# query authority show-policies + +show the policies + +``` +zetacored query authority show-policies [flags] +``` + +### Options + +``` + --grpc-addr string the gRPC endpoint to use for this chain + --grpc-insecure allow gRPC over insecure channels, if not TLS the server must use TLS + --height int Use a specific height to query state at (this can error if the node is pruning state) + -h, --help help for show-policies + --node string [host]:[port] to Tendermint RPC interface for this chain + -o, --output string Output format (text|json) +``` + +### Options inherited from parent commands + +``` + --chain-id string The network chain ID + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored query authority](zetacored_query_authority.md) - Querying commands for the authority module + diff --git a/docs/cli/zetacored/zetacored_tx.md b/docs/cli/zetacored/zetacored_tx.md index 2d8b13430e..d0c41e4937 100644 --- a/docs/cli/zetacored/zetacored_tx.md +++ b/docs/cli/zetacored/zetacored_tx.md @@ -25,6 +25,7 @@ zetacored tx [flags] ### SEE ALSO * [zetacored](zetacored.md) - Zetacore Daemon (server) +* [zetacored tx authority](zetacored_tx_authority.md) - authority transactions subcommands * [zetacored tx authz](zetacored_tx_authz.md) - Authorization transactions subcommands * [zetacored tx bank](zetacored_tx_bank.md) - Bank transaction subcommands * [zetacored tx broadcast](zetacored_tx_broadcast.md) - Broadcast transactions generated offline diff --git a/docs/cli/zetacored/zetacored_tx_authority.md b/docs/cli/zetacored/zetacored_tx_authority.md new file mode 100644 index 0000000000..eede40d5b5 --- /dev/null +++ b/docs/cli/zetacored/zetacored_tx_authority.md @@ -0,0 +1,29 @@ +# tx authority + +authority transactions subcommands + +``` +zetacored tx authority [flags] +``` + +### Options + +``` + -h, --help help for authority +``` + +### Options inherited from parent commands + +``` + --chain-id string The network chain ID + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored tx](zetacored_tx.md) - Transactions subcommands +* [zetacored tx authority update-policies](zetacored_tx_authority_update-policies.md) - Update the policies + diff --git a/docs/cli/zetacored/zetacored_tx_authority_update-policies.md b/docs/cli/zetacored/zetacored_tx_authority_update-policies.md new file mode 100644 index 0000000000..7681ef0fe8 --- /dev/null +++ b/docs/cli/zetacored/zetacored_tx_authority_update-policies.md @@ -0,0 +1,52 @@ +# tx authority update-policies + +Update the policies + +``` +zetacored tx authority update-policies [policies-json-file] [flags] +``` + +### Options + +``` + -a, --account-number uint The account number of the signing account (offline mode only) + --aux Generate aux signer data instead of sending a tx + -b, --broadcast-mode string Transaction broadcasting mode (sync|async|block) + --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) + --fee-granter string Fee granter grants fees for the transaction + --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer + --fees string Fees to pay along with transaction; eg: 10uatom + --from string Name or address of private key with which to sign + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) + --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) + --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) + -h, --help help for update-policies + --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) + --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used + --ledger Use a connected Ledger device + --node string [host]:[port] to tendermint rpc interface for this chain + --note string Note to add a description to the transaction (previously --memo) + --offline Offline mode (does not allow any online functionality) + -o, --output string Output format (text|json) + -s, --sequence uint The sequence number of the signing account (offline mode only) + --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature + --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height + --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator + -y, --yes Skip tx broadcasting prompt confirmation +``` + +### Options inherited from parent commands + +``` + --chain-id string The network chain ID + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored tx authority](zetacored_tx_authority.md) - authority transactions subcommands + diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index a2108f73b1..3ee890602d 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -26462,6 +26462,21 @@ paths: type: boolean tags: - Query + /zeta-chain/authority/policies: + get: + summary: Queries Policies + operationId: Query_Policies + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/authorityQueryGetPoliciesResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + tags: + - Query /zeta-chain/crosschain/cctx: get: summary: Queries a list of send items. @@ -53462,6 +53477,38 @@ definitions: format: int64 balance: type: string + authorityMsgUpdatePoliciesResponse: + type: object + description: MsgUpdatePoliciesResponse defines the MsgUpdatePoliciesResponse service. + authorityPolicies: + type: object + properties: + items: + type: array + items: + type: object + $ref: '#/definitions/authorityPolicy' + title: Policy contains info about authority policies + authorityPolicy: + type: object + properties: + policy_type: + $ref: '#/definitions/authorityPolicyType' + address: + type: string + authorityPolicyType: + type: string + enum: + - groupEmergency + - groupAdmin + default: groupEmergency + title: PolicyType defines the type of policy + authorityQueryGetPoliciesResponse: + type: object + properties: + policies: + $ref: '#/definitions/authorityPolicies' + description: QueryGetPoliciesResponse is the response type for the Query/Policies RPC method. bitcoinProof: type: object properties: @@ -54136,6 +54183,7 @@ definitions: $ref: '#/definitions/observerPolicy_Type' address: type: string + title: Deprecated(v14):Moved into the authority module observerBallotStatus: type: string enum: @@ -54374,7 +54422,7 @@ definitions: type: string is_supported: type: boolean - title: 'Deprecated: Use ChainParamsList' + title: 'Deprecated(v13): Use ChainParamsList' observerObserverUpdateReason: type: string enum: @@ -54403,6 +54451,7 @@ definitions: - group1 - group2 default: group1 + title: Deprecated(v14):Moved into the authority module observerQueryAllBlameRecordsResponse: type: object properties: @@ -54768,12 +54817,13 @@ definitions: items: type: object $ref: '#/definitions/observerObserverParams' - title: 'Deprecated: Use ChainParamsList' + title: 'Deprecated(v13): Use ChainParamsList' admin_policy: type: array items: type: object $ref: '#/definitions/observerAdmin_Policy' + title: Deprecated(v14):Moved into the authority module ballot_maturity_blocks: type: string format: int64 diff --git a/docs/spec/authority/messages.md b/docs/spec/authority/messages.md new file mode 100644 index 0000000000..788e95c6f6 --- /dev/null +++ b/docs/spec/authority/messages.md @@ -0,0 +1,13 @@ +# Messages + +## MsgUpdatePolicies + +UpdatePolicies updates policies + +```proto +message MsgUpdatePolicies { + string signer = 1; + Policies policies = 2; +} +``` + diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index f69a931598..d18adeb778 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -215,7 +215,7 @@ message MsgWhitelistERC20 { ## MsgUpdateTssAddress -Authorized: admin policy group 2. +UpdateTssAddress updates the TSS address. ```proto message MsgUpdateTssAddress { @@ -226,7 +226,7 @@ message MsgUpdateTssAddress { ## MsgMigrateTssFunds -Authorized: admin policy group 2. +MigrateTssFunds migrates the funds from the current TSS to the new TSS ```proto message MsgMigrateTssFunds { diff --git a/docs/spec/fungible/messages.md b/docs/spec/fungible/messages.md index 8b92ef5bad..696cc70631 100644 --- a/docs/spec/fungible/messages.md +++ b/docs/spec/fungible/messages.md @@ -91,7 +91,7 @@ message MsgUpdateContractBytecode { ## MsgUpdateZRC20WithdrawFee -Authorized: admin policy group 2. +UpdateZRC20WithdrawFee updates the withdraw fee and gas limit of a zrc20 token ```proto message MsgUpdateZRC20WithdrawFee { diff --git a/docs/spec/observer/messages.md b/docs/spec/observer/messages.md index 63e75cbe7c..ebf63c3781 100644 --- a/docs/spec/observer/messages.md +++ b/docs/spec/observer/messages.md @@ -2,7 +2,7 @@ ## MsgAddObserver -Authorized: admin policy group 2. +AddObserver adds an observer address to the observer set ```proto message MsgAddObserver { diff --git a/proto/authority/genesis.proto b/proto/authority/genesis.proto new file mode 100644 index 0000000000..f4032008c9 --- /dev/null +++ b/proto/authority/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package zetachain.zetacore.authority; + +import "authority/policies.proto"; +import "gogoproto/gogo.proto"; + +option go_package = "github.com/zeta-chain/zetacore/x/authority/types"; + +// GenesisState defines the authority module's genesis state. +message GenesisState { + Policies policies = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/authority/policies.proto b/proto/authority/policies.proto new file mode 100644 index 0000000000..85916c165a --- /dev/null +++ b/proto/authority/policies.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; +package zetachain.zetacore.authority; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/zeta-chain/zetacore/x/authority/types"; + +// PolicyType defines the type of policy +enum PolicyType { + option (gogoproto.goproto_enum_stringer) = true; + groupEmergency = 0; + groupAdmin = 1; +} + +message Policy { + PolicyType policy_type = 1; + string address = 2; +} + +// Policy contains info about authority policies +message Policies { + repeated Policy items = 1; +} diff --git a/proto/authority/query.proto b/proto/authority/query.proto new file mode 100644 index 0000000000..0aaee7a40c --- /dev/null +++ b/proto/authority/query.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package zetachain.zetacore.authority; + +import "authority/policies.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/zeta-chain/zetacore/x/authority/types"; + +// Query defines the gRPC querier service. +service Query { + // Queries Policies + rpc Policies(QueryGetPoliciesRequest) returns (QueryGetPoliciesResponse) { + option (google.api.http).get = "/zeta-chain/authority/policies"; + } +} + +// QueryGetPoliciesRequest is the request type for the Query/Policies RPC method. +message QueryGetPoliciesRequest {} + +// QueryGetPoliciesResponse is the response type for the Query/Policies RPC method. +message QueryGetPoliciesResponse { + Policies policies = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/authority/tx.proto b/proto/authority/tx.proto new file mode 100644 index 0000000000..4caa8d3808 --- /dev/null +++ b/proto/authority/tx.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package zetachain.zetacore.authority; + +import "authority/policies.proto"; +import "gogoproto/gogo.proto"; + +option go_package = "github.com/zeta-chain/zetacore/x/authority/types"; + +// Msg defines the Msg service. +service Msg { + rpc UpdatePolicies(MsgUpdatePolicies) returns (MsgUpdatePoliciesResponse); +} + +// MsgUpdatePolicies defines the MsgUpdatePolicies service. +message MsgUpdatePolicies { + string signer = 1; + Policies policies = 2 [(gogoproto.nullable) = false]; +} + +// MsgUpdatePoliciesResponse defines the MsgUpdatePoliciesResponse service. +message MsgUpdatePoliciesResponse {} diff --git a/proto/observer/params.proto b/proto/observer/params.proto index 7ad2d5ebf2..271710ca33 100644 --- a/proto/observer/params.proto +++ b/proto/observer/params.proto @@ -10,6 +10,7 @@ option go_package = "github.com/zeta-chain/zetacore/x/observer/types"; message ChainParamsList { repeated ChainParams chain_params = 1; } + message ChainParams { int64 chain_id = 11; uint64 confirmation_count = 1; @@ -33,7 +34,7 @@ message ChainParams { bool is_supported = 16; } -// Deprecated: Use ChainParamsList +// Deprecated(v13): Use ChainParamsList message ObserverParams { common.Chain chain = 1; string ballot_threshold = 3 [ @@ -47,12 +48,14 @@ message ObserverParams { bool is_supported = 5; } +// Deprecated(v14):Moved into the authority module enum Policy_Type { option (gogoproto.goproto_enum_stringer) = true; group1 = 0; group2 = 1; } +// Deprecated(v14):Moved into the authority module message Admin_Policy { Policy_Type policy_type = 1; string address = 2; @@ -62,9 +65,11 @@ message Admin_Policy { message Params { option (gogoproto.goproto_stringer) = false; - // Deprecated: Use ChainParamsList + // Deprecated(v13): Use ChainParamsList repeated ObserverParams observer_params = 1; + // Deprecated(v14):Moved into the authority module repeated Admin_Policy admin_policy = 2; + int64 ballot_maturity_blocks = 3; } diff --git a/testutil/keeper/authority.go b/testutil/keeper/authority.go new file mode 100644 index 0000000000..2c599e66a3 --- /dev/null +++ b/testutil/keeper/authority.go @@ -0,0 +1,79 @@ +package keeper + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + tmdb "github.com/tendermint/tm-db" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +var ( + AuthorityGovAddress = sample.Bech32AccAddress() +) + +func initAuthorityKeeper( + cdc codec.Codec, + db *tmdb.MemDB, + ss store.CommitMultiStore, +) keeper.Keeper { + storeKey := sdk.NewKVStoreKey(types.StoreKey) + memKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + ss.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + ss.MountStoreWithDB(memKey, storetypes.StoreTypeMemory, db) + + return keeper.NewKeeper( + cdc, + storeKey, + memKey, + AuthorityGovAddress, + ) +} + +// AuthorityKeeper instantiates an authority keeper for testing purposes +func AuthorityKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { + storeKey := sdk.NewKVStoreKey(types.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + + // Initialize local store + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + cdc := NewCodec() + + // Create regular keepers + sdkKeepers := NewSDKKeepers(cdc, db, stateStore) + + // Create the observer keeper + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) + require.NoError(t, stateStore.LoadLatestVersion()) + + ctx := NewContext(stateStore) + + // Initialize modules genesis + sdkKeepers.InitGenesis(ctx) + + // Add a proposer to the context + ctx = sdkKeepers.InitBlockProposer(t, ctx) + + k := keeper.NewKeeper( + cdc, + storeKey, + memStoreKey, + AuthorityGovAddress, + ) + + return &k, ctx +} + +// MockIsAuthorized mocks the IsAuthorized method of an authority keeper mock +func MockIsAuthorized(m *mock.Mock, address string, policyType types.PolicyType, isAuthorized bool) { + m.On("IsAuthorized", mock.Anything, address, policyType).Return(isAuthorized).Once() +} diff --git a/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index 45a68a5627..715383ee92 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -14,20 +14,22 @@ import ( ) type CrosschainMockOptions struct { - UseBankMock bool - UseAccountMock bool - UseStakingMock bool - UseObserverMock bool - UseFungibleMock bool + UseBankMock bool + UseAccountMock bool + UseStakingMock bool + UseObserverMock bool + UseFungibleMock bool + UseAuthorityMock bool } var ( CrosschainMocksAll = CrosschainMockOptions{ - UseBankMock: true, - UseAccountMock: true, - UseStakingMock: true, - UseObserverMock: true, - UseFungibleMock: true, + UseBankMock: true, + UseAccountMock: true, + UseStakingMock: true, + UseObserverMock: true, + UseFungibleMock: true, + UseAuthorityMock: true, } CrosschainNoMocks = CrosschainMockOptions{} ) @@ -50,6 +52,7 @@ func CrosschainKeeperWithMocks( sdkKeepers := NewSDKKeepers(cdc, db, stateStore) // Create zeta keepers + authorityKeeperTmp := initAuthorityKeeper(cdc, db, stateStore) observerKeeperTmp := initObserverKeeper( cdc, db, @@ -57,8 +60,9 @@ func CrosschainKeeperWithMocks( sdkKeepers.StakingKeeper, sdkKeepers.SlashingKeeper, sdkKeepers.ParamsKeeper, + authorityKeeperTmp, ) - fungiblekeeperTmp := initFungibleKeeper( + fungibleKeeperTmp := initFungibleKeeper( cdc, db, stateStore, @@ -67,13 +71,16 @@ func CrosschainKeeperWithMocks( sdkKeepers.BankKeeper, sdkKeepers.EvmKeeper, observerKeeperTmp, + authorityKeeperTmp, ) zetaKeepers := ZetaKeepers{ - ObserverKeeper: observerKeeperTmp, - FungibleKeeper: fungiblekeeperTmp, + ObserverKeeper: observerKeeperTmp, + FungibleKeeper: fungibleKeeperTmp, + AuthorityKeeper: &authorityKeeperTmp, } + var authorityKeeper types.AuthorityKeeper = authorityKeeperTmp var observerKeeper types.ObserverKeeper = observerKeeperTmp - var fungibleKeeper types.FungibleKeeper = fungiblekeeperTmp + var fungibleKeeper types.FungibleKeeper = fungibleKeeperTmp // Create the fungible keeper stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) @@ -102,6 +109,10 @@ func CrosschainKeeperWithMocks( if mockOptions.UseStakingMock { stakingKeeper = crosschainmocks.NewCrosschainStakingKeeper(t) } + + if mockOptions.UseAuthorityMock { + authorityKeeper = crosschainmocks.NewCrosschainAuthorityKeeper(t) + } if mockOptions.UseObserverMock { observerKeeper = crosschainmocks.NewCrosschainObserverKeeper(t) } @@ -119,6 +130,7 @@ func CrosschainKeeperWithMocks( bankKeeper, observerKeeper, fungibleKeeper, + authorityKeeper, ) return k, ctx, sdkKeepers, zetaKeepers @@ -135,6 +147,13 @@ func CrosschainKeeper(t testing.TB) (*keeper.Keeper, sdk.Context, SDKKeepers, Ze return CrosschainKeeperWithMocks(t, CrosschainNoMocks) } +// GetCrosschainAuthorityMock returns a new crosschain authority keeper mock +func GetCrosschainAuthorityMock(t testing.TB, keeper *keeper.Keeper) *crosschainmocks.CrosschainAuthorityKeeper { + cok, ok := keeper.GetAuthorityKeeper().(*crosschainmocks.CrosschainAuthorityKeeper) + require.True(t, ok) + return cok +} + func GetCrosschainAccountMock(t testing.TB, keeper *keeper.Keeper) *crosschainmocks.CrosschainAccountKeeper { cak, ok := keeper.GetAuthKeeper().(*crosschainmocks.CrosschainAccountKeeper) require.True(t, ok) diff --git a/testutil/keeper/emissions.go b/testutil/keeper/emissions.go index 988859c762..31479d77b7 100644 --- a/testutil/keeper/emissions.go +++ b/testutil/keeper/emissions.go @@ -52,6 +52,7 @@ func EmissionKeeperWithMockOptions( sdkKeepers.StakingKeeper, sdkKeepers.SlashingKeeper, sdkKeepers.ParamsKeeper, + initAuthorityKeeper(cdc, db, stateStore), ) zetaKeepers := ZetaKeepers{ diff --git a/testutil/keeper/fungible.go b/testutil/keeper/fungible.go index 5f9f2b0cfd..5097a208d2 100644 --- a/testutil/keeper/fungible.go +++ b/testutil/keeper/fungible.go @@ -22,18 +22,20 @@ import ( ) type FungibleMockOptions struct { - UseBankMock bool - UseAccountMock bool - UseObserverMock bool - UseEVMMock bool + UseBankMock bool + UseAccountMock bool + UseObserverMock bool + UseEVMMock bool + UseAuthorityMock bool } var ( FungibleMocksAll = FungibleMockOptions{ - UseBankMock: true, - UseAccountMock: true, - UseObserverMock: true, - UseEVMMock: true, + UseBankMock: true, + UseAccountMock: true, + UseObserverMock: true, + UseEVMMock: true, + UseAuthorityMock: true, } FungibleNoMocks = FungibleMockOptions{} ) @@ -47,6 +49,7 @@ func initFungibleKeeper( bankKeepr types.BankKeeper, evmKeeper types.EVMKeeper, observerKeeper types.ObserverKeeper, + authorityKeeper types.AuthorityKeeper, ) *keeper.Keeper { storeKey := sdk.NewKVStoreKey(types.StoreKey) memKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) @@ -62,6 +65,7 @@ func initFungibleKeeper( evmKeeper, bankKeepr, observerKeeper, + authorityKeeper, ) } @@ -78,6 +82,13 @@ func FungibleKeeperWithMocks(t testing.TB, mockOptions FungibleMockOptions) (*ke // Create regular keepers sdkKeepers := NewSDKKeepers(cdc, db, stateStore) + // Create authority keeper + authorityKeeperTmp := initAuthorityKeeper( + cdc, + db, + stateStore, + ) + // Create observer keeper observerKeeperTmp := initObserverKeeper( cdc, @@ -86,11 +97,14 @@ func FungibleKeeperWithMocks(t testing.TB, mockOptions FungibleMockOptions) (*ke sdkKeepers.StakingKeeper, sdkKeepers.SlashingKeeper, sdkKeepers.ParamsKeeper, + authorityKeeperTmp, ) zetaKeepers := ZetaKeepers{ - ObserverKeeper: observerKeeperTmp, + ObserverKeeper: observerKeeperTmp, + AuthorityKeeper: &authorityKeeperTmp, } var observerKeeper types.ObserverKeeper = observerKeeperTmp + var authorityKeeper types.AuthorityKeeper = authorityKeeperTmp // Create the fungible keeper stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) @@ -110,6 +124,7 @@ func FungibleKeeperWithMocks(t testing.TB, mockOptions FungibleMockOptions) (*ke var authKeeper types.AccountKeeper = sdkKeepers.AuthKeeper var bankKeeper types.BankKeeper = sdkKeepers.BankKeeper var evmKeeper types.EVMKeeper = sdkKeepers.EvmKeeper + if mockOptions.UseAccountMock { authKeeper = fungiblemocks.NewFungibleAccountKeeper(t) } @@ -122,6 +137,9 @@ func FungibleKeeperWithMocks(t testing.TB, mockOptions FungibleMockOptions) (*ke if mockOptions.UseEVMMock { evmKeeper = fungiblemocks.NewFungibleEVMKeeper(t) } + if mockOptions.UseAuthorityMock { + authorityKeeper = fungiblemocks.NewFungibleAuthorityKeeper(t) + } k := keeper.NewKeeper( cdc, @@ -132,6 +150,7 @@ func FungibleKeeperWithMocks(t testing.TB, mockOptions FungibleMockOptions) (*ke evmKeeper, bankKeeper, observerKeeper, + authorityKeeper, ) fungiblemodule.InitGenesis(ctx, *k, *types.DefaultGenesis()) @@ -151,6 +170,13 @@ func FungibleKeeper(t testing.TB) (*keeper.Keeper, sdk.Context, SDKKeepers, Zeta return k, ctx, sdkk, zk } +// GetFungibleAuthorityMock returns a new fungible authority keeper mock +func GetFungibleAuthorityMock(t testing.TB, keeper *keeper.Keeper) *fungiblemocks.FungibleAuthorityKeeper { + cok, ok := keeper.GetAuthorityKeeper().(*fungiblemocks.FungibleAuthorityKeeper) + require.True(t, ok) + return cok +} + func GetFungibleAccountMock(t testing.TB, keeper *keeper.Keeper) *fungiblemocks.FungibleAccountKeeper { fak, ok := keeper.GetAuthKeeper().(*fungiblemocks.FungibleAccountKeeper) require.True(t, ok) diff --git a/testutil/keeper/keeper.go b/testutil/keeper/keeper.go index 818c70f69b..3e76a951b5 100644 --- a/testutil/keeper/keeper.go +++ b/testutil/keeper/keeper.go @@ -5,9 +5,6 @@ import ( "testing" "time" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -21,6 +18,8 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" @@ -38,6 +37,9 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmdb "github.com/tendermint/tm-db" "github.com/zeta-chain/zetacore/testutil/sample" + authoritymodule "github.com/zeta-chain/zetacore/x/authority" + authoritykeeper "github.com/zeta-chain/zetacore/x/authority/keeper" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" crosschainmodule "github.com/zeta-chain/zetacore/x/crosschain" crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -91,6 +93,7 @@ type SDKKeepers struct { // ZetaKeepers is a struct containing Zeta module keepers for test purposes type ZetaKeepers struct { + AuthorityKeeper *authoritykeeper.Keeper CrosschainKeeper *crosschainkeeper.Keeper EmissionsKeeper *emissionskeeper.Keeper FungibleKeeper *fungiblekeeper.Keeper @@ -371,6 +374,9 @@ func (sdkk SDKKeepers) InitBlockProposer(t testing.TB, ctx sdk.Context) sdk.Cont // InitGenesis initializes the test modules genesis state for defined Zeta modules func (zk ZetaKeepers) InitGenesis(ctx sdk.Context) { + if zk.AuthorityKeeper != nil { + authoritymodule.InitGenesis(ctx, *zk.AuthorityKeeper, *authoritytypes.DefaultGenesis()) + } if zk.CrosschainKeeper != nil { crosschainmodule.InitGenesis(ctx, *zk.CrosschainKeeper, *crosschaintypes.DefaultGenesis()) } diff --git a/testutil/keeper/mocks/crosschain/authority.go b/testutil/keeper/mocks/crosschain/authority.go new file mode 100644 index 0000000000..9f08c9d673 --- /dev/null +++ b/testutil/keeper/mocks/crosschain/authority.go @@ -0,0 +1,47 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// CrosschainAuthorityKeeper is an autogenerated mock type for the CrosschainAuthorityKeeper type +type CrosschainAuthorityKeeper struct { + mock.Mock +} + +// IsAuthorized provides a mock function with given fields: ctx, address, policyType +func (_m *CrosschainAuthorityKeeper) IsAuthorized(ctx types.Context, address string, policyType authoritytypes.PolicyType) bool { + ret := _m.Called(ctx, address, policyType) + + if len(ret) == 0 { + panic("no return value specified for IsAuthorized") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(types.Context, string, authoritytypes.PolicyType) bool); ok { + r0 = rf(ctx, address, policyType) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// NewCrosschainAuthorityKeeper creates a new instance of CrosschainAuthorityKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCrosschainAuthorityKeeper(t interface { + mock.TestingT + Cleanup(func()) +}) *CrosschainAuthorityKeeper { + mock := &CrosschainAuthorityKeeper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/testutil/keeper/mocks/fungible/authority.go b/testutil/keeper/mocks/fungible/authority.go new file mode 100644 index 0000000000..929a99021c --- /dev/null +++ b/testutil/keeper/mocks/fungible/authority.go @@ -0,0 +1,47 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// FungibleAuthorityKeeper is an autogenerated mock type for the FungibleAuthorityKeeper type +type FungibleAuthorityKeeper struct { + mock.Mock +} + +// IsAuthorized provides a mock function with given fields: ctx, address, policyType +func (_m *FungibleAuthorityKeeper) IsAuthorized(ctx types.Context, address string, policyType authoritytypes.PolicyType) bool { + ret := _m.Called(ctx, address, policyType) + + if len(ret) == 0 { + panic("no return value specified for IsAuthorized") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(types.Context, string, authoritytypes.PolicyType) bool); ok { + r0 = rf(ctx, address, policyType) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// NewFungibleAuthorityKeeper creates a new instance of FungibleAuthorityKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFungibleAuthorityKeeper(t interface { + mock.TestingT + Cleanup(func()) +}) *FungibleAuthorityKeeper { + mock := &FungibleAuthorityKeeper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/testutil/keeper/mocks/mocks.go b/testutil/keeper/mocks/mocks.go index 1156061b90..0da231a7e5 100644 --- a/testutil/keeper/mocks/mocks.go +++ b/testutil/keeper/mocks/mocks.go @@ -36,6 +36,11 @@ type CrosschainFungibleKeeper interface { crosschaintypes.FungibleKeeper } +//go:generate mockery --name CrosschainAuthorityKeeper --filename authority.go --case underscore --output ./crosschain +type CrosschainAuthorityKeeper interface { + crosschaintypes.AuthorityKeeper +} + /** * Fungible Mocks */ @@ -60,6 +65,11 @@ type FungibleEVMKeeper interface { fungibletypes.EVMKeeper } +//go:generate mockery --name FungibleAuthorityKeeper --filename authority.go --case underscore --output ./fungible +type FungibleAuthorityKeeper interface { + fungibletypes.AuthorityKeeper +} + /** * Emissions Mocks */ @@ -97,3 +107,8 @@ type ObserverStakingKeeper interface { type ObserverSlashingKeeper interface { observertypes.SlashingKeeper } + +//go:generate mockery --name ObserverAuthorityKeeper --filename authority.go --case underscore --output ./observer +type ObserverAuthorityKeeper interface { + observertypes.AuthorityKeeper +} diff --git a/testutil/keeper/mocks/observer/authority.go b/testutil/keeper/mocks/observer/authority.go new file mode 100644 index 0000000000..76e5e0566c --- /dev/null +++ b/testutil/keeper/mocks/observer/authority.go @@ -0,0 +1,52 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// ObserverAuthorityKeeper is an autogenerated mock type for the ObserverAuthorityKeeper type +type ObserverAuthorityKeeper struct { + mock.Mock +} + +// IsAuthorized provides a mock function with given fields: ctx, address, policyType +func (_m *ObserverAuthorityKeeper) IsAuthorized(ctx types.Context, address string, policyType authoritytypes.PolicyType) bool { + ret := _m.Called(ctx, address, policyType) + + if len(ret) == 0 { + panic("no return value specified for IsAuthorized") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(types.Context, string, authoritytypes.PolicyType) bool); ok { + r0 = rf(ctx, address, policyType) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// SetPolicies provides a mock function with given fields: ctx, policies +func (_m *ObserverAuthorityKeeper) SetPolicies(ctx types.Context, policies authoritytypes.Policies) { + _m.Called(ctx, policies) +} + +// NewObserverAuthorityKeeper creates a new instance of ObserverAuthorityKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewObserverAuthorityKeeper(t interface { + mock.TestingT + Cleanup(func()) +}) *ObserverAuthorityKeeper { + mock := &ObserverAuthorityKeeper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/testutil/keeper/observer.go b/testutil/keeper/observer.go index 1a6dd2e257..c747df0218 100644 --- a/testutil/keeper/observer.go +++ b/testutil/keeper/observer.go @@ -21,14 +21,16 @@ import ( // ObserverMockOptions represents options for instantiating an observer keeper with mocks type ObserverMockOptions struct { - UseStakingMock bool - UseSlashingMock bool + UseStakingMock bool + UseSlashingMock bool + UseAuthorityMock bool } var ( ObserverMocksAll = ObserverMockOptions{ - UseStakingMock: true, - UseSlashingMock: true, + UseStakingMock: true, + UseSlashingMock: true, + UseAuthorityMock: true, } ObserverNoMocks = ObserverMockOptions{} ) @@ -40,6 +42,7 @@ func initObserverKeeper( stakingKeeper stakingkeeper.Keeper, slashingKeeper slashingkeeper.Keeper, paramKeeper paramskeeper.Keeper, + authorityKeeper types.AuthorityKeeper, ) *keeper.Keeper { storeKey := sdk.NewKVStoreKey(types.StoreKey) memKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) @@ -53,11 +56,12 @@ func initObserverKeeper( paramKeeper.Subspace(types.ModuleName), stakingKeeper, slashingKeeper, + authorityKeeper, ) } // ObserverKeeperWithMocks instantiates an observer keeper for testing purposes with the option to mock specific keepers -func ObserverKeeperWithMocks(t testing.TB, mockOptions ObserverMockOptions) (*keeper.Keeper, sdk.Context, SDKKeepers) { +func ObserverKeeperWithMocks(t testing.TB, mockOptions ObserverMockOptions) (*keeper.Keeper, sdk.Context, SDKKeepers, ZetaKeepers) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) @@ -66,6 +70,8 @@ func ObserverKeeperWithMocks(t testing.TB, mockOptions ObserverMockOptions) (*ke stateStore := store.NewCommitMultiStore(db) cdc := NewCodec() + authorityKeeperTmp := initAuthorityKeeper(cdc, db, stateStore) + // Create regular keepers sdkKeepers := NewSDKKeepers(cdc, db, stateStore) @@ -85,12 +91,16 @@ func ObserverKeeperWithMocks(t testing.TB, mockOptions ObserverMockOptions) (*ke // Initialize mocks for mocked keepers var stakingKeeper types.StakingKeeper = sdkKeepers.StakingKeeper var slashingKeeper types.SlashingKeeper = sdkKeepers.SlashingKeeper + var authorityKeeper types.AuthorityKeeper = authorityKeeperTmp if mockOptions.UseStakingMock { stakingKeeper = observermocks.NewObserverStakingKeeper(t) } if mockOptions.UseSlashingMock { slashingKeeper = observermocks.NewObserverSlashingKeeper(t) } + if mockOptions.UseAuthorityMock { + authorityKeeper = observermocks.NewObserverAuthorityKeeper(t) + } k := keeper.NewKeeper( cdc, @@ -99,18 +109,28 @@ func ObserverKeeperWithMocks(t testing.TB, mockOptions ObserverMockOptions) (*ke sdkKeepers.ParamsKeeper.Subspace(types.ModuleName), stakingKeeper, slashingKeeper, + authorityKeeper, ) k.SetParams(ctx, types.DefaultParams()) - return k, ctx, sdkKeepers + return k, ctx, sdkKeepers, ZetaKeepers{ + AuthorityKeeper: &authorityKeeperTmp, + } } // ObserverKeeper instantiates an observer keeper for testing purposes -func ObserverKeeper(t testing.TB) (*keeper.Keeper, sdk.Context, SDKKeepers) { +func ObserverKeeper(t testing.TB) (*keeper.Keeper, sdk.Context, SDKKeepers, ZetaKeepers) { return ObserverKeeperWithMocks(t, ObserverNoMocks) } +// GetObserverAuthorityMock returns a new observer authority keeper mock +func GetObserverAuthorityMock(t testing.TB, keeper *keeper.Keeper) *observermocks.ObserverAuthorityKeeper { + cok, ok := keeper.GetAuthorityKeeper().(*observermocks.ObserverAuthorityKeeper) + require.True(t, ok) + return cok +} + // GetObserverStakingMock returns a new observer staking keeper mock func GetObserverStakingMock(t testing.TB, keeper *keeper.Keeper) *ObserverMockStakingKeeper { k, ok := keeper.GetStakingKeeper().(*observermocks.ObserverStakingKeeper) @@ -120,15 +140,6 @@ func GetObserverStakingMock(t testing.TB, keeper *keeper.Keeper) *ObserverMockSt } } -// GetObserverSlashingMock returns a new observer slashing keeper mock -func GetObserverSlashingMock(t testing.TB, keeper *keeper.Keeper) *ObserverMockSlashingKeeper { - k, ok := keeper.GetSlashingKeeper().(*observermocks.ObserverSlashingKeeper) - require.True(t, ok) - return &ObserverMockSlashingKeeper{ - ObserverSlashingKeeper: k, - } -} - // ObserverMockStakingKeeper is a wrapper of the observer staking keeper mock that add methods to mock the GetValidator method type ObserverMockStakingKeeper struct { *observermocks.ObserverStakingKeeper @@ -138,6 +149,15 @@ func (m *ObserverMockStakingKeeper) MockGetValidator(validator stakingtypes.Vali m.On("GetValidator", mock.Anything, mock.Anything).Return(validator, true) } +// GetObserverSlashingMock returns a new observer slashing keeper mock +func GetObserverSlashingMock(t testing.TB, keeper *keeper.Keeper) *ObserverMockSlashingKeeper { + k, ok := keeper.GetSlashingKeeper().(*observermocks.ObserverSlashingKeeper) + require.True(t, ok) + return &ObserverMockSlashingKeeper{ + ObserverSlashingKeeper: k, + } +} + // ObserverMockSlashingKeeper is a wrapper of the observer slashing keeper mock that add methods to mock the IsTombstoned method type ObserverMockSlashingKeeper struct { *observermocks.ObserverSlashingKeeper diff --git a/testutil/sample/authority.go b/testutil/sample/authority.go new file mode 100644 index 0000000000..1e65b735e7 --- /dev/null +++ b/testutil/sample/authority.go @@ -0,0 +1,18 @@ +package sample + +import authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + +func Policies() authoritytypes.Policies { + return authoritytypes.Policies{ + Items: []*authoritytypes.Policy{ + { + Address: AccAddress(), + PolicyType: authoritytypes.PolicyType_groupEmergency, + }, + { + Address: AccAddress(), + PolicyType: authoritytypes.PolicyType_groupAdmin, + }, + }, + } +} diff --git a/typescript/authority/genesis_pb.d.ts b/typescript/authority/genesis_pb.d.ts new file mode 100644 index 0000000000..6265e54b8d --- /dev/null +++ b/typescript/authority/genesis_pb.d.ts @@ -0,0 +1,35 @@ +// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" +// @generated from file authority/genesis.proto (package zetachain.zetacore.authority, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import type { Policies } from "./policies_pb.js"; + +/** + * GenesisState defines the authority module's genesis state. + * + * @generated from message zetachain.zetacore.authority.GenesisState + */ +export declare class GenesisState extends Message { + /** + * @generated from field: zetachain.zetacore.authority.Policies policies = 1; + */ + policies?: Policies; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.GenesisState"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): GenesisState; + + static fromJson(jsonValue: JsonValue, options?: Partial): GenesisState; + + static fromJsonString(jsonString: string, options?: Partial): GenesisState; + + static equals(a: GenesisState | PlainMessage | undefined, b: GenesisState | PlainMessage | undefined): boolean; +} + diff --git a/typescript/authority/index.d.ts b/typescript/authority/index.d.ts new file mode 100644 index 0000000000..d363dc44f4 --- /dev/null +++ b/typescript/authority/index.d.ts @@ -0,0 +1,4 @@ +export * from "./genesis_pb"; +export * from "./policies_pb"; +export * from "./query_pb"; +export * from "./tx_pb"; diff --git a/typescript/authority/policies_pb.d.ts b/typescript/authority/policies_pb.d.ts new file mode 100644 index 0000000000..b6835f08a5 --- /dev/null +++ b/typescript/authority/policies_pb.d.ts @@ -0,0 +1,80 @@ +// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" +// @generated from file authority/policies.proto (package zetachain.zetacore.authority, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; + +/** + * PolicyType defines the type of policy + * + * @generated from enum zetachain.zetacore.authority.PolicyType + */ +export declare enum PolicyType { + /** + * @generated from enum value: groupEmergency = 0; + */ + groupEmergency = 0, + + /** + * @generated from enum value: groupAdmin = 1; + */ + groupAdmin = 1, +} + +/** + * @generated from message zetachain.zetacore.authority.Policy + */ +export declare class Policy extends Message { + /** + * @generated from field: zetachain.zetacore.authority.PolicyType policy_type = 1; + */ + policyType: PolicyType; + + /** + * @generated from field: string address = 2; + */ + address: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.Policy"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): Policy; + + static fromJson(jsonValue: JsonValue, options?: Partial): Policy; + + static fromJsonString(jsonString: string, options?: Partial): Policy; + + static equals(a: Policy | PlainMessage | undefined, b: Policy | PlainMessage | undefined): boolean; +} + +/** + * Policy contains info about authority policies + * + * @generated from message zetachain.zetacore.authority.Policies + */ +export declare class Policies extends Message { + /** + * @generated from field: repeated zetachain.zetacore.authority.Policy items = 1; + */ + items: Policy[]; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.Policies"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): Policies; + + static fromJson(jsonValue: JsonValue, options?: Partial): Policies; + + static fromJsonString(jsonString: string, options?: Partial): Policies; + + static equals(a: Policies | PlainMessage | undefined, b: Policies | PlainMessage | undefined): boolean; +} + diff --git a/typescript/authority/query_pb.d.ts b/typescript/authority/query_pb.d.ts new file mode 100644 index 0000000000..a53441fb68 --- /dev/null +++ b/typescript/authority/query_pb.d.ts @@ -0,0 +1,56 @@ +// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" +// @generated from file authority/query.proto (package zetachain.zetacore.authority, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import type { Policies } from "./policies_pb.js"; + +/** + * QueryGetPoliciesRequest is the request type for the Query/Policies RPC method. + * + * @generated from message zetachain.zetacore.authority.QueryGetPoliciesRequest + */ +export declare class QueryGetPoliciesRequest extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.QueryGetPoliciesRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryGetPoliciesRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryGetPoliciesRequest; + + static fromJsonString(jsonString: string, options?: Partial): QueryGetPoliciesRequest; + + static equals(a: QueryGetPoliciesRequest | PlainMessage | undefined, b: QueryGetPoliciesRequest | PlainMessage | undefined): boolean; +} + +/** + * QueryGetPoliciesResponse is the response type for the Query/Policies RPC method. + * + * @generated from message zetachain.zetacore.authority.QueryGetPoliciesResponse + */ +export declare class QueryGetPoliciesResponse extends Message { + /** + * @generated from field: zetachain.zetacore.authority.Policies policies = 1; + */ + policies?: Policies; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.QueryGetPoliciesResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryGetPoliciesResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryGetPoliciesResponse; + + static fromJsonString(jsonString: string, options?: Partial): QueryGetPoliciesResponse; + + static equals(a: QueryGetPoliciesResponse | PlainMessage | undefined, b: QueryGetPoliciesResponse | PlainMessage | undefined): boolean; +} + diff --git a/typescript/authority/tx_pb.d.ts b/typescript/authority/tx_pb.d.ts new file mode 100644 index 0000000000..47754ecd56 --- /dev/null +++ b/typescript/authority/tx_pb.d.ts @@ -0,0 +1,61 @@ +// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" +// @generated from file authority/tx.proto (package zetachain.zetacore.authority, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import type { Policies } from "./policies_pb.js"; + +/** + * MsgUpdatePolicies defines the MsgUpdatePolicies service. + * + * @generated from message zetachain.zetacore.authority.MsgUpdatePolicies + */ +export declare class MsgUpdatePolicies extends Message { + /** + * @generated from field: string signer = 1; + */ + signer: string; + + /** + * @generated from field: zetachain.zetacore.authority.Policies policies = 2; + */ + policies?: Policies; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.MsgUpdatePolicies"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgUpdatePolicies; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgUpdatePolicies; + + static fromJsonString(jsonString: string, options?: Partial): MsgUpdatePolicies; + + static equals(a: MsgUpdatePolicies | PlainMessage | undefined, b: MsgUpdatePolicies | PlainMessage | undefined): boolean; +} + +/** + * MsgUpdatePoliciesResponse defines the MsgUpdatePoliciesResponse service. + * + * @generated from message zetachain.zetacore.authority.MsgUpdatePoliciesResponse + */ +export declare class MsgUpdatePoliciesResponse extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.MsgUpdatePoliciesResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgUpdatePoliciesResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgUpdatePoliciesResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgUpdatePoliciesResponse; + + static equals(a: MsgUpdatePoliciesResponse | PlainMessage | undefined, b: MsgUpdatePoliciesResponse | PlainMessage | undefined): boolean; +} + diff --git a/typescript/observer/params_pb.d.ts b/typescript/observer/params_pb.d.ts index 9825349298..e06b0aebe5 100644 --- a/typescript/observer/params_pb.d.ts +++ b/typescript/observer/params_pb.d.ts @@ -8,6 +8,8 @@ import { Message, proto3 } from "@bufbuild/protobuf"; import type { Chain } from "../common/common_pb.js"; /** + * Deprecated(v14):Moved into the authority module + * * @generated from enum zetachain.zetacore.observer.Policy_Type */ export declare enum Policy_Type { @@ -136,7 +138,7 @@ export declare class ChainParams extends Message { } /** - * Deprecated: Use ChainParamsList + * Deprecated(v13): Use ChainParamsList * * @generated from message zetachain.zetacore.observer.ObserverParams */ @@ -177,6 +179,8 @@ export declare class ObserverParams extends Message { } /** + * Deprecated(v14):Moved into the authority module + * * @generated from message zetachain.zetacore.observer.Admin_Policy */ export declare class Admin_Policy extends Message { @@ -212,13 +216,15 @@ export declare class Admin_Policy extends Message { */ export declare class Params extends Message { /** - * Deprecated: Use ChainParamsList + * Deprecated(v13): Use ChainParamsList * * @generated from field: repeated zetachain.zetacore.observer.ObserverParams observer_params = 1; */ observerParams: ObserverParams[]; /** + * Deprecated(v14):Moved into the authority module + * * @generated from field: repeated zetachain.zetacore.observer.Admin_Policy admin_policy = 2; */ adminPolicy: Admin_Policy[]; diff --git a/x/authority/client/cli/query.go b/x/authority/client/cli/query.go new file mode 100644 index 0000000000..63ae30e227 --- /dev/null +++ b/x/authority/client/cli/query.go @@ -0,0 +1,27 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(_ string) *cobra.Command { + // Group authority queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + CmdShowPolicies(), + ) + + return cmd +} diff --git a/x/authority/client/cli/query_policies.go b/x/authority/client/cli/query_policies.go new file mode 100644 index 0000000000..66ad4e5644 --- /dev/null +++ b/x/authority/client/cli/query_policies.go @@ -0,0 +1,35 @@ +package cli + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// CmdShowPolicies returns the command to show the policies +func CmdShowPolicies() *cobra.Command { + cmd := &cobra.Command{ + Use: "show-policies", + Short: "show the policies", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.Policies(context.Background(), &types.QueryGetPoliciesRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/authority/client/cli/tx.go b/x/authority/client/cli/tx.go new file mode 100644 index 0000000000..ae3d7efa6b --- /dev/null +++ b/x/authority/client/cli/tx.go @@ -0,0 +1,26 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + CmdUpdatePolices(), + ) + + return cmd +} diff --git a/x/authority/client/cli/tx_update_policies.go b/x/authority/client/cli/tx_update_policies.go new file mode 100644 index 0000000000..0e0c822aa7 --- /dev/null +++ b/x/authority/client/cli/tx_update_policies.go @@ -0,0 +1,44 @@ +package cli + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func CmdUpdatePolices() *cobra.Command { + cmd := &cobra.Command{ + Use: "update-policies [policies-json-file]", + Short: "Update the policies", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + // Read the policies from the file using os package and unmarshal it into the policies variable + var policies types.Policies + policiesBytes, err := os.ReadFile(args[0]) + if err != nil { + return err + } + if err := policies.Unmarshal(policiesBytes); err != nil { + return err + } + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgUpdatePolicies( + clientCtx.GetFromAddress().String(), + policies, + ) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/authority/genesis.go b/x/authority/genesis.go new file mode 100644 index 0000000000..b82983fab9 --- /dev/null +++ b/x/authority/genesis.go @@ -0,0 +1,24 @@ +package authority + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/x/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// InitGenesis initializes the authority module's state from a provided genesis state +func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + k.SetPolicies(ctx, genState.Policies) +} + +// ExportGenesis returns the authority module's exported genesis. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + var genesis types.GenesisState + + policies, found := k.GetPolicies(ctx) + if found { + genesis.Policies = policies + } + + return &genesis +} diff --git a/x/authority/genesis_test.go b/x/authority/genesis_test.go new file mode 100644 index 0000000000..bb6e0f7353 --- /dev/null +++ b/x/authority/genesis_test.go @@ -0,0 +1,36 @@ +package authority_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/nullify" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestGenesis(t *testing.T) { + genesisState := types.GenesisState{ + Policies: sample.Policies(), + } + + // Init + k, ctx := keepertest.AuthorityKeeper(t) + authority.InitGenesis(ctx, *k, genesisState) + + // Check policy is set + policies, found := k.GetPolicies(ctx) + require.True(t, found) + require.Equal(t, genesisState.Policies, policies) + + // Export + got := authority.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + // Compare genesis after init and export + nullify.Fill(&genesisState) + nullify.Fill(got) + require.Equal(t, genesisState, *got) +} diff --git a/x/authority/keeper/grpc_query.go b/x/authority/keeper/grpc_query.go new file mode 100644 index 0000000000..1c9c7387e6 --- /dev/null +++ b/x/authority/keeper/grpc_query.go @@ -0,0 +1,7 @@ +package keeper + +import ( + "github.com/zeta-chain/zetacore/x/authority/types" +) + +var _ types.QueryServer = Keeper{} diff --git a/x/authority/keeper/grpc_query_policies.go b/x/authority/keeper/grpc_query_policies.go new file mode 100644 index 0000000000..e799ce97ec --- /dev/null +++ b/x/authority/keeper/grpc_query_policies.go @@ -0,0 +1,26 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/x/authority/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Policies queries policies +func (k Keeper) Policies(c context.Context, req *types.QueryGetPoliciesRequest) (*types.QueryGetPoliciesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(c) + + // fetch policies + policies, found := k.GetPolicies(ctx) + if !found { + return nil, status.Error(codes.NotFound, "policies not found") + } + + return &types.QueryGetPoliciesResponse{Policies: policies}, nil +} diff --git a/x/authority/keeper/grpc_query_policies_test.go b/x/authority/keeper/grpc_query_policies_test.go new file mode 100644 index 0000000000..40dc6a6124 --- /dev/null +++ b/x/authority/keeper/grpc_query_policies_test.go @@ -0,0 +1,37 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestKeeper_Policies(t *testing.T) { + t.Run("invalid request", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + + _, err := k.Policies(ctx, nil) + require.ErrorContains(t, err, "invalid request") + }) + + t.Run("policies not found", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + + _, err := k.Policies(ctx, &types.QueryGetPoliciesRequest{}) + require.ErrorContains(t, err, "policies not found") + }) + + t.Run("can retrieve policies", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + + policies := sample.Policies() + k.SetPolicies(ctx, policies) + + res, err := k.Policies(ctx, &types.QueryGetPoliciesRequest{}) + require.NoError(t, err) + require.Equal(t, policies, res.Policies) + }) +} diff --git a/x/authority/keeper/keeper.go b/x/authority/keeper/keeper.go new file mode 100644 index 0000000000..46e8cb9bc4 --- /dev/null +++ b/x/authority/keeper/keeper.go @@ -0,0 +1,55 @@ +package keeper + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/libs/log" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine +type Keeper struct { + cdc codec.Codec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey + // the address capable of executing a MsgUpdatePolicies message. Typically, this should be the x/gov module account. + govAddr sdk.AccAddress +} + +// NewKeeper creates new instances of the authority Keeper +func NewKeeper( + cdc codec.Codec, + storeKey, + memKey storetypes.StoreKey, + govAddr sdk.AccAddress, +) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + govAddr: govAddr, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// GetStoreKey returns the key to the store for authority +func (k Keeper) GetStoreKey() storetypes.StoreKey { + return k.storeKey +} + +// GetMemKey returns the mem key to the store for authority +func (k Keeper) GetMemKey() storetypes.StoreKey { + return k.memKey +} + +// GetCodec returns the codec for authority +func (k Keeper) GetCodec() codec.Codec { + return k.cdc +} diff --git a/x/authority/keeper/msg_server.go b/x/authority/keeper/msg_server.go new file mode 100644 index 0000000000..976679fd31 --- /dev/null +++ b/x/authority/keeper/msg_server.go @@ -0,0 +1,15 @@ +package keeper + +import "github.com/zeta-chain/zetacore/x/authority/types" + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} //nolint:typecheck +} + +var _ types.MsgServer = msgServer{} //nolint:typecheck diff --git a/x/authority/keeper/msg_server_update_policies.go b/x/authority/keeper/msg_server_update_policies.go new file mode 100644 index 0000000000..e228c8cdce --- /dev/null +++ b/x/authority/keeper/msg_server_update_policies.go @@ -0,0 +1,30 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// UpdatePolicies updates policies +func (k msgServer) UpdatePolicies(goCtx context.Context, msg *types.MsgUpdatePolicies) (*types.MsgUpdatePoliciesResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // check called by governance + if k.govAddr.String() != msg.Signer { + return nil, errorsmod.Wrapf( + govtypes.ErrInvalidSigner, + "invalid authority, expected %s, got %s", + k.govAddr.String(), + msg.Signer, + ) + } + + // set policies + k.SetPolicies(ctx, msg.Policies) + + return &types.MsgUpdatePoliciesResponse{}, nil +} diff --git a/x/authority/keeper/msg_server_update_policies_test.go b/x/authority/keeper/msg_server_update_policies_test.go new file mode 100644 index 0000000000..5a9a124a2a --- /dev/null +++ b/x/authority/keeper/msg_server_update_policies_test.go @@ -0,0 +1,48 @@ +package keeper_test + +import ( + "testing" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestMsgServer_UpdatePolicies(t *testing.T) { + t.Run("can't update policies with invalid signer", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + msgServer := keeper.NewMsgServerImpl(*k) + + policies := sample.Policies() + + _, err := msgServer.UpdatePolicies(sdk.WrapSDKContext(ctx), &types.MsgUpdatePolicies{ + Signer: sample.AccAddress(), + Policies: policies, + }) + require.ErrorIs(t, err, govtypes.ErrInvalidSigner) + }) + + t.Run("can update policies", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + msgServer := keeper.NewMsgServerImpl(*k) + + policies := sample.Policies() + + res, err := msgServer.UpdatePolicies(sdk.WrapSDKContext(ctx), &types.MsgUpdatePolicies{ + Signer: keepertest.AuthorityGovAddress.String(), + Policies: policies, + }) + require.NotNil(t, res) + require.NoError(t, err) + + // Check policy is set + got, found := k.GetPolicies(ctx) + require.True(t, found) + require.Equal(t, policies, got) + }) +} diff --git a/x/authority/keeper/policies.go b/x/authority/keeper/policies.go new file mode 100644 index 0000000000..93f0086e7a --- /dev/null +++ b/x/authority/keeper/policies.go @@ -0,0 +1,39 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// SetPolicies sets the policies to the store +func (k Keeper) SetPolicies(ctx sdk.Context, policies types.Policies) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PoliciesKey)) + b := k.cdc.MustMarshal(&policies) + store.Set([]byte{0}, b) +} + +// GetPolicies returns the policies from the store +func (k Keeper) GetPolicies(ctx sdk.Context) (val types.Policies, found bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PoliciesKey)) + b := store.Get([]byte{0}) + if b == nil { + return val, false + } + k.cdc.MustUnmarshal(b, &val) + return val, true +} + +// IsAuthorized checks if the address is authorized for the given policy type +func (k Keeper) IsAuthorized(ctx sdk.Context, address string, policyType types.PolicyType) bool { + policies, found := k.GetPolicies(ctx) + if !found { + return false + } + for _, policy := range policies.Items { + if policy.Address == address && policy.PolicyType == policyType { + return true + } + } + return false +} diff --git a/x/authority/keeper/policies_test.go b/x/authority/keeper/policies_test.go new file mode 100644 index 0000000000..13c0c4d80b --- /dev/null +++ b/x/authority/keeper/policies_test.go @@ -0,0 +1,59 @@ +package keeper_test + +import ( + "testing" + + "github.com/zeta-chain/zetacore/x/authority/types" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" +) + +func TestKeeper_SetPolicies(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + policies := sample.Policies() + + _, found := k.GetPolicies(ctx) + require.False(t, found) + + k.SetPolicies(ctx, policies) + + // Check policy is set + got, found := k.GetPolicies(ctx) + require.True(t, found) + require.Equal(t, policies, got) + + // Can set policies again + newPolicies := sample.Policies() + require.NotEqual(t, policies, newPolicies) + k.SetPolicies(ctx, newPolicies) + got, found = k.GetPolicies(ctx) + require.True(t, found) + require.Equal(t, newPolicies, got) +} + +func TestKeeper_IsAuthorized(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + + // Not authorized if no policies + require.False(t, k.IsAuthorized(ctx, sample.AccAddress(), types.PolicyType_groupAdmin)) + require.False(t, k.IsAuthorized(ctx, sample.AccAddress(), types.PolicyType_groupEmergency)) + + policies := sample.Policies() + k.SetPolicies(ctx, policies) + + // Check policy is set + got, found := k.GetPolicies(ctx) + require.True(t, found) + require.Equal(t, policies, got) + + // Check policy is authorized + for _, policy := range policies.Items { + require.True(t, k.IsAuthorized(ctx, policy.Address, policy.PolicyType)) + } + + // Check policy is not authorized + require.False(t, k.IsAuthorized(ctx, sample.AccAddress(), types.PolicyType_groupAdmin)) + require.False(t, k.IsAuthorized(ctx, sample.AccAddress(), types.PolicyType_groupEmergency)) +} diff --git a/x/authority/module.go b/x/authority/module.go new file mode 100644 index 0000000000..6c7d535ec5 --- /dev/null +++ b/x/authority/module.go @@ -0,0 +1,170 @@ +package authority + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/zeta-chain/zetacore/x/authority/client/cli" + "github.com/zeta-chain/zetacore/x/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface for the authority module. +type AppModuleBasic struct { + cdc codec.Codec +} + +func NewAppModuleBasic(cdc codec.Codec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the authority module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +func (AppModuleBasic) RegisterCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} + +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) +} + +// RegisterInterfaces registers the module's interface types +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} + +// DefaultGenesis returns the authority module's default genesis state. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis performs genesis state validation for the authority module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// RegisterRESTRoutes registers the authority module's REST service handlers. +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) { +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + fmt.Println("RegisterQueryHandlerClient err: %w", err) + } +} + +// GetTxCmd returns the authority module's root tx command. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the authority module's root query command. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd(types.StoreKey) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface for the authority module. +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// Name returns the authority module's name. +func (am AppModule) Name() string { + return am.AppModuleBasic.Name() +} + +// Route returns the authority module's message routing key. +func (am AppModule) Route() sdk.Route { + return sdk.Route{} +} + +// QuerierRoute returns the authority module's query routing key. +func (AppModule) QuerierRoute() string { return types.QuerierRoute } + +// LegacyQuerierHandler returns the authority module's Querier. +func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier { + return nil +} + +// RegisterServices registers a GRPC query service to respond to the +// module-specific GRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// RegisterInvariants registers the authority module's invariants. +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs the authority module's genesis initialization It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the authority module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock executes all ABCI BeginBlock logic respective to the authority module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock executes all ABCI EndBlock logic respective to the authority module. It +// returns no validator updates. +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/authority/types/codec.go b/x/authority/types/codec.go new file mode 100644 index 0000000000..ccb8f1e78b --- /dev/null +++ b/x/authority/types/codec.go @@ -0,0 +1,25 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgUpdatePolicies{}, "authority/UpdatePolicies", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgUpdatePolicies{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) diff --git a/x/authority/types/errors.go b/x/authority/types/errors.go new file mode 100644 index 0000000000..ab1254f4c2 --- /dev/null +++ b/x/authority/types/errors.go @@ -0,0 +1 @@ +package types diff --git a/x/authority/types/genesis.go b/x/authority/types/genesis.go new file mode 100644 index 0000000000..bed0e04a41 --- /dev/null +++ b/x/authority/types/genesis.go @@ -0,0 +1,13 @@ +package types + +// DefaultGenesis returns the default authority genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Policies: DefaultPolicies(), + } +} + +// Validate performs basic genesis state validation returning an error upon any failure +func (gs GenesisState) Validate() error { + return gs.Policies.Validate() +} diff --git a/x/authority/types/genesis.pb.go b/x/authority/types/genesis.pb.go new file mode 100644 index 0000000000..f76e4710a6 --- /dev/null +++ b/x/authority/types/genesis.pb.go @@ -0,0 +1,322 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: authority/genesis.proto + +package types + +import ( + fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/gogo/protobuf/proto" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the authority module's genesis state. +type GenesisState struct { + Policies Policies `protobuf:"bytes,1,opt,name=policies,proto3" json:"policies"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_72622afc33ec94d4, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetPolicies() Policies { + if m != nil { + return m.Policies + } + return Policies{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "zetachain.zetacore.authority.GenesisState") +} + +func init() { proto.RegisterFile("authority/genesis.proto", fileDescriptor_72622afc33ec94d4) } + +var fileDescriptor_72622afc33ec94d4 = []byte{ + // 201 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0x2c, 0x2d, 0xc9, + 0xc8, 0x2f, 0xca, 0x2c, 0xa9, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, + 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xa9, 0x4a, 0x2d, 0x49, 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x03, + 0xb3, 0xf2, 0x8b, 0x52, 0xf5, 0xe0, 0x6a, 0xa5, 0x24, 0x10, 0xda, 0x0a, 0xf2, 0x73, 0x32, 0x93, + 0x33, 0x53, 0xa1, 0xfa, 0xa4, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x4c, 0x7d, 0x10, 0x0b, 0x22, + 0xaa, 0x14, 0xc1, 0xc5, 0xe3, 0x0e, 0x31, 0x3e, 0xb8, 0x24, 0xb1, 0x24, 0x55, 0xc8, 0x83, 0x8b, + 0x03, 0xa6, 0x4f, 0x82, 0x51, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x4d, 0x0f, 0x9f, 0x85, 0x7a, 0x01, + 0x50, 0xd5, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0xc1, 0x75, 0x3b, 0x79, 0x9d, 0x78, 0x24, + 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, + 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x41, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, + 0x72, 0x7e, 0xae, 0x3e, 0xc8, 0x44, 0x5d, 0xb0, 0xe1, 0xfa, 0x30, 0xc3, 0xf5, 0x2b, 0xf4, 0x11, + 0x9e, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, 0x3b, 0xd6, 0x18, 0x10, 0x00, 0x00, 0xff, + 0xff, 0x70, 0x61, 0x2d, 0x81, 0x15, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Policies.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Policies.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Policies.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/authority/types/genesis_test.go b/x/authority/types/genesis_test.go new file mode 100644 index 0000000000..ec53fc1fad --- /dev/null +++ b/x/authority/types/genesis_test.go @@ -0,0 +1,57 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestGenesisState_Validate(t *testing.T) { + setConfig() + + tests := []struct { + name string + gs *types.GenesisState + errContains string + }{ + { + name: "default is valid", + gs: types.DefaultGenesis(), + errContains: "", + }, + { + name: "valid genesis", + gs: &types.GenesisState{ + Policies: sample.Policies(), + }, + errContains: "", + }, + { + name: "invalid if policies is invalid", + gs: &types.GenesisState{ + Policies: types.Policies{ + Items: []*types.Policy{ + { + Address: "invalid", + PolicyType: types.PolicyType_groupEmergency, + }, + }, + }, + }, + errContains: "invalid address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.gs.Validate() + if tt.errContains != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.errContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/authority/types/keys.go b/x/authority/types/keys.go new file mode 100644 index 0000000000..5b86ca9711 --- /dev/null +++ b/x/authority/types/keys.go @@ -0,0 +1,28 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "authority" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey is the message route + RouterKey = ModuleName + + // QuerierRoute defines the module's query routing key + QuerierRoute = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_" + ModuleName +) + +// KeyPrefix returns the store key prefix for the policies store +func KeyPrefix(p string) []byte { + return []byte(p) +} + +const ( + // PoliciesKey is the key for the policies store + PoliciesKey = "Policies-value-" +) diff --git a/x/authority/types/message_update_policies.go b/x/authority/types/message_update_policies.go new file mode 100644 index 0000000000..e34a7fb6c3 --- /dev/null +++ b/x/authority/types/message_update_policies.go @@ -0,0 +1,52 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const TypeMsgUpdatePolicies = "UpdatePolicies" + +var _ sdk.Msg = &MsgUpdatePolicies{} + +func NewMsgUpdatePolicies(signer string, policies Policies) *MsgUpdatePolicies { + return &MsgUpdatePolicies{ + Signer: signer, + Policies: policies, + } +} + +func (msg *MsgUpdatePolicies) Route() string { + return RouterKey +} + +func (msg *MsgUpdatePolicies) Type() string { + return TypeMsgUpdatePolicies +} + +func (msg *MsgUpdatePolicies) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgUpdatePolicies) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgUpdatePolicies) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid signer address (%s)", err) + } + + if err := msg.Policies.Validate(); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid policies (%s)", err) + } + + return nil +} diff --git a/x/authority/types/message_update_policies_test.go b/x/authority/types/message_update_policies_test.go new file mode 100644 index 0000000000..011d1e5d2c --- /dev/null +++ b/x/authority/types/message_update_policies_test.go @@ -0,0 +1,53 @@ +package types_test + +import ( + "testing" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestMsgUpdatePolicies_ValidateBasic(t *testing.T) { + setConfig() + + tests := []struct { + name string + msg *types.MsgUpdatePolicies + err error + }{ + { + name: "valid message", + msg: types.NewMsgUpdatePolicies(sample.AccAddress(), sample.Policies()), + }, + { + name: "invalid creator address", + msg: types.NewMsgUpdatePolicies("invalid", sample.Policies()), + err: sdkerrors.ErrInvalidAddress, + }, + { + name: "invalid policies", + msg: types.NewMsgUpdatePolicies(sample.AccAddress(), types.Policies{ + Items: []*types.Policy{ + { + Address: "invalid", + PolicyType: types.PolicyType_groupEmergency, + }, + }, + }), + err: sdkerrors.ErrInvalidRequest, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.msg.ValidateBasic() + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/authority/types/policies.go b/x/authority/types/policies.go new file mode 100644 index 0000000000..5526ea98f1 --- /dev/null +++ b/x/authority/types/policies.go @@ -0,0 +1,52 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // DefaultPolicyAddress is the default value for policy address + DefaultPolicyAddress = "zeta1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsxn0x73" +) + +// DefaultPolicies returns the default value for policies +func DefaultPolicies() Policies { + return Policies{ + Items: []*Policy{ + { + Address: DefaultPolicyAddress, + PolicyType: PolicyType_groupEmergency, + }, + { + Address: DefaultPolicyAddress, + PolicyType: PolicyType_groupAdmin, + }, + }, + } +} + +// Validate performs basic validation of policies +func (p Policies) Validate() error { + policyTypeMap := make(map[PolicyType]bool) + + // for each policy, check address, policy type, and ensure no duplicate policy types + for _, policy := range p.Items { + _, err := sdk.AccAddressFromBech32(policy.Address) + if err != nil { + return fmt.Errorf("invalid address: %s", err) + } + + if policy.PolicyType != PolicyType_groupEmergency && policy.PolicyType != PolicyType_groupAdmin { + return fmt.Errorf("invalid policy type: %s", policy.PolicyType) + } + + if policyTypeMap[policy.PolicyType] { + return fmt.Errorf("duplicate policy type: %s", policy.PolicyType) + } + policyTypeMap[policy.PolicyType] = true + } + + return nil +} diff --git a/x/authority/types/policies.pb.go b/x/authority/types/policies.pb.go new file mode 100644 index 0000000000..bf14e65f12 --- /dev/null +++ b/x/authority/types/policies.pb.go @@ -0,0 +1,568 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: authority/policies.proto + +package types + +import ( + fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/gogo/protobuf/proto" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PolicyType defines the type of policy +type PolicyType int32 + +const ( + PolicyType_groupEmergency PolicyType = 0 + PolicyType_groupAdmin PolicyType = 1 +) + +var PolicyType_name = map[int32]string{ + 0: "groupEmergency", + 1: "groupAdmin", +} + +var PolicyType_value = map[string]int32{ + "groupEmergency": 0, + "groupAdmin": 1, +} + +func (x PolicyType) String() string { + return proto.EnumName(PolicyType_name, int32(x)) +} + +func (PolicyType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_cf39aa57ca0776f1, []int{0} +} + +type Policy struct { + PolicyType PolicyType `protobuf:"varint,1,opt,name=policy_type,json=policyType,proto3,enum=zetachain.zetacore.authority.PolicyType" json:"policy_type,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *Policy) Reset() { *m = Policy{} } +func (m *Policy) String() string { return proto.CompactTextString(m) } +func (*Policy) ProtoMessage() {} +func (*Policy) Descriptor() ([]byte, []int) { + return fileDescriptor_cf39aa57ca0776f1, []int{0} +} +func (m *Policy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Policy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Policy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Policy) XXX_Merge(src proto.Message) { + xxx_messageInfo_Policy.Merge(m, src) +} +func (m *Policy) XXX_Size() int { + return m.Size() +} +func (m *Policy) XXX_DiscardUnknown() { + xxx_messageInfo_Policy.DiscardUnknown(m) +} + +var xxx_messageInfo_Policy proto.InternalMessageInfo + +func (m *Policy) GetPolicyType() PolicyType { + if m != nil { + return m.PolicyType + } + return PolicyType_groupEmergency +} + +func (m *Policy) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// Policy contains info about authority policies +type Policies struct { + Items []*Policy `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` +} + +func (m *Policies) Reset() { *m = Policies{} } +func (m *Policies) String() string { return proto.CompactTextString(m) } +func (*Policies) ProtoMessage() {} +func (*Policies) Descriptor() ([]byte, []int) { + return fileDescriptor_cf39aa57ca0776f1, []int{1} +} +func (m *Policies) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Policies) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Policies.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Policies) XXX_Merge(src proto.Message) { + xxx_messageInfo_Policies.Merge(m, src) +} +func (m *Policies) XXX_Size() int { + return m.Size() +} +func (m *Policies) XXX_DiscardUnknown() { + xxx_messageInfo_Policies.DiscardUnknown(m) +} + +var xxx_messageInfo_Policies proto.InternalMessageInfo + +func (m *Policies) GetItems() []*Policy { + if m != nil { + return m.Items + } + return nil +} + +func init() { + proto.RegisterEnum("zetachain.zetacore.authority.PolicyType", PolicyType_name, PolicyType_value) + proto.RegisterType((*Policy)(nil), "zetachain.zetacore.authority.Policy") + proto.RegisterType((*Policies)(nil), "zetachain.zetacore.authority.Policies") +} + +func init() { proto.RegisterFile("authority/policies.proto", fileDescriptor_cf39aa57ca0776f1) } + +var fileDescriptor_cf39aa57ca0776f1 = []byte{ + // 284 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x48, 0x2c, 0x2d, 0xc9, + 0xc8, 0x2f, 0xca, 0x2c, 0xa9, 0xd4, 0x2f, 0xc8, 0xcf, 0xc9, 0x4c, 0xce, 0x4c, 0x2d, 0xd6, 0x2b, + 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xa9, 0x4a, 0x2d, 0x49, 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, + 0x03, 0xb3, 0xf2, 0x8b, 0x52, 0xf5, 0xe0, 0x8a, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x0a, + 0xf5, 0x41, 0x2c, 0x88, 0x1e, 0xa5, 0x5c, 0x2e, 0xb6, 0x00, 0x90, 0x29, 0x95, 0x42, 0x9e, 0x5c, + 0xdc, 0x60, 0xf3, 0x2a, 0xe3, 0x4b, 0x2a, 0x0b, 0x52, 0x25, 0x18, 0x15, 0x18, 0x35, 0xf8, 0x8c, + 0x34, 0xf4, 0xf0, 0x99, 0xa9, 0x07, 0xd1, 0x1a, 0x52, 0x59, 0x90, 0x1a, 0xc4, 0x55, 0x00, 0x67, + 0x0b, 0x49, 0x70, 0xb1, 0x27, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x4b, 0x30, 0x29, 0x30, 0x6a, + 0x70, 0x06, 0xc1, 0xb8, 0x4a, 0x6e, 0x5c, 0x1c, 0x01, 0x50, 0x47, 0x0b, 0x59, 0x71, 0xb1, 0x66, + 0x96, 0xa4, 0xe6, 0x16, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0xa9, 0x10, 0x63, 0x55, 0x10, + 0x44, 0x8b, 0x96, 0x19, 0x17, 0x17, 0xc2, 0x6e, 0x21, 0x21, 0x2e, 0xbe, 0xf4, 0xa2, 0xfc, 0xd2, + 0x02, 0xd7, 0xdc, 0xd4, 0xa2, 0xf4, 0xd4, 0xbc, 0xe4, 0x4a, 0x01, 0x06, 0x21, 0x3e, 0x2e, 0x2e, + 0xb0, 0x98, 0x63, 0x4a, 0x6e, 0x66, 0x9e, 0x00, 0xa3, 0x14, 0xcb, 0x8a, 0x25, 0x72, 0x8c, 0x4e, + 0x5e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, + 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x90, 0x9e, 0x59, 0x92, + 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0xb2, 0x5e, 0x17, 0xec, 0x12, 0x7d, 0x98, 0x4b, + 0xf4, 0x2b, 0xf4, 0x11, 0xe1, 0x0e, 0x0a, 0xa0, 0xe2, 0x24, 0x36, 0x70, 0x08, 0x1a, 0x03, 0x02, + 0x00, 0x00, 0xff, 0xff, 0x08, 0x31, 0x60, 0x2c, 0x91, 0x01, 0x00, 0x00, +} + +func (m *Policy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Policy) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Policy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintPolicies(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0x12 + } + if m.PolicyType != 0 { + i = encodeVarintPolicies(dAtA, i, uint64(m.PolicyType)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Policies) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Policies) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Policies) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPolicies(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintPolicies(dAtA []byte, offset int, v uint64) int { + offset -= sovPolicies(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Policy) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PolicyType != 0 { + n += 1 + sovPolicies(uint64(m.PolicyType)) + } + l = len(m.Address) + if l > 0 { + n += 1 + l + sovPolicies(uint64(l)) + } + return n +} + +func (m *Policies) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovPolicies(uint64(l)) + } + } + return n +} + +func sovPolicies(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozPolicies(x uint64) (n int) { + return sovPolicies(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Policy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPolicies + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Policy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Policy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PolicyType", wireType) + } + m.PolicyType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPolicies + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PolicyType |= PolicyType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPolicies + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPolicies + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPolicies + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPolicies(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPolicies + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Policies) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPolicies + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Policies: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Policies: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPolicies + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPolicies + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPolicies + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, &Policy{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPolicies(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPolicies + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPolicies(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPolicies + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPolicies + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPolicies + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthPolicies + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPolicies + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthPolicies + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthPolicies = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPolicies = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPolicies = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/authority/types/policies_test.go b/x/authority/types/policies_test.go new file mode 100644 index 0000000000..8d609d377c --- /dev/null +++ b/x/authority/types/policies_test.go @@ -0,0 +1,112 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/app" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// setConfig sets the global config to use zeta chain's bech32 prefixes +func setConfig() { + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) + cfg.Seal() +} + +func TestPolicies_Validate(t *testing.T) { + setConfig() + + // use table driven tests to test the validation of policies + tests := []struct { + name string + policies types.Policies + errContains string + }{ + { + name: "empty is valid", + policies: types.Policies{}, + errContains: "", + }, + { + name: "default is valid", + policies: types.DefaultPolicies(), + errContains: "", + }, + { + name: "regular valid policies", + policies: sample.Policies(), + errContains: "", + }, + { + name: "valid if a policy type is not existing", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: sample.AccAddress(), + PolicyType: types.PolicyType_groupEmergency, + }, + }, + }, + errContains: "", + }, + { + name: "invalid if address is invalid", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: "invalid", + PolicyType: types.PolicyType_groupEmergency, + }, + }, + }, + errContains: "invalid address", + }, + { + name: "invalid if policy type is invalid", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: sample.AccAddress(), + PolicyType: types.PolicyType(1000), + }, + }, + }, + errContains: "invalid policy type", + }, + { + name: "invalid if duplicated policy type", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: sample.AccAddress(), + PolicyType: types.PolicyType_groupEmergency, + }, + { + Address: sample.AccAddress(), + PolicyType: types.PolicyType_groupAdmin, + }, + { + Address: sample.AccAddress(), + PolicyType: types.PolicyType_groupEmergency, + }, + }, + }, + errContains: "duplicate policy type", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.policies.Validate() + if tt.errContains != "" { + require.ErrorContains(t, err, tt.errContains) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/authority/types/query.pb.go b/x/authority/types/query.pb.go new file mode 100644 index 0000000000..8d46ef40f1 --- /dev/null +++ b/x/authority/types/query.pb.go @@ -0,0 +1,538 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: authority/query.proto + +package types + +import ( + context "context" + fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + + _ "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryGetPoliciesRequest is the request type for the Query/Policies RPC method. +type QueryGetPoliciesRequest struct { +} + +func (m *QueryGetPoliciesRequest) Reset() { *m = QueryGetPoliciesRequest{} } +func (m *QueryGetPoliciesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryGetPoliciesRequest) ProtoMessage() {} +func (*QueryGetPoliciesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b64d9e7f9da035b5, []int{0} +} +func (m *QueryGetPoliciesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetPoliciesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetPoliciesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetPoliciesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetPoliciesRequest.Merge(m, src) +} +func (m *QueryGetPoliciesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryGetPoliciesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetPoliciesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetPoliciesRequest proto.InternalMessageInfo + +// QueryGetPoliciesResponse is the response type for the Query/Policies RPC method. +type QueryGetPoliciesResponse struct { + Policies Policies `protobuf:"bytes,1,opt,name=policies,proto3" json:"policies"` +} + +func (m *QueryGetPoliciesResponse) Reset() { *m = QueryGetPoliciesResponse{} } +func (m *QueryGetPoliciesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryGetPoliciesResponse) ProtoMessage() {} +func (*QueryGetPoliciesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b64d9e7f9da035b5, []int{1} +} +func (m *QueryGetPoliciesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryGetPoliciesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryGetPoliciesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryGetPoliciesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryGetPoliciesResponse.Merge(m, src) +} +func (m *QueryGetPoliciesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryGetPoliciesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryGetPoliciesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryGetPoliciesResponse proto.InternalMessageInfo + +func (m *QueryGetPoliciesResponse) GetPolicies() Policies { + if m != nil { + return m.Policies + } + return Policies{} +} + +func init() { + proto.RegisterType((*QueryGetPoliciesRequest)(nil), "zetachain.zetacore.authority.QueryGetPoliciesRequest") + proto.RegisterType((*QueryGetPoliciesResponse)(nil), "zetachain.zetacore.authority.QueryGetPoliciesResponse") +} + +func init() { proto.RegisterFile("authority/query.proto", fileDescriptor_b64d9e7f9da035b5) } + +var fileDescriptor_b64d9e7f9da035b5 = []byte{ + // 318 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4d, 0x2c, 0x2d, 0xc9, + 0xc8, 0x2f, 0xca, 0x2c, 0xa9, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0x92, 0xa9, 0x4a, 0x2d, 0x49, 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x03, 0xb3, 0xf2, + 0x8b, 0x52, 0xf5, 0xe0, 0x2a, 0xa5, 0x24, 0x10, 0x9a, 0x0a, 0xf2, 0x73, 0x32, 0x93, 0x33, 0x53, + 0x8b, 0x21, 0xfa, 0xa4, 0xb4, 0x92, 0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xf5, 0x93, 0x12, 0x8b, 0x53, + 0x21, 0x06, 0xea, 0x97, 0x19, 0x26, 0xa5, 0x96, 0x24, 0x1a, 0xea, 0x17, 0x24, 0xa6, 0x67, 0xe6, + 0x25, 0x96, 0x64, 0xe6, 0xe7, 0x41, 0xd5, 0x8a, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x99, 0xfa, 0x20, + 0x16, 0x54, 0x54, 0x26, 0x3d, 0x3f, 0x3f, 0x3d, 0x27, 0x55, 0x3f, 0xb1, 0x20, 0x53, 0x3f, 0x31, + 0x2f, 0x2f, 0xbf, 0x04, 0xac, 0x05, 0x6a, 0xbe, 0x92, 0x24, 0x97, 0x78, 0x20, 0xc8, 0x54, 0xf7, + 0xd4, 0x92, 0x00, 0xa8, 0xcd, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x4a, 0x29, 0x5c, 0x12, + 0x98, 0x52, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x1e, 0x5c, 0x1c, 0x30, 0x87, 0x4a, 0x30, + 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0xa9, 0xe9, 0xe1, 0xf3, 0xa1, 0x1e, 0xcc, 0x04, 0x27, 0x96, 0x13, + 0xf7, 0xe4, 0x19, 0x82, 0xe0, 0xba, 0x8d, 0x56, 0x33, 0x72, 0xb1, 0x82, 0xad, 0x11, 0x5a, 0xc8, + 0xc8, 0xc5, 0x01, 0x53, 0x26, 0x64, 0x8a, 0xdf, 0x38, 0x1c, 0x6e, 0x96, 0x32, 0x23, 0x55, 0x1b, + 0xc4, 0x3f, 0x4a, 0x6a, 0x4d, 0x97, 0x9f, 0x4c, 0x66, 0x52, 0x10, 0x92, 0xd3, 0x07, 0xe9, 0xd2, + 0x05, 0x1b, 0xa0, 0x8f, 0x19, 0x29, 0x4e, 0x5e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, + 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, + 0xc7, 0x10, 0x65, 0x90, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0x8b, 0x6c, 0x06, + 0xcc, 0x11, 0xfa, 0x15, 0x48, 0xc6, 0x95, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x63, 0xc0, + 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x24, 0x39, 0xaa, 0x53, 0x32, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Queries Policies + Policies(ctx context.Context, in *QueryGetPoliciesRequest, opts ...grpc.CallOption) (*QueryGetPoliciesResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Policies(ctx context.Context, in *QueryGetPoliciesRequest, opts ...grpc.CallOption) (*QueryGetPoliciesResponse, error) { + out := new(QueryGetPoliciesResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.authority.Query/Policies", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Queries Policies + Policies(context.Context, *QueryGetPoliciesRequest) (*QueryGetPoliciesResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Policies(ctx context.Context, req *QueryGetPoliciesRequest) (*QueryGetPoliciesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Policies not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Policies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryGetPoliciesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Policies(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.authority.Query/Policies", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Policies(ctx, req.(*QueryGetPoliciesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "zetachain.zetacore.authority.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Policies", + Handler: _Query_Policies_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "authority/query.proto", +} + +func (m *QueryGetPoliciesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetPoliciesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetPoliciesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryGetPoliciesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryGetPoliciesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryGetPoliciesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Policies.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryGetPoliciesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetPoliciesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Policies.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryGetPoliciesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetPoliciesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetPoliciesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryGetPoliciesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryGetPoliciesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryGetPoliciesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Policies.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/authority/types/query.pb.gw.go b/x/authority/types/query.pb.gw.go new file mode 100644 index 0000000000..008ffb866d --- /dev/null +++ b/x/authority/types/query.pb.gw.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: authority/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Policies_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetPoliciesRequest + var metadata runtime.ServerMetadata + + msg, err := client.Policies(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Policies_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryGetPoliciesRequest + var metadata runtime.ServerMetadata + + msg, err := server.Policies(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Policies_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Policies_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Policies_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Policies_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Policies_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Policies_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Policies_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "authority", "policies"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Policies_0 = runtime.ForwardResponseMessage +) diff --git a/x/authority/types/tx.pb.go b/x/authority/types/tx.pb.go new file mode 100644 index 0000000000..1f109e4e3d --- /dev/null +++ b/x/authority/types/tx.pb.go @@ -0,0 +1,582 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: authority/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgUpdatePolicies defines the MsgUpdatePolicies service. +type MsgUpdatePolicies struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + Policies Policies `protobuf:"bytes,2,opt,name=policies,proto3" json:"policies"` +} + +func (m *MsgUpdatePolicies) Reset() { *m = MsgUpdatePolicies{} } +func (m *MsgUpdatePolicies) String() string { return proto.CompactTextString(m) } +func (*MsgUpdatePolicies) ProtoMessage() {} +func (*MsgUpdatePolicies) Descriptor() ([]byte, []int) { + return fileDescriptor_dccbf6440c31743d, []int{0} +} +func (m *MsgUpdatePolicies) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdatePolicies) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdatePolicies.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdatePolicies) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdatePolicies.Merge(m, src) +} +func (m *MsgUpdatePolicies) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdatePolicies) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdatePolicies.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdatePolicies proto.InternalMessageInfo + +func (m *MsgUpdatePolicies) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgUpdatePolicies) GetPolicies() Policies { + if m != nil { + return m.Policies + } + return Policies{} +} + +// MsgUpdatePoliciesResponse defines the MsgUpdatePoliciesResponse service. +type MsgUpdatePoliciesResponse struct { +} + +func (m *MsgUpdatePoliciesResponse) Reset() { *m = MsgUpdatePoliciesResponse{} } +func (m *MsgUpdatePoliciesResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdatePoliciesResponse) ProtoMessage() {} +func (*MsgUpdatePoliciesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dccbf6440c31743d, []int{1} +} +func (m *MsgUpdatePoliciesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdatePoliciesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdatePoliciesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdatePoliciesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdatePoliciesResponse.Merge(m, src) +} +func (m *MsgUpdatePoliciesResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdatePoliciesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdatePoliciesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdatePoliciesResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgUpdatePolicies)(nil), "zetachain.zetacore.authority.MsgUpdatePolicies") + proto.RegisterType((*MsgUpdatePoliciesResponse)(nil), "zetachain.zetacore.authority.MsgUpdatePoliciesResponse") +} + +func init() { proto.RegisterFile("authority/tx.proto", fileDescriptor_dccbf6440c31743d) } + +var fileDescriptor_dccbf6440c31743d = []byte{ + // 260 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4a, 0x2c, 0x2d, 0xc9, + 0xc8, 0x2f, 0xca, 0x2c, 0xa9, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, + 0xa9, 0x4a, 0x2d, 0x49, 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x03, 0xb3, 0xf2, 0x8b, 0x52, 0xf5, + 0xe0, 0xca, 0xa4, 0x24, 0x10, 0x3a, 0x0a, 0xf2, 0x73, 0x32, 0x93, 0x33, 0x53, 0x8b, 0x21, 0xfa, + 0xa4, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x4c, 0x7d, 0x10, 0x0b, 0x22, 0xaa, 0x54, 0xca, 0x25, + 0xe8, 0x5b, 0x9c, 0x1e, 0x5a, 0x90, 0x92, 0x58, 0x92, 0x1a, 0x00, 0xd5, 0x20, 0x24, 0xc6, 0xc5, + 0x56, 0x9c, 0x99, 0x9e, 0x97, 0x5a, 0x24, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x19, 0x04, 0xe5, 0x09, + 0x79, 0x70, 0x71, 0xc0, 0x0c, 0x95, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd3, 0xc3, 0xe7, + 0x1a, 0x3d, 0x98, 0x89, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0xc1, 0x75, 0x2b, 0x49, 0x73, + 0x49, 0x62, 0x58, 0x1b, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x6a, 0xd4, 0xc8, 0xc8, 0xc5, + 0xec, 0x5b, 0x9c, 0x2e, 0x54, 0xc5, 0xc5, 0x87, 0xe6, 0x30, 0x7d, 0xfc, 0xd6, 0x61, 0x18, 0x29, + 0x65, 0x4e, 0xa2, 0x06, 0x98, 0x1b, 0x9c, 0xbc, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, + 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, + 0x8e, 0x21, 0xca, 0x20, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x64, + 0xa4, 0x2e, 0xd8, 0x74, 0x7d, 0x98, 0xe9, 0xfa, 0x15, 0xfa, 0x48, 0x91, 0x56, 0x59, 0x90, 0x5a, + 0x9c, 0xc4, 0x06, 0x0e, 0x6a, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x8b, 0x6c, 0x4f, + 0xce, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + UpdatePolicies(ctx context.Context, in *MsgUpdatePolicies, opts ...grpc.CallOption) (*MsgUpdatePoliciesResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) UpdatePolicies(ctx context.Context, in *MsgUpdatePolicies, opts ...grpc.CallOption) (*MsgUpdatePoliciesResponse, error) { + out := new(MsgUpdatePoliciesResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.authority.Msg/UpdatePolicies", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + UpdatePolicies(context.Context, *MsgUpdatePolicies) (*MsgUpdatePoliciesResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) UpdatePolicies(ctx context.Context, req *MsgUpdatePolicies) (*MsgUpdatePoliciesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdatePolicies not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_UpdatePolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdatePolicies) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdatePolicies(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.authority.Msg/UpdatePolicies", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdatePolicies(ctx, req.(*MsgUpdatePolicies)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "zetachain.zetacore.authority.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdatePolicies", + Handler: _Msg_UpdatePolicies_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "authority/tx.proto", +} + +func (m *MsgUpdatePolicies) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdatePolicies) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdatePolicies) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Policies.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdatePoliciesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdatePoliciesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdatePoliciesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgUpdatePolicies) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Policies.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdatePoliciesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgUpdatePolicies) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdatePolicies: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdatePolicies: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Policies.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdatePoliciesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdatePoliciesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdatePoliciesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/crosschain/keeper/gas_payment_test.go b/x/crosschain/keeper/gas_payment_test.go index d0a702daf5..8ed31d8a46 100644 --- a/x/crosschain/keeper/gas_payment_test.go +++ b/x/crosschain/keeper/gas_payment_test.go @@ -4,22 +4,21 @@ import ( "math/big" "testing" + "github.com/zeta-chain/zetacore/testutil/sample" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" zetacommon "github.com/zeta-chain/zetacore/common" testkeeper "github.com/zeta-chain/zetacore/testutil/keeper" - "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/crosschain/types" - fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) var ( // gasLimit = big.NewInt(21_000) - value used in SetupChainGasCoinAndPool for gas limit initialization - withdrawFee uint64 = 1000 + withdrawFee int64 = 1000 gasPrice uint64 = 2 inputAmount uint64 = 1e16 ) @@ -28,9 +27,6 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { t.Run("can pay gas in native gas", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) - fungibleMsgServer := fungiblekeeper.NewMsgServerImpl(*zk.FungibleKeeper) // deploy gas coin and set fee params chainID := getValidEthChainID(t) @@ -38,11 +34,10 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") - _, err := fungibleMsgServer.UpdateZRC20WithdrawFee( - sdk.UnwrapSDKContext(ctx), - fungibletypes.NewMsgUpdateZRC20WithdrawFee(admin, zrc20.String(), sdk.NewUint(withdrawFee), math.Uint{}), - ) + + _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, zrc20, big.NewInt(withdrawFee)) require.NoError(t, err) + k.SetGasPrice(ctx, types.GasPrice{ ChainId: chainID, MedianIndex: 0, @@ -99,8 +94,6 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { t.Run("should fail if can't query the gas price", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) // deploy gas coin and set fee params chainID := getValidEthChainID(t) @@ -130,20 +123,16 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { t.Run("should fail if not enough amount for the fee", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) - fungibleMsgServer := fungiblekeeper.NewMsgServerImpl(*zk.FungibleKeeper) // deploy gas coin and set fee params chainID := getValidEthChainID(t) setSupportedChain(ctx, zk, chainID) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") - _, err := fungibleMsgServer.UpdateZRC20WithdrawFee( - sdk.UnwrapSDKContext(ctx), - fungibletypes.NewMsgUpdateZRC20WithdrawFee(admin, zrc20.String(), sdk.NewUint(withdrawFee), math.Uint{}), - ) + + _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, zrc20, big.NewInt(withdrawFee)) require.NoError(t, err) + k.SetGasPrice(ctx, types.GasPrice{ ChainId: chainID, MedianIndex: 0, @@ -175,9 +164,6 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) - fungibleMsgServer := fungiblekeeper.NewMsgServerImpl(*zk.FungibleKeeper) // deploy gas coin, erc20 and set fee params chainID := getValidEthChainID(t) @@ -195,11 +181,10 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { assetAddress, "bar", ) - _, err := fungibleMsgServer.UpdateZRC20WithdrawFee( - sdk.UnwrapSDKContext(ctx), - fungibletypes.NewMsgUpdateZRC20WithdrawFee(admin, gasZRC20.String(), sdk.NewUint(withdrawFee), math.Uint{}), - ) + + _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, gasZRC20, big.NewInt(withdrawFee)) require.NoError(t, err) + k.SetGasPrice(ctx, types.GasPrice{ ChainId: chainID, MedianIndex: 0, @@ -270,8 +255,6 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { t.Run("should fail if can't query the gas price", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) // deploy gas coin and set fee params chainID := getValidEthChainID(t) @@ -301,9 +284,6 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { t.Run("should fail if can't find the ZRC20", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) - fungibleMsgServer := fungiblekeeper.NewMsgServerImpl(*zk.FungibleKeeper) // deploy gas coin, erc20 and set fee params chainID := getValidEthChainID(t) @@ -311,11 +291,10 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { assetAddress := sample.EthAddress().String() deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foo", "foo") - _, err := fungibleMsgServer.UpdateZRC20WithdrawFee( - sdk.UnwrapSDKContext(ctx), - fungibletypes.NewMsgUpdateZRC20WithdrawFee(admin, gasZRC20.String(), sdk.NewUint(withdrawFee), math.Uint{}), - ) + + _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, gasZRC20, big.NewInt(withdrawFee)) require.NoError(t, err) + k.SetGasPrice(ctx, types.GasPrice{ ChainId: chainID, MedianIndex: 0, @@ -347,9 +326,6 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { t.Run("should fail if liquidity pool not setup", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) - fungibleMsgServer := fungiblekeeper.NewMsgServerImpl(*zk.FungibleKeeper) // deploy gas coin, erc20 and set fee params chainID := getValidEthChainID(t) @@ -367,11 +343,10 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { assetAddress, "bar", ) - _, err := fungibleMsgServer.UpdateZRC20WithdrawFee( - sdk.UnwrapSDKContext(ctx), - fungibletypes.NewMsgUpdateZRC20WithdrawFee(admin, gasZRC20.String(), sdk.NewUint(withdrawFee), math.Uint{}), - ) + + _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, gasZRC20, big.NewInt(withdrawFee)) require.NoError(t, err) + k.SetGasPrice(ctx, types.GasPrice{ ChainId: chainID, MedianIndex: 0, @@ -403,9 +378,6 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { t.Run("should fail if not enough amount for the fee", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) - fungibleMsgServer := fungiblekeeper.NewMsgServerImpl(*zk.FungibleKeeper) // deploy gas coin, erc20 and set fee params chainID := getValidEthChainID(t) @@ -423,11 +395,10 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { assetAddress, "bar", ) - _, err := fungibleMsgServer.UpdateZRC20WithdrawFee( - sdk.UnwrapSDKContext(ctx), - fungibletypes.NewMsgUpdateZRC20WithdrawFee(admin, gasZRC20.String(), sdk.NewUint(withdrawFee), math.Uint{}), - ) + + _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, gasZRC20, big.NewInt(withdrawFee)) require.NoError(t, err) + k.SetGasPrice(ctx, types.GasPrice{ ChainId: chainID, MedianIndex: 0, @@ -475,8 +446,6 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { t.Run("can pay gas in zeta", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) // deploy gas coin and set fee params chainID := getValidEthChainID(t) @@ -564,8 +533,6 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { t.Run("should fail if can't query the gas price", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) // deploy gas coin and set fee params chainID := getValidEthChainID(t) @@ -595,8 +562,6 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { t.Run("should fail if not enough amount for the fee", func(t *testing.T) { k, ctx, sdkk, zk := testkeeper.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) // deploy gas coin and set fee params chainID := getValidEthChainID(t) diff --git a/x/crosschain/keeper/keeper.go b/x/crosschain/keeper/keeper.go index 745372a48a..5ed6084983 100644 --- a/x/crosschain/keeper/keeper.go +++ b/x/crosschain/keeper/keeper.go @@ -3,13 +3,11 @@ package keeper import ( "fmt" + "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -25,6 +23,7 @@ type ( bankKeeper types.BankKeeper zetaObserverKeeper types.ObserverKeeper fungibleKeeper types.FungibleKeeper + authorityKeeper types.AuthorityKeeper } ) @@ -38,6 +37,7 @@ func NewKeeper( bankKeeper types.BankKeeper, zetaObserverKeeper types.ObserverKeeper, fungibleKeeper types.FungibleKeeper, + authorityKeeper types.AuthorityKeeper, ) *Keeper { // ensure governance module account is set // FIXME: enable this check! (disabled for now to avoid unit test panic) @@ -55,6 +55,7 @@ func NewKeeper( bankKeeper: bankKeeper, zetaObserverKeeper: zetaObserverKeeper, fungibleKeeper: fungibleKeeper, + authorityKeeper: authorityKeeper, } } @@ -82,6 +83,10 @@ func (k Keeper) GetObserverKeeper() types.ObserverKeeper { return k.zetaObserverKeeper } +func (k Keeper) GetAuthorityKeeper() types.AuthorityKeeper { + return k.authorityKeeper +} + func (k Keeper) GetStoreKey() storetypes.StoreKey { return k.storeKey } diff --git a/x/crosschain/keeper/keeper_test.go b/x/crosschain/keeper/keeper_test.go index 3a65063a16..58cc10896e 100644 --- a/x/crosschain/keeper/keeper_test.go +++ b/x/crosschain/keeper/keeper_test.go @@ -16,6 +16,7 @@ import ( "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmdb "github.com/tendermint/tm-db" + authoritykeeper "github.com/zeta-chain/zetacore/x/authority/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/observer/keeper" @@ -44,6 +45,7 @@ func setupKeeper(t testing.TB) (*Keeper, sdk.Context) { authKeeper := authkeeper.AccountKeeper{} observerKeeper := keeper.Keeper{} fungibleKeeper := fungiblekeeper.Keeper{} + authorityKeeper := authoritykeeper.Keeper{} k := NewKeeper( codec.NewProtoCodec(registry), @@ -55,6 +57,7 @@ func setupKeeper(t testing.TB) (*Keeper, sdk.Context) { bankKeeper, observerKeeper, &fungibleKeeper, + authorityKeeper, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/x/crosschain/keeper/msg_server_abort_stuck_cctx.go b/x/crosschain/keeper/msg_server_abort_stuck_cctx.go index 32c4bdf55f..82efc1589e 100644 --- a/x/crosschain/keeper/msg_server_abort_stuck_cctx.go +++ b/x/crosschain/keeper/msg_server_abort_stuck_cctx.go @@ -4,6 +4,7 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -22,7 +23,7 @@ func (k msgServer) AbortStuckCCTX( ctx := sdk.UnwrapSDKContext(goCtx) // check if authorized - if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, observertypes.ErrNotAuthorized } diff --git a/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go b/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go index fadda6b74f..5f266cf51a 100644 --- a/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go +++ b/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -13,10 +14,14 @@ import ( func TestMsgServer_AbortStuckCCTX(t *testing.T) { t.Run("can abort a cctx in pending inbound", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // create a cctx cctx := sample.CrossChainTx(t, "cctx_index") @@ -40,10 +45,14 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { }) t.Run("can abort a cctx in pending outbound", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // create a cctx cctx := sample.CrossChainTx(t, "cctx_index") @@ -67,10 +76,14 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { }) t.Run("can abort a cctx in pending revert", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // create a cctx cctx := sample.CrossChainTx(t, "cctx_index") @@ -94,9 +107,15 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { }) t.Run("cannot abort a cctx in pending outbound if not admin", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) msgServer := crosschainkeeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + // create a cctx cctx := sample.CrossChainTx(t, "cctx_index") cctx.CctxStatus = &crosschaintypes.Status{ @@ -107,17 +126,21 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ - Creator: sample.AccAddress(), + Creator: admin, CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.ErrorIs(t, err, observertypes.ErrNotAuthorized) }) t.Run("cannot abort a cctx if doesn't exist", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) msgServer := crosschainkeeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ @@ -128,10 +151,14 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { }) t.Run("cannot abort a cctx if not pending", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) msgServer := crosschainkeeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // create a cctx cctx := sample.CrossChainTx(t, "cctx_index") diff --git a/x/crosschain/keeper/msg_server_add_to_intx_tracker.go b/x/crosschain/keeper/msg_server_add_to_intx_tracker.go index 53bd5bdc8b..fcd58780af 100644 --- a/x/crosschain/keeper/msg_server_add_to_intx_tracker.go +++ b/x/crosschain/keeper/msg_server_add_to_intx_tracker.go @@ -7,6 +7,7 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -21,8 +22,7 @@ func (k msgServer) AddToInTxTracker(goCtx context.Context, msg *types.MsgAddToIn return nil, observertypes.ErrSupportedChains } - adminPolicyAccount := k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group1) - isAdmin := msg.Creator == adminPolicyAccount + isAdmin := k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupEmergency) isObserver := k.zetaObserverKeeper.IsAuthorized(ctx, msg.Creator) isProven := false diff --git a/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go b/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go index 7457068b38..e3662f2567 100644 --- a/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go @@ -9,19 +9,50 @@ import ( "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +func setupVerificationParams(zk keepertest.ZetaKeepers, ctx sdk.Context, tx_index int64, chainID int64, header ethtypes.Header, headerRLP []byte, block *ethtypes.Block) { + params := zk.ObserverKeeper.GetParams(ctx) + zk.ObserverKeeper.SetParams(ctx, params) + zk.ObserverKeeper.SetBlockHeader(ctx, common.BlockHeader{ + Height: block.Number().Int64(), + Hash: block.Hash().Bytes(), + ParentHash: header.ParentHash.Bytes(), + ChainId: chainID, + Header: common.NewEthereumHeader(headerRLP), + }) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ChainParams: []*observertypes.ChainParams{ + { + ChainId: chainID, + ConnectorContractAddress: block.Transactions()[tx_index].To().Hex(), + BallotThreshold: sdk.OneDec(), + MinObserverDelegation: sdk.OneDec(), + IsSupported: true, + }, + }}) + zk.ObserverKeeper.SetCrosschainFlags(ctx, observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ + IsEthTypeChainEnabled: true, + IsBtcTypeChainEnabled: false, + }, + }) +} + func TestMsgServer_AddToInTxTracker(t *testing.T) { t.Run("add proof based tracker with correct proof", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := int64(5) + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -38,11 +69,14 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { t.Run("fail to add proof based tracker with wrong tx hash", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID(t) + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -59,11 +93,15 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { t.Run("fail to add proof based tracker with wrong chain id", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID(t) + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: sample.AccAddress(), ChainId: 97, @@ -77,12 +115,16 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { _, found := k.GetInTxTracker(ctx, chainID, tx.Hash().Hex()) require.False(t, found) }) + t.Run("fail normal user submit without proof", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) tx_hash := "string" + chainID := getValidEthChainID(t) setSupportedChain(ctx, zk, chainID) + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -96,14 +138,22 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { _, found := k.GetInTxTracker(ctx, chainID, tx_hash) require.False(t, found) }) + t.Run("admin add tx tracker", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + tx_hash := "string" chainID := getValidEthChainID(t) setSupportedChain(ctx, zk, chainID) + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: admin, ChainId: chainID, @@ -117,14 +167,22 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { _, found := k.GetInTxTracker(ctx, chainID, tx_hash) require.True(t, found) }) + t.Run("admin submit fake tracker", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + tx_hash := "string" chainID := getValidEthChainID(t) setSupportedChain(ctx, zk, chainID) + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ Creator: admin, ChainId: chainID, @@ -141,30 +199,3 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { require.False(t, found) }) } - -func setupVerificationParams(zk keepertest.ZetaKeepers, ctx sdk.Context, tx_index int64, chainID int64, header ethtypes.Header, headerRLP []byte, block *ethtypes.Block) { - params := zk.ObserverKeeper.GetParams(ctx) - zk.ObserverKeeper.SetParams(ctx, params) - zk.ObserverKeeper.SetBlockHeader(ctx, common.BlockHeader{ - Height: block.Number().Int64(), - Hash: block.Hash().Bytes(), - ParentHash: header.ParentHash.Bytes(), - ChainId: chainID, - Header: common.NewEthereumHeader(headerRLP), - }) - zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ChainParams: []*observertypes.ChainParams{ - { - ChainId: chainID, - ConnectorContractAddress: block.Transactions()[tx_index].To().Hex(), - BallotThreshold: sdk.OneDec(), - MinObserverDelegation: sdk.OneDec(), - IsSupported: true, - }, - }}) - zk.ObserverKeeper.SetCrosschainFlags(ctx, observertypes.CrosschainFlags{ - BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ - IsEthTypeChainEnabled: true, - IsBtcTypeChainEnabled: false, - }, - }) -} diff --git a/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go b/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go index 64f1396859..518a601965 100644 --- a/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go +++ b/x/crosschain/keeper/msg_server_add_to_outtx_tracker.go @@ -13,6 +13,7 @@ import ( eth "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -48,8 +49,7 @@ func (k msgServer) AddToOutTxTracker(goCtx context.Context, msg *types.MsgAddToO } if msg.Proof == nil { // without proof, only certain accounts can send this message - adminPolicyAccount := k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group1) - isAdmin := msg.Creator == adminPolicyAccount + isAdmin := k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupEmergency) isObserver := k.zetaObserverKeeper.IsAuthorized(ctx, msg.Creator) // Sender needs to be either the admin policy account or an observer diff --git a/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go b/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go index a6dfe317eb..424af23e15 100644 --- a/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go @@ -9,6 +9,7 @@ import ( "github.com/zeta-chain/zetacore/common/ethereum" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -18,17 +19,53 @@ func getEthereumChainID() int64 { return 5 // Goerli } + +func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce int64) { + + tssPubKey := "zetapub1addwnpepq28c57cvcs0a2htsem5zxr6qnlvq9mzhmm76z3jncsnzz32rclangr2g35p" + k.GetObserverKeeper().SetTSS(ctx, observertypes.TSS{ + TssPubkey: tssPubKey, + }) + k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ + Tss: tssPubKey, + NonceLow: 0, + NonceHigh: 1, + ChainId: chainId, + }) + cctx := types.CrossChainTx{ + Creator: "any", + Index: "0x123", + CctxStatus: &types.Status{ + Status: types.CctxStatus_PendingOutbound, + }, + } + k.SetCrossChainTx(ctx, cctx) + k.GetObserverKeeper().SetNonceToCctx(ctx, observertypes.NonceToCctx{ + ChainId: chainId, + Nonce: nonce, + CctxIndex: "0x123", + Tss: "zetapub1addwnpepq28c57cvcs0a2htsem5zxr6qnlvq9mzhmm76z3jncsnzz32rclangr2g35p", + }) +} + func TestMsgServer_AddToOutTxTracker(t *testing.T) { t.Run("add tracker admin", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + chainID := getEthereumChainID() txIndex, block, header, headerRLP, _, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, 0) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: admin, ChainId: chainID, @@ -42,15 +79,22 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { _, found := k.GetOutTxTracker(ctx, chainID, 0) require.True(t, found) }) + t.Run("unable to add tracker admin exceeding maximum allowed length of hashlist without proof", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + chainID := getEthereumChainID() txIndex, block, header, headerRLP, _, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, 0) + k.SetOutTxTracker(ctx, types.OutTxTracker{ ChainId: chainID, Nonce: 0, @@ -67,7 +111,9 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { }, }, }) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: admin, ChainId: chainID, @@ -85,12 +131,16 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { t.Run("fail add proof based tracker with wrong chainID", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getEthereumChainID() + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, int64(tx.Nonce())) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), ChainId: 97, @@ -107,12 +157,16 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { t.Run("fail add proof based tracker with wrong nonce", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getEthereumChainID() + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, 1) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -129,11 +183,14 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { t.Run("fail add proof based tracker with wrong tx_hash", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getEthereumChainID() + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, int64(tx.Nonce())) + msgServer := keeper.NewMsgServerImpl(*k) _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), @@ -150,13 +207,17 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { }) t.Run("fail proof based tracker with incorrect proof", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) chainID := getEthereumChainID() + txIndex, block, header, headerRLP, _, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, int64(tx.Nonce())) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -170,14 +231,18 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { _, found := k.GetOutTxTracker(ctx, chainID, tx.Nonce()) require.False(t, found) }) + t.Run("add proof based tracker with correct proof", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getEthereumChainID() txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) setupTssAndNonceToCctx(k, ctx, chainID, int64(tx.Nonce())) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -191,9 +256,12 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { _, found := k.GetOutTxTracker(ctx, chainID, tx.Nonce()) require.True(t, found) }) + t.Run("add proven txHash even if length of hashList is already 2", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getEthereumChainID() + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) @@ -214,7 +282,9 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { }, }, }) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -233,9 +303,12 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { require.False(t, tracker.HashList[1].Proved) require.False(t, tracker.HashList[2].Proved) }) + t.Run("add proof for existing txHash", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getEthereumChainID() + txIndex, block, header, headerRLP, proof, tx, err := sample.Proof() require.NoError(t, err) setupVerificationParams(zk, ctx, txIndex, chainID, header, headerRLP, block) @@ -254,7 +327,9 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { tracker, found := k.GetOutTxTracker(ctx, chainID, tx.Nonce()) require.True(t, found) require.False(t, tracker.HashList[0].Proved) + msgServer := keeper.NewMsgServerImpl(*k) + _, err = msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ Creator: sample.AccAddress(), ChainId: chainID, @@ -271,31 +346,3 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { require.True(t, tracker.HashList[0].Proved) }) } - -func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce int64) { - - tssPubKey := "zetapub1addwnpepq28c57cvcs0a2htsem5zxr6qnlvq9mzhmm76z3jncsnzz32rclangr2g35p" - k.GetObserverKeeper().SetTSS(ctx, observertypes.TSS{ - TssPubkey: tssPubKey, - }) - k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ - Tss: tssPubKey, - NonceLow: 0, - NonceHigh: 1, - ChainId: chainId, - }) - cctx := types.CrossChainTx{ - Creator: "any", - Index: "0x123", - CctxStatus: &types.Status{ - Status: types.CctxStatus_PendingOutbound, - }, - } - k.SetCrossChainTx(ctx, cctx) - k.GetObserverKeeper().SetNonceToCctx(ctx, observertypes.NonceToCctx{ - ChainId: chainId, - Nonce: nonce, - CctxIndex: "0x123", - Tss: "zetapub1addwnpepq28c57cvcs0a2htsem5zxr6qnlvq9mzhmm76z3jncsnzz32rclangr2g35p", - }) -} diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index a552d0feb4..96db6a964d 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -13,45 +13,57 @@ import ( tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -// Authorized: admin policy group 2. +// MigrateTssFunds migrates the funds from the current TSS to the new TSS func (k msgServer) MigrateTssFunds(goCtx context.Context, msg *types.MsgMigrateTssFunds) (*types.MsgMigrateTssFundsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group2) { + + // check if authorized + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Update can only be executed by the correct policy account") } + if k.zetaObserverKeeper.IsInboundEnabled(ctx) { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "cannot migrate funds while inbound is enabled") } + tss, found := k.zetaObserverKeeper.GetTSS(ctx) if !found { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "cannot find current TSS") } + tssHistory := k.zetaObserverKeeper.GetAllTSS(ctx) sort.SliceStable(tssHistory, func(i, j int) bool { return tssHistory[i].FinalizedZetaHeight < tssHistory[j].FinalizedZetaHeight }) + if tss.TssPubkey == tssHistory[len(tssHistory)-1].TssPubkey { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "no new tss address has been generated") } + // This check is to deal with an edge case where the current TSS is not part of the TSS history list at all if tss.FinalizedZetaHeight >= tssHistory[len(tssHistory)-1].FinalizedZetaHeight { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "current tss is the latest") } + pendingNonces, found := k.GetObserverKeeper().GetPendingNonces(ctx, tss.TssPubkey, msg.ChainId) if !found { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "cannot find pending nonces for chain") } + if pendingNonces.NonceLow != pendingNonces.NonceHigh { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "cannot migrate funds when there are pending nonces") } + err := k.MigrateTSSFundsForChain(ctx, msg.ChainId, msg.Amount, tss, tssHistory) if err != nil { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, err.Error()) } + return &types.MsgMigrateTssFundsResponse{}, nil } diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index 4d909abdf0..8eadd25049 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -10,16 +10,82 @@ import ( "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +func setupTssMigrationParams( + zk keepertest.ZetaKeepers, + k *keeper.Keeper, + ctx sdk.Context, + chain common.Chain, + amount sdkmath.Uint, + setNewTss bool, + setCurrentTSS bool, +) (string, string) { + zk.ObserverKeeper.SetCrosschainFlags(ctx, observertypes.CrosschainFlags{ + IsInboundEnabled: false, + IsOutboundEnabled: true, + }) + + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{ + { + ChainId: chain.ChainId, + BallotThreshold: sdk.NewDec(0), + MinObserverDelegation: sdk.OneDec(), + IsSupported: true, + }, + }, + }) + + currentTss := sample.Tss() + newTss := sample.Tss() + newTss.FinalizedZetaHeight = currentTss.FinalizedZetaHeight + 1 + newTss.KeyGenZetaHeight = currentTss.KeyGenZetaHeight + 1 + k.GetObserverKeeper().SetTSS(ctx, currentTss) + if setCurrentTSS { + k.GetObserverKeeper().SetTSSHistory(ctx, currentTss) + } + if setNewTss { + k.GetObserverKeeper().SetTSSHistory(ctx, newTss) + } + k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ + NonceLow: 1, + NonceHigh: 1, + ChainId: chain.ChainId, + Tss: currentTss.TssPubkey, + }) + k.SetGasPrice(ctx, crosschaintypes.GasPrice{ + Creator: "", + Index: "", + ChainId: chain.ChainId, + Signers: nil, + BlockNums: nil, + Prices: []uint64{100000, 100000, 100000}, + MedianIndex: 1, + }) + k.GetObserverKeeper().SetChainNonces(ctx, observertypes.ChainNonces{ + Index: chain.ChainName.String(), + ChainId: chain.ChainId, + Nonce: 1, + }) + indexString := keeper.GetIndexStringForTssMigration(currentTss.TssPubkey, newTss.TssPubkey, chain.ChainId, amount, ctx.BlockHeight()) + return indexString, currentTss.TssPubkey +} + func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { t.Run("test gas price multiplier", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("10000000000000000000") @@ -42,11 +108,17 @@ func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { }) } + func TestMsgServer_MigrateTssFunds(t *testing.T) { t.Run("successfully create tss migration cctx", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("10000000000000000000") @@ -65,10 +137,16 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { Mul(sdkmath.NewUintFromString(cctx.GetCurrentOutTxParam().OutboundTxGasPrice)) require.Equal(t, cctx.GetCurrentOutTxParam().Amount.String(), amount.Sub(feeCalculated).String()) }) + t.Run("not enough funds in tss address for migration", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("100") @@ -84,10 +162,16 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { _, found := k.GetCrossChainTx(ctx, index) require.False(t, found) }) + t.Run("unable to migrate funds if new TSS is not created ", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("10000000000000000000") @@ -103,10 +187,16 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { _, found := k.GetCrossChainTx(ctx, index) require.False(t, found) }) + t.Run("unable to migrate funds when nonce low does not match nonce high", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("10000000000000000000") @@ -129,10 +219,16 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { _, found := k.GetCrossChainTx(ctx, index) require.False(t, found) }) + t.Run("unable to migrate funds when a pending cctx is presnt in migration info", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("10000000000000000000") @@ -166,9 +262,14 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { }) t.Run("unable to migrate funds if current TSS is not present in TSSHistory and no new TSS has been generated", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) chain := getValidEthChain(t) amount := sdkmath.NewUintFromString("10000000000000000000") @@ -192,62 +293,3 @@ func TestMsgServer_MigrateTssFunds(t *testing.T) { require.False(t, found) }) } -func setupTssMigrationParams( - zk keepertest.ZetaKeepers, - k *keeper.Keeper, - ctx sdk.Context, - chain common.Chain, - amount sdkmath.Uint, - setNewTss bool, - setCurrentTSS bool, -) (string, string) { - zk.ObserverKeeper.SetCrosschainFlags(ctx, observertypes.CrosschainFlags{ - IsInboundEnabled: false, - IsOutboundEnabled: true, - }) - - zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ - ChainParams: []*observertypes.ChainParams{ - { - ChainId: chain.ChainId, - BallotThreshold: sdk.NewDec(0), - MinObserverDelegation: sdk.OneDec(), - IsSupported: true, - }, - }, - }) - - currentTss := sample.Tss() - newTss := sample.Tss() - newTss.FinalizedZetaHeight = currentTss.FinalizedZetaHeight + 1 - newTss.KeyGenZetaHeight = currentTss.KeyGenZetaHeight + 1 - k.GetObserverKeeper().SetTSS(ctx, currentTss) - if setCurrentTSS { - k.GetObserverKeeper().SetTSSHistory(ctx, currentTss) - } - if setNewTss { - k.GetObserverKeeper().SetTSSHistory(ctx, newTss) - } - k.GetObserverKeeper().SetPendingNonces(ctx, observertypes.PendingNonces{ - NonceLow: 1, - NonceHigh: 1, - ChainId: chain.ChainId, - Tss: currentTss.TssPubkey, - }) - k.SetGasPrice(ctx, crosschaintypes.GasPrice{ - Creator: "", - Index: "", - ChainId: chain.ChainId, - Signers: nil, - BlockNums: nil, - Prices: []uint64{100000, 100000, 100000}, - MedianIndex: 1, - }) - k.GetObserverKeeper().SetChainNonces(ctx, observertypes.ChainNonces{ - Index: chain.ChainName.String(), - ChainId: chain.ChainId, - Nonce: 1, - }) - indexString := keeper.GetIndexStringForTssMigration(currentTss.TssPubkey, newTss.TssPubkey, chain.ChainId, amount, ctx.BlockHeight()) - return indexString, currentTss.TssPubkey -} diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx.go b/x/crosschain/keeper/msg_server_refund_aborted_tx.go index b011c35163..fc651ec249 100644 --- a/x/crosschain/keeper/msg_server_refund_aborted_tx.go +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "golang.org/x/net/context" @@ -22,7 +23,7 @@ func (k msgServer) RefundAbortedCCTX(goCtx context.Context, msg *types.MsgRefund ctx := sdk.UnwrapSDKContext(goCtx) // check if authorized - if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, observertypes.ErrNotAuthorized } diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go index 3cc1285863..1c6f7da680 100644 --- a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go @@ -10,6 +10,7 @@ import ( "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" @@ -37,10 +38,15 @@ func Test_GetRefundAddress(t *testing.T) { } func TestMsgServer_RefundAbortedCCTX(t *testing.T) { t.Run("successfully refund tx for coin-type gas", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -69,11 +75,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("successfully refund tx for coin-type zeta", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -102,11 +114,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("successfully refund tx to inbound amount if outbound is not found for coin-type zeta", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -136,11 +154,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("successfully refund to optional refund address if provided", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -169,12 +193,18 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("successfully refund tx for coin-type ERC20", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) asset := sample.EthAddress().String() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -213,11 +243,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("successfully refund tx for coin-type Gas with BTC sender", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidBtcChainID() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -246,11 +282,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("fail refund if address provided is invalid", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -271,11 +313,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) require.ErrorContains(t, err, "invalid refund address") }) + t.Run("fail refund if address provided is null ", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -296,11 +344,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) require.ErrorContains(t, err, "invalid refund address") }) + t.Run("fail refund if status is not aborted", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -323,11 +377,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, found) require.False(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("fail refund if status cctx not found", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -346,11 +406,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) require.ErrorContains(t, err, "cannot find cctx") }) + t.Run("fail refund if refund address not provided", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidBtcChainID() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -371,11 +437,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) require.ErrorContains(t, err, "refund address is required") }) + t.Run("fail refund tx for coin-type Zeta if zeta accounting object is not present", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -395,11 +467,17 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { }) require.ErrorContains(t, err, "unable to find zeta accounting") }) + t.Run("fail refund if non admin account is the creator", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() chainID := getValidEthChainID(t) - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -414,7 +492,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { _ = setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, cctx.InboundTxParams.SenderChainId, "foobar", "foobar") _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ - Creator: sample.AccAddress(), + Creator: admin, CctxIndex: cctx.Index, RefundAddress: cctx.InboundTxParams.Sender, }) diff --git a/x/crosschain/keeper/msg_server_remove_out_tx_tracker.go b/x/crosschain/keeper/msg_server_remove_out_tx_tracker.go index 33b6784ff9..0d4bb23ac5 100644 --- a/x/crosschain/keeper/msg_server_remove_out_tx_tracker.go +++ b/x/crosschain/keeper/msg_server_remove_out_tx_tracker.go @@ -4,6 +4,7 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -13,7 +14,7 @@ import ( // Authorized: admin policy group 1. func (k msgServer) RemoveFromOutTxTracker(goCtx context.Context, msg *types.MsgRemoveFromOutTxTracker) (*types.MsgRemoveFromOutTxTrackerResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group1) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupEmergency) { return &types.MsgRemoveFromOutTxTrackerResponse{}, observertypes.ErrNotAuthorizedPolicy } diff --git a/x/crosschain/keeper/msg_server_update_tss.go b/x/crosschain/keeper/msg_server_update_tss.go index b6c2198278..be9c6a8595 100644 --- a/x/crosschain/keeper/msg_server_update_tss.go +++ b/x/crosschain/keeper/msg_server_update_tss.go @@ -3,20 +3,25 @@ package keeper import ( "context" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/zeta-chain/zetacore/x/crosschain/types" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" ) -// Authorized: admin policy group 2. +// UpdateTssAddress updates the TSS address. func (k msgServer) UpdateTssAddress(goCtx context.Context, msg *types.MsgUpdateTssAddress) (*types.MsgUpdateTssAddressResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) + + // check if authorized // TODO : Add a new policy type for updating the TSS address - if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observerTypes.Policy_Type_group2) { + // https://github.com/zeta-chain/node/issues/1715 + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Update can only be executed by the correct policy account") } + currentTss, found := k.zetaObserverKeeper.GetTSS(ctx) if !found { return nil, errorsmod.Wrap(types.ErrUnableToUpdateTss, "cannot find current TSS") @@ -24,6 +29,7 @@ func (k msgServer) UpdateTssAddress(goCtx context.Context, msg *types.MsgUpdateT if currentTss.TssPubkey == msg.TssPubkey { return nil, errorsmod.Wrap(types.ErrUnableToUpdateTss, "no new tss address has been generated") } + tss, ok := k.zetaObserverKeeper.CheckIfTssPubkeyHasBeenGenerated(ctx, msg.TssPubkey) if !ok { return nil, errorsmod.Wrap(types.ErrUnableToUpdateTss, "tss pubkey has not been generated") @@ -34,6 +40,7 @@ func (k msgServer) UpdateTssAddress(goCtx context.Context, msg *types.MsgUpdateT if len(k.zetaObserverKeeper.GetSupportedChains(ctx)) != len(tssMigrators) { return nil, errorsmod.Wrap(types.ErrUnableToUpdateTss, "cannot update tss address not enough migrations have been created and completed") } + // GetAllTssFundMigrators would return the migrators created for the current migration // if any of the migrations is still pending we should not allow the tss address to be updated // we can wait for all migrations to complete before updating; this includes btc and eth chains. @@ -50,6 +57,7 @@ func (k msgServer) UpdateTssAddress(goCtx context.Context, msg *types.MsgUpdateT } k.GetObserverKeeper().SetTssAndUpdateNonce(ctx, tss) + // Remove all migrators once the tss address has been updated successfully, // A new set of migrators will be created when the next migration is triggered k.zetaObserverKeeper.RemoveAllExistingMigrators(ctx) diff --git a/x/crosschain/keeper/msg_server_update_tss_test.go b/x/crosschain/keeper/msg_server_update_tss_test.go index 6da9638343..0020cb32f8 100644 --- a/x/crosschain/keeper/msg_server_update_tss_test.go +++ b/x/crosschain/keeper/msg_server_update_tss_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/stretchr/testify/require" - keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/keeper" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/x/observer/types" @@ -14,9 +14,14 @@ import ( func TestMsgServer_UpdateTssAddress(t *testing.T) { t.Run("successfully update tss address", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) tssOld := sample.Tss() tssNew := sample.Tss() @@ -47,9 +52,14 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { }) t.Run("new tss has not been added to tss history", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) tssOld := sample.Tss() tssNew := sample.Tss() @@ -79,9 +89,14 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { }) t.Run("old tss pubkey provided", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) tssOld := sample.Tss() k.GetObserverKeeper().SetTSSHistory(ctx, tssOld) @@ -110,9 +125,14 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { }) t.Run("unable to update tss when not enough migrators are present", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) tssOld := sample.Tss() tssNew := sample.Tss() @@ -148,9 +168,14 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { }) t.Run("unable to update tss when pending cctx is present", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) tssOld := sample.Tss() tssNew := sample.Tss() @@ -185,9 +210,14 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { }) t.Run("unable to update tss cctx is not present", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + msgServer := keeper.NewMsgServerImpl(*k) tssOld := sample.Tss() tssNew := sample.Tss() diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index cadd5b51e0..e269f2312a 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" @@ -16,7 +18,6 @@ import ( "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // WhitelistERC20 deploys a new zrc20, create a foreign coin object for the ERC20 @@ -25,9 +26,12 @@ import ( // Authorized: admin policy group 1. func (k msgServer) WhitelistERC20(goCtx context.Context, msg *types.MsgWhitelistERC20) (*types.MsgWhitelistERC20Response, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + + // check if authorized + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Deploy can only be executed by the correct policy account") } + erc20Addr := ethcommon.HexToAddress(msg.Erc20Address) if erc20Addr == (ethcommon.Address{}) { return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid ERC20 contract address (%s)", msg.Erc20Address) diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20_test.go b/x/crosschain/keeper/msg_server_whitelist_erc20_test.go index 82cd292fb2..7f13a25611 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20_test.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -17,14 +19,19 @@ import ( func TestKeeper_WhitelistERC20(t *testing.T) { t.Run("can deploy and whitelist an erc20", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) chainID := getValidEthChainID(t) setSupportedChain(ctx, zk, chainID) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "FOOBAR") @@ -65,6 +72,8 @@ func TestKeeper_WhitelistERC20(t *testing.T) { require.NoError(t, err) require.Equal(t, uint64(100000), gasLimit.Uint64()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // Ensure that whitelist a new erc20 create a cctx with a different index res, err = msgServer.WhitelistERC20(ctx, &types.MsgWhitelistERC20{ Creator: admin, @@ -81,12 +90,19 @@ func TestKeeper_WhitelistERC20(t *testing.T) { }) t.Run("should fail if not authorized", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + _, err := msgServer.WhitelistERC20(ctx, &types.MsgWhitelistERC20{ - Creator: sample.AccAddress(), + Creator: admin, Erc20Address: sample.EthAddress().Hex(), ChainId: getValidEthChainID(t), Name: "foo", @@ -98,12 +114,16 @@ func TestKeeper_WhitelistERC20(t *testing.T) { }) t.Run("should fail if invalid erc20 address", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := msgServer.WhitelistERC20(ctx, &types.MsgWhitelistERC20{ Creator: admin, @@ -118,12 +138,16 @@ func TestKeeper_WhitelistERC20(t *testing.T) { }) t.Run("should fail if foreign coin already exists for the asset", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) chainID := getValidEthChainID(t) asset := sample.EthAddress().Hex() @@ -145,13 +169,17 @@ func TestKeeper_WhitelistERC20(t *testing.T) { }) t.Run("should fail if no tss set", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) chainID := getValidEthChainID(t) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) erc20Address := sample.EthAddress().Hex() _, err := msgServer.WhitelistERC20(ctx, &types.MsgWhitelistERC20{ @@ -167,12 +195,17 @@ func TestKeeper_WhitelistERC20(t *testing.T) { }) t.Run("should fail if nox valid chain ID", func(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + msgServer := crosschainkeeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + k.GetObserverKeeper().SetTssAndUpdateNonce(ctx, sample.Tss()) erc20Address := sample.EthAddress().Hex() diff --git a/x/crosschain/keeper/utils_test.go b/x/crosschain/keeper/utils_test.go index aed660b70b..141cfd3e00 100644 --- a/x/crosschain/keeper/utils_test.go +++ b/x/crosschain/keeper/utils_test.go @@ -215,22 +215,6 @@ func setupZRC20Pool( require.NoError(t, err) } -// setAdminPolicies sets the admin policies for the observer module with group 1 and 2 -func setAdminPolicies(ctx sdk.Context, zk testkeeper.ZetaKeepers, admin string) { - params := zk.ObserverKeeper.GetParams(ctx) - params.AdminPolicy = []*observertypes.Admin_Policy{ - { - PolicyType: observertypes.Policy_Type_group1, - Address: admin, - }, - { - PolicyType: observertypes.Policy_Type_group2, - Address: admin, - }, - } - zk.ObserverKeeper.SetParams(ctx, params) -} - // setSupportedChain sets the supported chains for the observer module func setSupportedChain(ctx sdk.Context, zk testkeeper.ZetaKeepers, chainIDs ...int64) { chainParamsList := make([]*observertypes.ChainParams, len(chainIDs)) diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index d8d73487fa..4286bbf48b 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -10,7 +10,7 @@ import ( eth "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/zeta-chain/zetacore/common" - + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -162,3 +162,7 @@ type FungibleKeeper interface { FundGasStabilityPool(ctx sdk.Context, chainID int64, amount *big.Int) error WithdrawFromGasStabilityPool(ctx sdk.Context, chainID int64, amount *big.Int) error } + +type AuthorityKeeper interface { + IsAuthorized(ctx sdk.Context, address string, policyType authoritytypes.PolicyType) bool +} diff --git a/x/fungible/keeper/keeper.go b/x/fungible/keeper/keeper.go index 13fc4ba566..b25e466099 100644 --- a/x/fungible/keeper/keeper.go +++ b/x/fungible/keeper/keeper.go @@ -8,20 +8,20 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/tendermint/tendermint/libs/log" - "github.com/zeta-chain/zetacore/x/fungible/types" ) type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey - paramstore paramtypes.Subspace - authKeeper types.AccountKeeper - evmKeeper types.EVMKeeper - bankKeeper types.BankKeeper - observerKeeper types.ObserverKeeper + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey + paramstore paramtypes.Subspace + authKeeper types.AccountKeeper + evmKeeper types.EVMKeeper + bankKeeper types.BankKeeper + observerKeeper types.ObserverKeeper + authorityKeeper types.AuthorityKeeper } ) @@ -34,6 +34,7 @@ func NewKeeper( evmKeeper types.EVMKeeper, bankKeeper types.BankKeeper, observerKeeper types.ObserverKeeper, + authorityKeeper types.AuthorityKeeper, ) *Keeper { // set KeyTable if it has not already been set if !ps.HasKeyTable() { @@ -41,14 +42,15 @@ func NewKeeper( } return &Keeper{ - cdc: cdc, - storeKey: storeKey, - memKey: memKey, - paramstore: ps, - authKeeper: authKeeper, - evmKeeper: evmKeeper, - bankKeeper: bankKeeper, - observerKeeper: observerKeeper, + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + paramstore: ps, + authKeeper: authKeeper, + evmKeeper: evmKeeper, + bankKeeper: bankKeeper, + observerKeeper: observerKeeper, + authorityKeeper: authorityKeeper, } } @@ -71,3 +73,7 @@ func (k Keeper) GetBankKeeper() types.BankKeeper { func (k Keeper) GetObserverKeeper() types.ObserverKeeper { return k.observerKeeper } + +func (k Keeper) GetAuthorityKeeper() types.AuthorityKeeper { + return k.authorityKeeper +} diff --git a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go index a490f060c6..dece8b65de 100644 --- a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go +++ b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go @@ -5,13 +5,12 @@ import ( "math/big" cosmoserrors "cosmossdk.io/errors" - "github.com/ethereum/go-ethereum/common" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common" zetacommon "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // DeployFungibleCoinZRC20 deploys a fungible coin from a connected chains as a ZRC20 on ZetaChain. @@ -42,7 +41,7 @@ func (k msgServer) DeployFungibleCoinZRC20(goCtx context.Context, msg *types.Msg return nil, err } - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserrors.Wrap(sdkerrors.ErrUnauthorized, "Deploy can only be executed by the correct policy account") } diff --git a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20_test.go b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20_test.go index 1e64419f1a..0413c6d239 100644 --- a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20_test.go +++ b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20_test.go @@ -10,6 +10,7 @@ import ( "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -17,11 +18,16 @@ import ( func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { t.Run("can deploy a new zrc20", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + chainID := getValidChainID(t) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -55,6 +61,8 @@ func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { require.NoError(t, err) require.Equal(t, gasAddress, gas.Hex()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can deploy non-gas zrc20 res, err = msgServer.DeployFungibleCoinZRC20(ctx, types.NewMsgDeployFungibleCoinZRC20( admin, @@ -87,15 +95,21 @@ func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { }) t.Run("should not deploy a new zrc20 if not admin", func(t *testing.T) { - k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) chainID := getValidChainID(t) + admin := sample.AccAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) // should not deploy a new zrc20 if not admin _, err := keeper.NewMsgServerImpl(*k).DeployFungibleCoinZRC20(ctx, types.NewMsgDeployFungibleCoinZRC20( - sample.AccAddress(), + admin, sample.EthAddress().Hex(), chainID, 8, @@ -109,10 +123,13 @@ func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { }) t.Run("should not deploy a new zrc20 with wrong decimal", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + chainID := getValidChainID(t) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -133,10 +150,14 @@ func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { }) t.Run("should not deploy a new zrc20 with invalid chain ID", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -156,10 +177,14 @@ func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { }) t.Run("should not deploy an existing gas or erc20 contract", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + chainID := getValidChainID(t) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -175,17 +200,27 @@ func TestMsgServer_DeployFungibleCoinZRC20(t *testing.T) { 1000000, ) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // Attempt to deploy the same gas token twice should result in error _, err := keeper.NewMsgServerImpl(*k).DeployFungibleCoinZRC20(ctx, deployMsg) require.NoError(t, err) + + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + _, err = keeper.NewMsgServerImpl(*k).DeployFungibleCoinZRC20(ctx, deployMsg) require.Error(t, err) require.ErrorIs(t, err, types.ErrForeignCoinAlreadyExist) // Similar to above, redeploying existing erc20 should also fail deployMsg.CoinType = common.CoinType_ERC20 + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + _, err = keeper.NewMsgServerImpl(*k).DeployFungibleCoinZRC20(ctx, deployMsg) require.NoError(t, err) + + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + _, err = keeper.NewMsgServerImpl(*k).DeployFungibleCoinZRC20(ctx, deployMsg) require.Error(t, err) require.ErrorIs(t, err, types.ErrForeignCoinAlreadyExist) diff --git a/x/fungible/keeper/msg_server_deploy_system_contract.go b/x/fungible/keeper/msg_server_deploy_system_contract.go index c849ea1dee..a53a97f774 100644 --- a/x/fungible/keeper/msg_server_deploy_system_contract.go +++ b/x/fungible/keeper/msg_server_deploy_system_contract.go @@ -6,8 +6,8 @@ import ( cosmoserror "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // DeploySystemContracts deploy new instances of the system contracts @@ -16,7 +16,7 @@ import ( func (k msgServer) DeploySystemContracts(goCtx context.Context, msg *types.MsgDeploySystemContracts) (*types.MsgDeploySystemContractsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserror.Wrap(sdkerrors.ErrUnauthorized, "System contract deployment can only be executed by the correct policy account") } diff --git a/x/fungible/keeper/msg_server_deploy_system_contract_test.go b/x/fungible/keeper/msg_server_deploy_system_contract_test.go index 86b9745021..9f149b4841 100644 --- a/x/fungible/keeper/msg_server_deploy_system_contract_test.go +++ b/x/fungible/keeper/msg_server_deploy_system_contract_test.go @@ -4,25 +4,29 @@ import ( "errors" "testing" - "github.com/stretchr/testify/mock" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestMsgServer_DeploySystemContracts(t *testing.T) { t.Run("admin can deploy system contracts", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) res, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(admin)) require.NoError(t, err) @@ -35,24 +39,32 @@ func TestMsgServer_DeploySystemContracts(t *testing.T) { }) t.Run("non-admin cannot deploy system contracts", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) nonadmin := sample.AccAddress() - setAdminPolicies(ctx, zk, nonadmin, observertypes.Policy_Type_group1) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, nonadmin, authoritytypes.PolicyType_groupAdmin, false) _, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(nonadmin)) require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) }) t.Run("should fail if contract deployment fails", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ - UseEVMMock: true, + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, }) msgServer := keeper.NewMsgServerImpl(*k) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) mockEVMKeeper.On( diff --git a/x/fungible/keeper/msg_server_remove_foreign_coin.go b/x/fungible/keeper/msg_server_remove_foreign_coin.go index a66f02a9c1..6fdecc7960 100644 --- a/x/fungible/keeper/msg_server_remove_foreign_coin.go +++ b/x/fungible/keeper/msg_server_remove_foreign_coin.go @@ -6,8 +6,8 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // RemoveForeignCoin removes a coin from the list of foreign coins in the @@ -16,7 +16,7 @@ import ( // Authorized: admin policy group 2. func (k msgServer) RemoveForeignCoin(goCtx context.Context, msg *types.MsgRemoveForeignCoin) (*types.MsgRemoveForeignCoinResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserrors.Wrap(sdkerrors.ErrUnauthorized, "Removal can only be executed by the correct policy account") } index := msg.Name diff --git a/x/fungible/keeper/msg_server_remove_foreign_coin_test.go b/x/fungible/keeper/msg_server_remove_foreign_coin_test.go index fedfe7315b..49bddf3521 100644 --- a/x/fungible/keeper/msg_server_remove_foreign_coin_test.go +++ b/x/fungible/keeper/msg_server_remove_foreign_coin_test.go @@ -7,18 +7,24 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestMsgServer_RemoveForeignCoin(t *testing.T) { t.Run("can remove a foreign coin", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + chainID := getValidChainID(t) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -34,25 +40,37 @@ func TestMsgServer_RemoveForeignCoin(t *testing.T) { }) t.Run("should fail if not admin", func(t *testing.T) { - k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) chainID := getValidChainID(t) + admin := sample.AccAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foo", "foo") - _, err := msgServer.RemoveForeignCoin(ctx, types.NewMsgRemoveForeignCoin(sample.AccAddress(), zrc20.Hex())) + _, err := msgServer.RemoveForeignCoin(ctx, types.NewMsgRemoveForeignCoin(admin, zrc20.Hex())) require.Error(t, err) require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) }) t.Run("should fail if not found", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := msgServer.RemoveForeignCoin(ctx, types.NewMsgRemoveForeignCoin(admin, sample.EthAddress().Hex())) require.Error(t, err) diff --git a/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap.go b/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap.go index cebf1632f7..f3df975ede 100644 --- a/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap.go +++ b/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap.go @@ -6,8 +6,8 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // UpdateZRC20LiquidityCap updates the liquidity cap for a ZRC20 token. @@ -17,7 +17,7 @@ func (k msgServer) UpdateZRC20LiquidityCap(goCtx context.Context, msg *types.Msg ctx := sdk.UnwrapSDKContext(goCtx) // check authorization - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserrors.Wrap(sdkerrors.ErrUnauthorized, "update can only be executed by group 2 policy group") } diff --git a/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap_test.go b/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap_test.go index ea51fabc14..dc0b64c29a 100644 --- a/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap_test.go +++ b/x/fungible/keeper/msg_server_udpate_zrc20_liquidity_cap_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "testing" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "cosmossdk.io/math" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/stretchr/testify/require" @@ -10,12 +12,14 @@ import ( "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { t.Run("can update the liquidity cap of zrc20", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() coinAddress := sample.EthAddress().String() @@ -24,7 +28,8 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { foreignCoin.LiquidityCap = math.Uint{} k.SetForeignCoins(ctx, foreignCoin) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // can update liquidity cap _, err := msgServer.UpdateZRC20LiquidityCap(ctx, types.NewMsgUpdateZRC20LiquidityCap( @@ -38,6 +43,8 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { require.True(t, found) require.True(t, coin.LiquidityCap.Equal(math.NewUint(42)), "invalid liquidity cap", coin.LiquidityCap.String()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can update liquidity cap again _, err = msgServer.UpdateZRC20LiquidityCap(ctx, types.NewMsgUpdateZRC20LiquidityCap( admin, @@ -50,6 +57,8 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { require.True(t, found) require.True(t, coin.LiquidityCap.Equal(math.NewUint(4200000)), "invalid liquidity cap", coin.LiquidityCap.String()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can set liquidity cap to 0 _, err = msgServer.UpdateZRC20LiquidityCap(ctx, types.NewMsgUpdateZRC20LiquidityCap( admin, @@ -62,6 +71,8 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { require.True(t, found) require.True(t, coin.LiquidityCap.Equal(math.ZeroUint()), "invalid liquidity cap", coin.LiquidityCap.String()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can set liquidity cap to nil _, err = msgServer.UpdateZRC20LiquidityCap(ctx, types.NewMsgUpdateZRC20LiquidityCap( admin, @@ -76,7 +87,10 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { }) t.Run("should fail if not admin", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() coinAddress := sample.EthAddress().String() @@ -85,10 +99,11 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { foreignCoin.LiquidityCap = math.Uint{} k.SetForeignCoins(ctx, foreignCoin) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) _, err := msgServer.UpdateZRC20LiquidityCap(ctx, types.NewMsgUpdateZRC20LiquidityCap( - sample.AccAddress(), + admin, coinAddress, math.NewUint(42), )) @@ -97,12 +112,16 @@ func TestMsgServer_UpdateZRC20LiquidityCap(t *testing.T) { }) t.Run("should fail if zrc20 does not exist", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() coinAddress := sample.EthAddress().String() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := msgServer.UpdateZRC20LiquidityCap(ctx, types.NewMsgUpdateZRC20LiquidityCap( admin, diff --git a/x/fungible/keeper/msg_server_update_contract_bytecode.go b/x/fungible/keeper/msg_server_update_contract_bytecode.go index acfd4acba1..b3c63b30f3 100644 --- a/x/fungible/keeper/msg_server_update_contract_bytecode.go +++ b/x/fungible/keeper/msg_server_update_contract_bytecode.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) // UpdateContractBytecode updates the bytecode of a contract from the bytecode @@ -22,7 +22,7 @@ func (k msgServer) UpdateContractBytecode(goCtx context.Context, msg *types.MsgU ctx := sdk.UnwrapSDKContext(goCtx) // check authorization - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserror.Wrap(sdkerrors.ErrUnauthorized, "Deploy can only be executed by the correct policy account") } diff --git a/x/fungible/keeper/msg_server_update_contract_bytecode_test.go b/x/fungible/keeper/msg_server_update_contract_bytecode_test.go index cb8e894eb0..530d23b306 100644 --- a/x/fungible/keeper/msg_server_update_contract_bytecode_test.go +++ b/x/fungible/keeper/msg_server_update_contract_bytecode_test.go @@ -5,32 +5,20 @@ import ( "math/big" "testing" - ethcommon "github.com/ethereum/go-ethereum/common" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/evmos/ethermint/x/evm/statedb" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" zetacommon "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -func setAdminPolicies(ctx sdk.Context, zk keepertest.ZetaKeepers, admin string, policyType observertypes.Policy_Type) { - zk.ObserverKeeper.SetParams(ctx, observertypes.Params{ - AdminPolicy: []*observertypes.Admin_Policy{ - { - PolicyType: policyType, - Address: admin, - }, - }, - }) -} - func codeHashFromAddress(t *testing.T, ctx sdk.Context, k *keeper.Keeper, contractAddr string) string { res, err := k.CodeHash(ctx, &types.QueryCodeHashRequest{ Address: contractAddr, @@ -41,13 +29,14 @@ func codeHashFromAddress(t *testing.T, ctx sdk.Context, k *keeper.Keeper, contra func TestKeeper_UpdateContractBytecode(t *testing.T) { t.Run("can update the bytecode from another contract", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() msgServer := keeper.NewMsgServerImpl(*k) - // set admin policy - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) // sample chainIDs and addresses chainList := zetacommon.DefaultChainsList() @@ -104,6 +93,8 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { require.NoError(t, err) codeHash := codeHashFromAddress(t, ctx, k, newCodeAddress.Hex()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // update the bytecode _, err = msgServer.UpdateContractBytecode(ctx, types.NewMsgUpdateContractBytecode( admin, @@ -147,6 +138,9 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { ) codeHash = codeHashFromAddress(t, ctx, k, newCodeAddress.Hex()) require.NoError(t, err) + + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + _, err = msgServer.UpdateContractBytecode(ctx, types.NewMsgUpdateContractBytecode( admin, zrc20.Hex(), @@ -165,13 +159,16 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("can update the bytecode of the wzeta connector contract", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() msgServer := keeper.NewMsgServerImpl(*k) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + // deploy a connector - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) wzeta, _, _, oldConnector, _ := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) codeHash := codeHashFromAddress(t, ctx, k, oldConnector.Hex()) @@ -181,6 +178,8 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { require.NotEmpty(t, newConnector) assertContractDeployment(t, sdkk.EvmKeeper, ctx, newConnector) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can update the bytecode of the new connector with the old connector contract _, err = msgServer.UpdateContractBytecode(ctx, types.NewMsgUpdateContractBytecode( admin, @@ -191,11 +190,17 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("should fail if unauthorized", func(t *testing.T) { - k, ctx, _, _ := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) _, err := msgServer.UpdateContractBytecode(ctx, types.NewMsgUpdateContractBytecode( - sample.AccAddress(), + admin, sample.EthAddress().Hex(), sample.Hash().Hex(), )) @@ -203,11 +208,14 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("should fail invalid contract address", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := msgServer.UpdateContractBytecode(ctx, &types.MsgUpdateContractBytecode{ Creator: admin, @@ -218,15 +226,18 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("should fail if can't get contract account", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ - UseEVMMock: true, + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + UseAuthorityMock: true, }) msgServer := keeper.NewMsgServerImpl(*k) mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) contractAddr := sample.EthAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + mockEVMKeeper.On( "GetAccount", mock.Anything, @@ -244,12 +255,15 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("should fail neither a zrc20 nor wzeta connector", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) wzeta, _, _, _, _ := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) // can't update the bytecode of the wzeta contract @@ -262,12 +276,16 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("should fail if system contract not found", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + _, _, _, connector, _ := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) // remove system contract @@ -283,13 +301,18 @@ func TestKeeper_UpdateContractBytecode(t *testing.T) { }) t.Run("should fail if can't set account with new bytecode", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ - UseEVMMock: true, + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, }) + msgServer := keeper.NewMsgServerImpl(*k) mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + contractAddr := sample.EthAddress() newCodeHash := sample.Hash().Hex() diff --git a/x/fungible/keeper/msg_server_update_system_contract.go b/x/fungible/keeper/msg_server_update_system_contract.go index 1a5ce6dbc9..3e76f9abb2 100644 --- a/x/fungible/keeper/msg_server_update_system_contract.go +++ b/x/fungible/keeper/msg_server_update_system_contract.go @@ -11,14 +11,14 @@ import ( "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // Authorized: admin policy group 2. func (k msgServer) UpdateSystemContract(goCtx context.Context, msg *types.MsgUpdateSystemContract) (*types.MsgUpdateSystemContractResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserrors.Wrap(sdkerrors.ErrUnauthorized, "Deploy can only be executed by the correct policy account") } newSystemContractAddr := ethcommon.HexToAddress(msg.NewSystemContractAddress) diff --git a/x/fungible/keeper/msg_server_update_system_contract_test.go b/x/fungible/keeper/msg_server_update_system_contract_test.go index 0fbc261c47..60ae69dc80 100644 --- a/x/fungible/keeper/msg_server_update_system_contract_test.go +++ b/x/fungible/keeper/msg_server_update_system_contract_test.go @@ -13,18 +13,23 @@ import ( zetacommon "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestKeeper_UpdateSystemContract(t *testing.T) { t.Run("can update the system contract", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) queryZRC20SystemContract := func(contract common.Address) string { abi, err := zrc20.ZRC20MetaData.GetAbi() @@ -76,11 +81,15 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { }) t.Run("can update and overwrite the system contract if system contract not found", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) wzeta, err := k.DeployWZETA(ctx) require.NoError(t, err) @@ -95,6 +104,8 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { newSystemContract, err := k.DeployContract(ctx, systemcontract.SystemContractMetaData, wzeta, factory, router) require.NoError(t, err) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can update the system contract _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(admin, newSystemContract.Hex())) require.NoError(t, err) @@ -108,6 +119,8 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { newSystemContract, err = k.DeployContract(ctx, systemcontract.SystemContractMetaData, wzeta, factory, router) require.NoError(t, err) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can overwrite the previous system contract _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(admin, newSystemContract.Hex())) require.NoError(t, err) @@ -119,10 +132,17 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { }) t.Run("should not update the system contract if not admin", func(t *testing.T) { - k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + // deploy a new system contracts wzeta, factory, router, _, oldSystemContract := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) newSystemContract, err := k.DeployContract(ctx, systemcontract.SystemContractMetaData, wzeta, factory, router) @@ -130,17 +150,22 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { require.NotEqual(t, oldSystemContract, newSystemContract) // should not update the system contract if not admin - _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(sample.AccAddress(), newSystemContract.Hex())) + _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(admin, newSystemContract.Hex())) require.Error(t, err) require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) }) t.Run("should not update the system contract if invalid address", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // deploy a new system contracts wzeta, factory, router, _, oldSystemContract := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -155,16 +180,18 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { }) t.Run("should not update if any of 3 evm calls for foreign coin fail", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ - UseEVMMock: true, + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) chains := zetacommon.DefaultChainsList() require.True(t, len(chains) > 1) @@ -197,6 +224,8 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { // fail on first evm call mockEVMKeeper.MockEVMFailCallOnce() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can't update the system contract _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(admin, newSystemContract.Hex())) require.ErrorIs(t, err, types.ErrContractCall) @@ -205,6 +234,8 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { mockEVMKeeper.MockEVMSuccessCallOnce() mockEVMKeeper.MockEVMFailCallOnce() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can't update the system contract _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(admin, newSystemContract.Hex())) require.ErrorIs(t, err, types.ErrContractCall) @@ -213,6 +244,8 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { mockEVMKeeper.MockEVMSuccessCallTimes(2) mockEVMKeeper.MockEVMFailCallOnce() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can't update the system contract _, err = msgServer.UpdateSystemContract(ctx, types.NewMsgUpdateSystemContract(admin, newSystemContract.Hex())) require.ErrorIs(t, err, types.ErrContractCall) diff --git a/x/fungible/keeper/msg_server_update_zrc20_paused_status.go b/x/fungible/keeper/msg_server_update_zrc20_paused_status.go index e5527071ea..74da64dc08 100644 --- a/x/fungible/keeper/msg_server_update_zrc20_paused_status.go +++ b/x/fungible/keeper/msg_server_update_zrc20_paused_status.go @@ -3,12 +3,13 @@ package keeper import ( "context" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) // UpdateZRC20PausedStatus updates the paused status of a ZRC20 @@ -28,11 +29,11 @@ func (k msgServer) UpdateZRC20PausedStatus( // check if the sender is the admin // unpausing requires group2 admin - requiredPolicyAccount := zetaObserverTypes.Policy_Type_group1 + requiredPolicyAccount := authoritytypes.PolicyType_groupEmergency if msg.Action == types.UpdatePausedStatusAction_UNPAUSE { - requiredPolicyAccount = zetaObserverTypes.Policy_Type_group2 + requiredPolicyAccount = authoritytypes.PolicyType_groupAdmin } - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(requiredPolicyAccount) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, requiredPolicyAccount) { return nil, cosmoserrors.Wrap(sdkerrors.ErrUnauthorized, "Update can only be executed by the correct policy account") } diff --git a/x/fungible/keeper/msg_server_update_zrc20_paused_status_test.go b/x/fungible/keeper/msg_server_update_zrc20_paused_status_test.go index 82b6beb977..305bf1aadd 100644 --- a/x/fungible/keeper/msg_server_update_zrc20_paused_status_test.go +++ b/x/fungible/keeper/msg_server_update_zrc20_paused_status_test.go @@ -7,16 +7,20 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { t.Run("can update the paused status of zrc20", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) assertUnpaused := func(zrc20 string) { fc, found := k.GetForeignCoins(ctx, zrc20) @@ -38,7 +42,7 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { assertUnpaused(zrc20B) assertUnpaused(zrc20C) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) // can pause zrc20 _, err := msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( @@ -54,7 +58,7 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { assertPaused(zrc20B) assertUnpaused(zrc20C) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // can unpause zrc20 _, err = msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( @@ -69,7 +73,7 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { assertPaused(zrc20B) assertUnpaused(zrc20C) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) // can pause already paused zrc20 _, err = msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( @@ -84,7 +88,7 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { assertPaused(zrc20B) assertUnpaused(zrc20C) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // can unpause already unpaused zrc20 _, err = msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( @@ -99,7 +103,7 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { assertPaused(zrc20B) assertUnpaused(zrc20C) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) // can pause all zrc20 _, err = msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( @@ -116,7 +120,7 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { assertPaused(zrc20B) assertPaused(zrc20C) - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // can unpause all zrc20 _, err = msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( @@ -135,10 +139,13 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { }) t.Run("should fail if invalid message", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) invalidMsg := types.NewMsgUpdateZRC20PausedStatus(admin, []string{}, types.UpdatePausedStatusAction_PAUSE) require.ErrorIs(t, invalidMsg.ValidateBasic(), sdkerrors.ErrInvalidRequest) @@ -148,18 +155,24 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { }) t.Run("should fail if not authorized", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, false) + _, err := msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( - sample.AccAddress(), + admin, []string{sample.EthAddress().String()}, types.UpdatePausedStatusAction_PAUSE, )) require.ErrorIs(t, err, sdkerrors.ErrUnauthorized) - admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) _, err = msgServer.UpdateZRC20PausedStatus(ctx, types.NewMsgUpdateZRC20PausedStatus( admin, @@ -171,10 +184,15 @@ func TestKeeper_UpdateZRC20PausedStatus(t *testing.T) { }) t.Run("should fail if zrc20 does not exist", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group1) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) zrc20A, zrc20B := sample.EthAddress().String(), sample.EthAddress().String() k.SetForeignCoins(ctx, sample.ForeignCoins(t, zrc20A)) diff --git a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee.go b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee.go index 52761180ba..da366d8f07 100644 --- a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee.go +++ b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee.go @@ -4,20 +4,19 @@ import ( "context" cosmoserrors "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" ) -// Authorized: admin policy group 2. +// UpdateZRC20WithdrawFee updates the withdraw fee and gas limit of a zrc20 token func (k msgServer) UpdateZRC20WithdrawFee(goCtx context.Context, msg *types.MsgUpdateZRC20WithdrawFee) (*types.MsgUpdateZRC20WithdrawFeeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) // check signer permission - if msg.Creator != k.observerKeeper.GetParams(ctx).GetAdminPolicyAccount(zetaObserverTypes.Policy_Type_group2) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return nil, cosmoserrors.Wrap(sdkerrors.ErrUnauthorized, "deploy can only be executed by the correct policy account") } diff --git a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go index 29a5e157ae..128893d5c7 100644 --- a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go +++ b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go @@ -5,7 +5,6 @@ import ( "testing" "cosmossdk.io/math" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/mock" @@ -13,21 +12,24 @@ import ( "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { t.Run("can update the withdraw fee", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) chainID := getValidChainID(t) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) // set coin admin admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) // deploy the system contract and a ZRC20 contract deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) @@ -38,6 +40,8 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { require.NoError(t, err) require.Zero(t, protocolFee.Uint64()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can update the protocol fee and gas limit _, err = msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( admin, @@ -55,6 +59,8 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { require.NoError(t, err) require.Equal(t, uint64(42), gasLimit.Uint64()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can update protocol fee only _, err = msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( admin, @@ -70,6 +76,8 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { require.NoError(t, err) require.Equal(t, uint64(42), gasLimit.Uint64()) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // can update gas limit only _, err = msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( admin, @@ -87,11 +95,17 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { }) t.Run("should fail if not authorized", func(t *testing.T) { - k, ctx, _, _ := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + _, err := msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( - sample.AccAddress(), + admin, sample.EthAddress().String(), math.NewUint(42), math.Uint{}, @@ -100,10 +114,14 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { }) t.Run("should fail if invalid zrc20 address", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( admin, @@ -115,10 +133,14 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { }) t.Run("should fail if can't retrieve the foreign coin", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( admin, @@ -130,13 +152,18 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { }) t.Run("should fail if can't query old fee", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeper(t) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) // setup admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + zrc20 := sample.EthAddress() k.SetForeignCoins(ctx, sample.ForeignCoins(t, zrc20.String())) @@ -151,14 +178,19 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { }) t.Run("should fail if contract call for setting new protocol fee fails", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{UseEVMMock: true}) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + UseAuthorityMock: true, + }) msgServer := keeper.NewMsgServerImpl(*k) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) // setup admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + zrc20Addr := sample.EthAddress() k.SetForeignCoins(ctx, sample.ForeignCoins(t, zrc20Addr.String())) @@ -196,14 +228,19 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { }) t.Run("should fail if contract call for setting new protocol fee fails", func(t *testing.T) { - k, ctx, _, zk := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{UseEVMMock: true}) + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + UseAuthorityMock: true, + }) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) msgServer := keeper.NewMsgServerImpl(*k) mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) // setup admin := sample.AccAddress() - setAdminPolicies(ctx, zk, admin, observertypes.Policy_Type_group2) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + zrc20Addr := sample.EthAddress() k.SetForeignCoins(ctx, sample.ForeignCoins(t, zrc20Addr.String())) diff --git a/x/fungible/types/expected_keepers.go b/x/fungible/types/expected_keepers.go index 180642b2af..feddedf4b7 100644 --- a/x/fungible/types/expected_keepers.go +++ b/x/fungible/types/expected_keepers.go @@ -12,6 +12,7 @@ import ( "github.com/evmos/ethermint/x/evm/statedb" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/zeta-chain/zetacore/common" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -53,3 +54,7 @@ type EVMKeeper interface { GetCode(ctx sdk.Context, codeHash ethcommon.Hash) []byte SetAccount(ctx sdk.Context, addr ethcommon.Address, account statedb.Account) error } + +type AuthorityKeeper interface { + IsAuthorized(ctx sdk.Context, address string, policyType authoritytypes.PolicyType) bool +} diff --git a/x/observer/genesis_test.go b/x/observer/genesis_test.go index 73193de928..7482f24f95 100644 --- a/x/observer/genesis_test.go +++ b/x/observer/genesis_test.go @@ -44,7 +44,7 @@ func TestGenesis(t *testing.T) { } // Init and export - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) observer.InitGenesis(ctx, *k, genesisState) got := observer.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/observer/keeper/chain_nonces_test.go b/x/observer/keeper/chain_nonces_test.go index d133c7fc53..e0b12aedec 100644 --- a/x/observer/keeper/chain_nonces_test.go +++ b/x/observer/keeper/chain_nonces_test.go @@ -10,7 +10,7 @@ import ( func TestKeeper_GetChainNonces(t *testing.T) { t.Run("Get chain nonces", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) chainNoncesList := sample.ChainNoncesList(t, 10) for _, n := range chainNoncesList { k.SetChainNonces(ctx, n) @@ -22,7 +22,7 @@ func TestKeeper_GetChainNonces(t *testing.T) { } }) t.Run("Get chain nonces not found", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) chainNoncesList := sample.ChainNoncesList(t, 10) for _, n := range chainNoncesList { k.SetChainNonces(ctx, n) @@ -31,7 +31,7 @@ func TestKeeper_GetChainNonces(t *testing.T) { require.False(t, found) }) t.Run("Get all chain nonces", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) chainNoncesList := sample.ChainNoncesList(t, 10) for _, n := range chainNoncesList { k.SetChainNonces(ctx, n) diff --git a/x/observer/keeper/chain_params_test.go b/x/observer/keeper/chain_params_test.go index 9bf63cd65b..2d63265122 100644 --- a/x/observer/keeper/chain_params_test.go +++ b/x/observer/keeper/chain_params_test.go @@ -12,7 +12,7 @@ import ( func TestKeeper_GetSupportedChainFromChainID(t *testing.T) { t.Run("return nil if chain not found", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) // no core params list require.Nil(t, k.GetSupportedChainFromChainID(ctx, getValidEthChainIDWithIndex(t, 0))) @@ -31,7 +31,7 @@ func TestKeeper_GetSupportedChainFromChainID(t *testing.T) { }) t.Run("return chain if chain found", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) chainID := getValidEthChainIDWithIndex(t, 0) setSupportedChain(ctx, *k, getValidEthChainIDWithIndex(t, 1), chainID) chain := k.GetSupportedChainFromChainID(ctx, chainID) @@ -42,12 +42,12 @@ func TestKeeper_GetSupportedChainFromChainID(t *testing.T) { func TestKeeper_GetSupportedChains(t *testing.T) { t.Run("return empty list if no core params list", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) require.Empty(t, k.GetSupportedChains(ctx)) }) t.Run("return list containing supported chains", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) require.Greater(t, len(common.ExternalChainList()), 5) supported1 := common.ExternalChainList()[0] diff --git a/x/observer/keeper/grpc_query_blame_test.go b/x/observer/keeper/grpc_query_blame_test.go index 141246af47..36d95ed23e 100644 --- a/x/observer/keeper/grpc_query_blame_test.go +++ b/x/observer/keeper/grpc_query_blame_test.go @@ -12,7 +12,7 @@ import ( ) func TestKeeper_BlameByIdentifier(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) var chainId int64 = 97 var nonce uint64 = 101 digest := "85f5e10431f69bc2a14046a13aabaefc660103b6de7a84f75c4b96181d03f0b5" @@ -31,7 +31,7 @@ func TestKeeper_BlameByIdentifier(t *testing.T) { } func TestKeeper_BlameByChainAndNonce(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) var chainId int64 = 97 var nonce uint64 = 101 digest := "85f5e10431f69bc2a14046a13aabaefc660103b6de7a84f75c4b96181d03f0b5" @@ -52,7 +52,7 @@ func TestKeeper_BlameByChainAndNonce(t *testing.T) { func TestKeeper_BlameAll(t *testing.T) { t.Run("GetBlameRecord by limit ", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) blameList := sample.BlameRecordsList(t, 10) for _, record := range blameList { k.SetBlame(ctx, record) @@ -69,7 +69,7 @@ func TestKeeper_BlameAll(t *testing.T) { require.Equal(t, len(blameList), int(pageRes.Total)) }) t.Run("GetBlameRecord by offset ", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) blameList := sample.BlameRecordsList(t, 20) offset := 10 for _, record := range blameList { @@ -88,7 +88,7 @@ func TestKeeper_BlameAll(t *testing.T) { require.Equal(t, len(blameList), int(pageRes.Total)) }) t.Run("GetAllBlameRecord", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) blameList := sample.BlameRecordsList(t, 100) for _, record := range blameList { k.SetBlame(ctx, record) @@ -103,7 +103,7 @@ func TestKeeper_BlameAll(t *testing.T) { require.Equal(t, blameList, rst) }) t.Run("Get no records if nothing is set", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) rst := k.GetAllBlame(ctx) require.Len(t, rst, 0) }) diff --git a/x/observer/keeper/grpc_query_nonces_test.go b/x/observer/keeper/grpc_query_nonces_test.go index 74f4777b5d..657eb59008 100644 --- a/x/observer/keeper/grpc_query_nonces_test.go +++ b/x/observer/keeper/grpc_query_nonces_test.go @@ -14,7 +14,7 @@ import ( ) func TestChainNoncesQuerySingle(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) wctx := sdk.WrapSDKContext(ctx) chainNonces := sample.ChainNoncesList(t, 2) for _, nonce := range chainNonces { @@ -59,7 +59,7 @@ func TestChainNoncesQuerySingle(t *testing.T) { } func TestChainNoncesQueryPaginated(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) wctx := sdk.WrapSDKContext(ctx) chainNonces := sample.ChainNoncesList(t, 5) for _, nonce := range chainNonces { diff --git a/x/observer/keeper/keeper.go b/x/observer/keeper/keeper.go index c7a5f38e77..cda854ccc6 100644 --- a/x/observer/keeper/keeper.go +++ b/x/observer/keeper/keeper.go @@ -3,23 +3,23 @@ package keeper import ( "fmt" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/tendermint/tendermint/libs/log" "github.com/zeta-chain/zetacore/x/observer/types" ) type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - memKey storetypes.StoreKey - paramstore paramtypes.Subspace - stakingKeeper types.StakingKeeper - slashingKeeper types.SlashingKeeper + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey + paramstore paramtypes.Subspace + stakingKeeper types.StakingKeeper + slashingKeeper types.SlashingKeeper + authorityKeeper types.AuthorityKeeper } ) @@ -30,6 +30,7 @@ func NewKeeper( ps paramtypes.Subspace, stakingKeeper types.StakingKeeper, slashinKeeper types.SlashingKeeper, + authorityKeeper types.AuthorityKeeper, ) *Keeper { // set KeyTable if it has not already been set if !ps.HasKeyTable() { @@ -37,12 +38,13 @@ func NewKeeper( } return &Keeper{ - cdc: cdc, - storeKey: storeKey, - memKey: memKey, - paramstore: ps, - stakingKeeper: stakingKeeper, - slashingKeeper: slashinKeeper, + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + paramstore: ps, + stakingKeeper: stakingKeeper, + slashingKeeper: slashinKeeper, + authorityKeeper: authorityKeeper, } } @@ -54,6 +56,10 @@ func (k Keeper) GetStakingKeeper() types.StakingKeeper { return k.stakingKeeper } +func (k Keeper) GetAuthorityKeeper() types.AuthorityKeeper { + return k.authorityKeeper +} + func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } diff --git a/x/observer/keeper/keeper_test.go b/x/observer/keeper/keeper_test.go index 7c13b7b0f2..c34cdb5392 100644 --- a/x/observer/keeper/keeper_test.go +++ b/x/observer/keeper/keeper_test.go @@ -3,18 +3,18 @@ package keeper import ( "testing" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" typesparams "github.com/cosmos/cosmos-sdk/x/params/types" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmdb "github.com/tendermint/tm-db" + authoritykeeper "github.com/zeta-chain/zetacore/x/authority/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -45,6 +45,7 @@ func SetupKeeper(t testing.TB) (*Keeper, sdk.Context) { paramsSubspace, stakingkeeper.Keeper{}, slashingkeeper.Keeper{}, + authoritykeeper.Keeper{}, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil) diff --git a/x/observer/keeper/migrator.go b/x/observer/keeper/migrator.go index 0cee0ed183..c4b676884c 100644 --- a/x/observer/keeper/migrator.go +++ b/x/observer/keeper/migrator.go @@ -7,6 +7,7 @@ import ( v4 "github.com/zeta-chain/zetacore/x/observer/migrations/v4" v5 "github.com/zeta-chain/zetacore/x/observer/migrations/v5" v6 "github.com/zeta-chain/zetacore/x/observer/migrations/v6" + v7 "github.com/zeta-chain/zetacore/x/observer/migrations/v7" ) // Migrator is a struct for handling in-place store migrations. @@ -42,3 +43,8 @@ func (m Migrator) Migrate4to5(ctx sdk.Context) error { func (m Migrator) Migrate5to6(ctx sdk.Context) error { return v6.MigrateStore(ctx, m.observerKeeper) } + +// Migrate6to7 migrates the store from consensus version 6 to 7 +func (m Migrator) Migrate6to7(ctx sdk.Context) error { + return v7.MigrateStore(ctx, m.observerKeeper) +} diff --git a/x/observer/keeper/msg_server_add_block_header_test.go b/x/observer/keeper/msg_server_add_block_header_test.go index 85a7ec505b..d50b9bf861 100644 --- a/x/observer/keeper/msg_server_add_block_header_test.go +++ b/x/observer/keeper/msg_server_add_block_header_test.go @@ -149,7 +149,7 @@ func TestMsgServer_AddBlockHeader(t *testing.T) { } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) k.SetObserverSet(ctx, types.ObserverSet{ ObserverList: []string{observerAddress.String()}, diff --git a/x/observer/keeper/msg_server_add_observer.go b/x/observer/keeper/msg_server_add_observer.go index 5bf64e5d95..efdb085b01 100644 --- a/x/observer/keeper/msg_server_add_observer.go +++ b/x/observer/keeper/msg_server_add_observer.go @@ -4,6 +4,8 @@ import ( "context" "math" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -11,12 +13,15 @@ import ( "github.com/zeta-chain/zetacore/x/observer/types" ) -// Authorized: admin policy group 2. +// AddObserver adds an observer address to the observer set func (k msgServer) AddObserver(goCtx context.Context, msg *types.MsgAddObserver) (*types.MsgAddObserverResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.GetParams(ctx).GetAdminPolicyAccount(types.Policy_Type_group2) { + + // check permission + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return &types.MsgAddObserverResponse{}, types.ErrNotAuthorizedPolicy } + pubkey, err := common.NewPubKey(msg.ZetaclientGranteePubkey) if err != nil { return &types.MsgAddObserverResponse{}, cosmoserrors.Wrap(sdkerrors.ErrInvalidPubKey, err.Error()) @@ -25,7 +30,9 @@ func (k msgServer) AddObserver(goCtx context.Context, msg *types.MsgAddObserver) if err != nil { return &types.MsgAddObserverResponse{}, cosmoserrors.Wrap(sdkerrors.ErrInvalidPubKey, err.Error()) } + k.DisableInboundOnly(ctx) + // AddNodeAccountOnly flag usage // True: adds observer into the Node Account list but returns without adding to the observer list // False: adds observer to the observer list, and not the node account list @@ -41,9 +48,12 @@ func (k msgServer) AddObserver(goCtx context.Context, msg *types.MsgAddObserver) k.SetKeygen(ctx, types.Keygen{BlockNumber: math.MaxInt64}) return &types.MsgAddObserverResponse{}, nil } + k.AddObserverToSet(ctx, msg.ObserverAddress) observerSet, _ := k.GetObserverSet(ctx) + k.SetLastObserverCount(ctx, &types.LastObserverCount{Count: observerSet.LenUint()}) EmitEventAddObserver(ctx, observerSet.LenUint(), msg.ObserverAddress, granteeAddress.String(), msg.ZetaclientGranteePubkey) + return &types.MsgAddObserverResponse{}, nil } diff --git a/x/observer/keeper/msg_server_remove_chain_params.go b/x/observer/keeper/msg_server_remove_chain_params.go index 4ee6259571..4980df63fc 100644 --- a/x/observer/keeper/msg_server_remove_chain_params.go +++ b/x/observer/keeper/msg_server_remove_chain_params.go @@ -4,13 +4,16 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/observer/types" ) // RemoveChainParams removes chain parameters for a specific chain. func (k msgServer) RemoveChainParams(goCtx context.Context, msg *types.MsgRemoveChainParams) (*types.MsgRemoveChainParamsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.GetParams(ctx).GetAdminPolicyAccount(types.Policy_Type_group2) { + + // check permission + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return &types.MsgRemoveChainParamsResponse{}, types.ErrNotAuthorizedPolicy } diff --git a/x/observer/keeper/msg_server_remove_chain_params_test.go b/x/observer/keeper/msg_server_remove_chain_params_test.go index 238f86e256..3eae032746 100644 --- a/x/observer/keeper/msg_server_remove_chain_params_test.go +++ b/x/observer/keeper/msg_server_remove_chain_params_test.go @@ -8,22 +8,27 @@ import ( "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/observer/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) func TestMsgServer_RemoveChainParams(t *testing.T) { t.Run("can update chain params", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) srv := keeper.NewMsgServerImpl(*k) + // mock the authority keeper for authorization + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + chain1 := common.ExternalChainList()[0].ChainId chain2 := common.ExternalChainList()[1].ChainId chain3 := common.ExternalChainList()[2].ChainId // set admin admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) // add chain params k.SetChainParamsList(ctx, types.ChainParamsList{ @@ -34,6 +39,8 @@ func TestMsgServer_RemoveChainParams(t *testing.T) { }, }) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // remove chain params _, err := srv.RemoveChainParams(sdk.WrapSDKContext(ctx), &types.MsgRemoveChainParams{ Creator: admin, @@ -75,34 +82,32 @@ func TestMsgServer_RemoveChainParams(t *testing.T) { }) t.Run("cannot remove chain params if not authorized", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) - srv := keeper.NewMsgServerImpl(*k) - - _, err := srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ - Creator: sample.AccAddress(), - ChainParams: sample.ChainParams(common.ExternalChainList()[0].ChainId), + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, }) - require.ErrorIs(t, err, types.ErrNotAuthorizedPolicy) + srv := keeper.NewMsgServerImpl(*k) - // group 1 should not be able to update core params admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group1) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) - _, err = srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ - Creator: sample.AccAddress(), - ChainParams: sample.ChainParams(common.ExternalChainList()[0].ChainId), + _, err := srv.RemoveChainParams(sdk.WrapSDKContext(ctx), &types.MsgRemoveChainParams{ + Creator: admin, + ChainId: common.ExternalChainList()[0].ChainId, }) require.ErrorIs(t, err, types.ErrNotAuthorizedPolicy) - }) t.Run("cannot remove if chain ID not found", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) srv := keeper.NewMsgServerImpl(*k) // set admin admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // not found if no chain params _, found := k.GetChainParamsList(ctx) @@ -123,6 +128,8 @@ func TestMsgServer_RemoveChainParams(t *testing.T) { }, }) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // not found if chain ID not in list _, err = srv.RemoveChainParams(sdk.WrapSDKContext(ctx), &types.MsgRemoveChainParams{ Creator: admin, diff --git a/x/observer/keeper/msg_server_update_chain_params.go b/x/observer/keeper/msg_server_update_chain_params.go index 638eb373f0..3a2f0dea14 100644 --- a/x/observer/keeper/msg_server_update_chain_params.go +++ b/x/observer/keeper/msg_server_update_chain_params.go @@ -4,6 +4,7 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -13,7 +14,9 @@ import ( // Only the admin policy account is authorized to broadcast this message. func (k msgServer) UpdateChainParams(goCtx context.Context, msg *types.MsgUpdateChainParams) (*types.MsgUpdateChainParamsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.GetParams(ctx).GetAdminPolicyAccount(types.Policy_Type_group2) { + + // check permission + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return &types.MsgUpdateChainParamsResponse{}, types.ErrNotAuthorizedPolicy } diff --git a/x/observer/keeper/msg_server_update_chain_params_test.go b/x/observer/keeper/msg_server_update_chain_params_test.go index 1e4a707aeb..2a14a752b4 100644 --- a/x/observer/keeper/msg_server_update_chain_params_test.go +++ b/x/observer/keeper/msg_server_update_chain_params_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "testing" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/common" @@ -14,7 +16,9 @@ import ( func TestMsgServer_UpdateChainParams(t *testing.T) { t.Run("can update chain params", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) srv := keeper.NewMsgServerImpl(*k) chain1 := common.ExternalChainList()[0].ChainId @@ -23,12 +27,14 @@ func TestMsgServer_UpdateChainParams(t *testing.T) { // set admin admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) // check list initially empty _, found := k.GetChainParamsList(ctx) require.False(t, found) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // a new chain params can be added chainParams1 := sample.ChainParams(chain1) _, err := srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ @@ -43,6 +49,8 @@ func TestMsgServer_UpdateChainParams(t *testing.T) { require.Len(t, chainParamsList.ChainParams, 1) require.Equal(t, chainParams1, chainParamsList.ChainParams[0]) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // a new chian params can be added chainParams2 := sample.ChainParams(chain2) _, err = srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ @@ -58,6 +66,8 @@ func TestMsgServer_UpdateChainParams(t *testing.T) { require.Equal(t, chainParams1, chainParamsList.ChainParams[0]) require.Equal(t, chainParams2, chainParamsList.ChainParams[1]) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // a new chain params can be added chainParams3 := sample.ChainParams(chain3) _, err = srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ @@ -74,6 +84,8 @@ func TestMsgServer_UpdateChainParams(t *testing.T) { require.Equal(t, chainParams2, chainParamsList.ChainParams[1]) require.Equal(t, chainParams3, chainParamsList.ChainParams[2]) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + // chain params can be updated chainParams2.ConfirmationCount = chainParams2.ConfirmationCount + 1 _, err = srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ @@ -92,24 +104,19 @@ func TestMsgServer_UpdateChainParams(t *testing.T) { }) t.Run("cannot update chain params if not authorized", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) - srv := keeper.NewMsgServerImpl(*k) - - _, err := srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ - Creator: sample.AccAddress(), - ChainParams: sample.ChainParams(common.ExternalChainList()[0].ChainId), + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, }) - require.ErrorIs(t, err, types.ErrNotAuthorizedPolicy) + srv := keeper.NewMsgServerImpl(*k) - // group 1 should not be able to update chain params admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group1) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) - _, err = srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ - Creator: sample.AccAddress(), + _, err := srv.UpdateChainParams(sdk.WrapSDKContext(ctx), &types.MsgUpdateChainParams{ + Creator: admin, ChainParams: sample.ChainParams(common.ExternalChainList()[0].ChainId), }) require.ErrorIs(t, err, types.ErrNotAuthorizedPolicy) - }) } diff --git a/x/observer/keeper/msg_server_update_crosschain_flags.go b/x/observer/keeper/msg_server_update_crosschain_flags.go index 30325bef64..42be93a3a8 100644 --- a/x/observer/keeper/msg_server_update_crosschain_flags.go +++ b/x/observer/keeper/msg_server_update_crosschain_flags.go @@ -15,7 +15,7 @@ func (k msgServer) UpdateCrosschainFlags(goCtx context.Context, msg *types.MsgUp ctx := sdk.UnwrapSDKContext(goCtx) // check permission - if msg.Creator != k.GetParams(ctx).GetAdminPolicyAccount(msg.GetRequiredGroup()) { + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, msg.GetRequiredPolicyType()) { return &types.MsgUpdateCrosschainFlagsResponse{}, types.ErrNotAuthorizedPolicy } diff --git a/x/observer/keeper/msg_server_update_crosschain_flags_test.go b/x/observer/keeper/msg_server_update_crosschain_flags_test.go index aaa33f4e23..4eb779191a 100644 --- a/x/observer/keeper/msg_server_update_crosschain_flags_test.go +++ b/x/observer/keeper/msg_server_update_crosschain_flags_test.go @@ -8,29 +8,22 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/observer/keeper" "github.com/zeta-chain/zetacore/x/observer/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -func setAdminCrossChainFlags(ctx sdk.Context, k *keeper.Keeper, admin string, group types.Policy_Type) { - k.SetParams(ctx, observertypes.Params{ - AdminPolicy: []*observertypes.Admin_Policy{ - { - PolicyType: group, - Address: admin, - }, - }, - }) -} - func TestMsgServer_UpdateCrosschainFlags(t *testing.T) { t.Run("can update crosschain flags", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) srv := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) + // mock the authority keeper for authorization + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err := srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ Creator: admin, @@ -58,7 +51,7 @@ func TestMsgServer_UpdateCrosschainFlags(t *testing.T) { require.True(t, flags.BlockHeaderVerificationFlags.IsEthTypeChainEnabled) require.False(t, flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled) - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) // can update flags again _, err = srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ @@ -88,7 +81,7 @@ func TestMsgServer_UpdateCrosschainFlags(t *testing.T) { require.True(t, flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled) // group 1 should be able to disable inbound and outbound - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) // if gas price increase flags is nil, it should not be updated _, err = srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ @@ -109,7 +102,7 @@ func TestMsgServer_UpdateCrosschainFlags(t *testing.T) { require.True(t, flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled) // group 1 should be able to disable header verification - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) // if gas price increase flags is nil, it should not be updated _, err = srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ @@ -138,7 +131,7 @@ func TestMsgServer_UpdateCrosschainFlags(t *testing.T) { _, found = k.GetCrosschainFlags(ctx) require.False(t, found) - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) _, err = srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ Creator: admin, @@ -157,19 +150,24 @@ func TestMsgServer_UpdateCrosschainFlags(t *testing.T) { }) t.Run("cannot update crosschain flags if not authorized", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) srv := keeper.NewMsgServerImpl(*k) + admin := sample.AccAddress() + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, false) + _, err := srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ - Creator: sample.AccAddress(), + Creator: admin, IsInboundEnabled: false, IsOutboundEnabled: false, }) require.Error(t, err) require.Equal(t, types.ErrNotAuthorizedPolicy, err) - admin := sample.AccAddress() - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group1) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) _, err = srv.UpdateCrosschainFlags(sdk.WrapSDKContext(ctx), &types.MsgUpdateCrosschainFlags{ Creator: admin, diff --git a/x/observer/keeper/msg_server_update_keygen.go b/x/observer/keeper/msg_server_update_keygen.go index 9bf0250fb1..680906d393 100644 --- a/x/observer/keeper/msg_server_update_keygen.go +++ b/x/observer/keeper/msg_server_update_keygen.go @@ -3,6 +3,8 @@ package keeper import ( "context" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -12,11 +14,13 @@ import ( // // Authorized: admin policy group 1. func (k msgServer) UpdateKeygen(goCtx context.Context, msg *types.MsgUpdateKeygen) (*types.MsgUpdateKeygenResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - if msg.Creator != k.GetParams(ctx).GetAdminPolicyAccount(types.Policy_Type_group1) { + + // check permission + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupEmergency) { return &types.MsgUpdateKeygenResponse{}, types.ErrNotAuthorizedPolicy } + keygen, found := k.GetKeygen(ctx) if !found { return nil, types.ErrKeygenNotFound @@ -24,15 +28,20 @@ func (k msgServer) UpdateKeygen(goCtx context.Context, msg *types.MsgUpdateKeyge if msg.Block <= (ctx.BlockHeight() + 10) { return nil, types.ErrKeygenBlockTooLow } + nodeAccountList := k.GetAllNodeAccount(ctx) granteePubKeys := make([]string, len(nodeAccountList)) for i, nodeAccount := range nodeAccountList { granteePubKeys[i] = nodeAccount.GranteePubkey.Secp256k1.String() } + + // update keygen keygen.GranteePubkeys = granteePubKeys keygen.BlockNumber = msg.Block keygen.Status = types.KeygenStatus_PendingKeygen k.SetKeygen(ctx, keygen) + EmitEventKeyGenBlockUpdated(ctx, &keygen) + return &types.MsgUpdateKeygenResponse{}, nil } diff --git a/x/observer/keeper/msg_server_update_observer.go b/x/observer/keeper/msg_server_update_observer.go index c3aaee7472..e32542a8b8 100644 --- a/x/observer/keeper/msg_server_update_observer.go +++ b/x/observer/keeper/msg_server_update_observer.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/x/observer/types" @@ -78,7 +80,8 @@ func (k Keeper) CheckUpdateReason(ctx sdk.Context, msg *types.MsgUpdateObserver) } case types.ObserverUpdateReason_AdminUpdate: { - if msg.Creator != k.GetParams(ctx).GetAdminPolicyAccount(types.Policy_Type_group2) { + // Group admin is authorized to update observer + if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupAdmin) { return false, types.ErrNotAuthorizedPolicy } return true, nil diff --git a/x/observer/keeper/msg_server_update_observer_test.go b/x/observer/keeper/msg_server_update_observer_test.go index 679e9851c5..1d6123dab9 100644 --- a/x/observer/keeper/msg_server_update_observer_test.go +++ b/x/observer/keeper/msg_server_update_observer_test.go @@ -11,13 +11,14 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/observer/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) func TestMsgServer_UpdateObserver(t *testing.T) { t.Run("successfully update tombstoned observer", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -73,7 +74,7 @@ func TestMsgServer_UpdateObserver(t *testing.T) { }) t.Run("unable to update to a non validator address", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -122,7 +123,7 @@ func TestMsgServer_UpdateObserver(t *testing.T) { }) t.Run("unable to update tombstoned validator with with non operator account", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -172,8 +173,9 @@ func TestMsgServer_UpdateObserver(t *testing.T) { }) require.ErrorIs(t, err, types.ErrUpdateObserver) }) + t.Run("unable to update non-tombstoned observer with update reason tombstoned", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -222,8 +224,9 @@ func TestMsgServer_UpdateObserver(t *testing.T) { }) require.ErrorIs(t, err, types.ErrUpdateObserver) }) + t.Run("unable to update observer with no node account", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -268,8 +271,9 @@ func TestMsgServer_UpdateObserver(t *testing.T) { }) require.ErrorIs(t, err, types.ErrNodeAccountNotFound) }) + t.Run("unable to update observer when last observer count is missing", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -313,12 +317,16 @@ func TestMsgServer_UpdateObserver(t *testing.T) { }) require.ErrorIs(t, err, types.ErrLastObserverCountNotFound) }) + t.Run("update observer using admin policy", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) srv := keeper.NewMsgServerImpl(*k) admin := sample.AccAddress() + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) - setAdminCrossChainFlags(ctx, k, admin, types.Policy_Type_group2) // #nosec G404 test purpose - weak randomness is not an issue here r := rand.New(rand.NewSource(9)) @@ -364,12 +372,14 @@ func TestMsgServer_UpdateObserver(t *testing.T) { UpdateReason: types.ObserverUpdateReason_AdminUpdate, }) require.NoError(t, err) + acc, found := k.GetNodeAccount(ctx, newOperatorAddress.String()) require.True(t, found) require.Equal(t, newOperatorAddress.String(), acc.Operator) }) + t.Run("fail to update observer using regular account and update type admin", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) srv := keeper.NewMsgServerImpl(*k) // #nosec G404 test purpose - weak randomness is not an issue here diff --git a/x/observer/keeper/vote_inbound_test.go b/x/observer/keeper/vote_inbound_test.go index e1391c051f..94b9ed4105 100644 --- a/x/observer/keeper/vote_inbound_test.go +++ b/x/observer/keeper/vote_inbound_test.go @@ -14,7 +14,7 @@ import ( func TestKeeper_VoteOnInboundBallot(t *testing.T) { t.Run("fail if inbound not enabled", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetCrosschainFlags(ctx, types.CrosschainFlags{ IsInboundEnabled: false, @@ -35,7 +35,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("fail if sender chain not supported", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetCrosschainFlags(ctx, types.CrosschainFlags{ IsInboundEnabled: true, @@ -78,7 +78,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("fail if not authorized", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetCrosschainFlags(ctx, types.CrosschainFlags{ IsInboundEnabled: true, @@ -107,7 +107,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("fail if receiver chain not supported", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -172,7 +172,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("fail if inbound contain ZETA but receiver chain doesn't support ZETA", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -214,7 +214,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("can add vote and create ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -258,7 +258,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("can add vote and create ballot without finalizing ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -311,7 +311,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("can add vote to an existing ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -375,7 +375,7 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { }) t.Run("can add vote to an existing ballot and finalize ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) diff --git a/x/observer/keeper/vote_outbound_test.go b/x/observer/keeper/vote_outbound_test.go index 0e087bac1f..ad84672d4f 100644 --- a/x/observer/keeper/vote_outbound_test.go +++ b/x/observer/keeper/vote_outbound_test.go @@ -13,7 +13,7 @@ import ( func TestKeeper_VoteOnOutboundBallot(t *testing.T) { t.Run("fail if chain is not supported", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetChainParamsList(ctx, types.ChainParamsList{}) @@ -49,7 +49,7 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { }) t.Run("fail if receive status is invalid", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetChainParamsList(ctx, types.ChainParamsList{ ChainParams: []*types.ChainParams{ @@ -72,7 +72,7 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { }) t.Run("fail if sender is not authorized", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetChainParamsList(ctx, types.ChainParamsList{ ChainParams: []*types.ChainParams{ @@ -96,7 +96,7 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { }) t.Run("can add vote and create ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -134,7 +134,7 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { }) t.Run("can add vote and create ballot without finalizing ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -180,7 +180,7 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { }) t.Run("can add vote to an existing ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) @@ -238,7 +238,7 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { }) t.Run("can add vote to an existing ballot and finalize ballot", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) observer := sample.AccAddress() stakingMock := keepertest.GetObserverStakingMock(t, k) diff --git a/x/observer/migrations/v3/migrate_test.go b/x/observer/migrations/v3/migrate_test.go index ce580cbf42..81e3747517 100644 --- a/x/observer/migrations/v3/migrate_test.go +++ b/x/observer/migrations/v3/migrate_test.go @@ -11,7 +11,7 @@ import ( ) func TestMigrateStore(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) // nothing if no admin policy params := types.DefaultParams() diff --git a/x/observer/migrations/v4/migrate_test.go b/x/observer/migrations/v4/migrate_test.go index 55c87d6541..64cbeaedef 100644 --- a/x/observer/migrations/v4/migrate_test.go +++ b/x/observer/migrations/v4/migrate_test.go @@ -11,7 +11,7 @@ import ( ) func TestMigrateCrosschainFlags(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) store := prefix.NewStore(ctx.KVStore(k.StoreKey()), types.KeyPrefix(types.CrosschainFlagsKey)) legacyFlags := types.LegacyCrosschainFlags{ IsInboundEnabled: false, diff --git a/x/observer/migrations/v5/migrate_test.go b/x/observer/migrations/v5/migrate_test.go index f5aa035414..1ba3d77eff 100644 --- a/x/observer/migrations/v5/migrate_test.go +++ b/x/observer/migrations/v5/migrate_test.go @@ -15,7 +15,7 @@ import ( func TestMigrateObserverMapper(t *testing.T) { t.Run("TestMigrateStore", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) legacyObserverMapperStore := prefix.NewStore(ctx.KVStore(k.StoreKey()), types.KeyPrefix(types.ObserverMapperKey)) legacyObserverMapperList := sample.LegacyObserverMapperList(t, 12, "sample") for _, legacyObserverMapper := range legacyObserverMapperList { @@ -43,7 +43,7 @@ func TestMigrateObserverMapper(t *testing.T) { } func TestMigrateObserverParams(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) // set chain params previousChainParamsList := types.ChainParamsList{ diff --git a/x/observer/migrations/v6/migrate_test.go b/x/observer/migrations/v6/migrate_test.go index b99242aabc..a9159aefc4 100644 --- a/x/observer/migrations/v6/migrate_test.go +++ b/x/observer/migrations/v6/migrate_test.go @@ -12,7 +12,7 @@ import ( func TestMigrateObserverParams(t *testing.T) { t.Run("Migrate when keygen is Pending", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) k.SetKeygen(ctx, types.Keygen{ Status: types.KeygenStatus_PendingKeygen, BlockNumber: math.MaxInt64, @@ -57,7 +57,7 @@ func TestMigrateObserverParams(t *testing.T) { require.Equal(t, participantList, participantList) }) t.Run("Migrate when keygen is not Pending", func(t *testing.T) { - k, ctx, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) participantList := []string{ "zetapub1addwnpepqglunjrgl3qg08duxq9pf28jmvrer3crwnnfzp6m0u0yh9jk9mnn5p76utc", "zetapub1addwnpepqwwpjwwnes7cywfkr0afme7ymk8rf5jzhn8pfr6qqvfm9v342486qsrh4f5", diff --git a/x/observer/migrations/v7/migrate.go b/x/observer/migrations/v7/migrate.go new file mode 100644 index 0000000000..2d69977f55 --- /dev/null +++ b/x/observer/migrations/v7/migrate.go @@ -0,0 +1,52 @@ +package v7 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +// observerKeeper prevents circular dependency +type observerKeeper interface { + GetParams(ctx sdk.Context) (params types.Params) + GetAuthorityKeeper() types.AuthorityKeeper +} + +// MigrateStore performs in-place store migrations from v6 to v7 +func MigrateStore(ctx sdk.Context, observerKeeper observerKeeper) error { + return MigratePolicies(ctx, observerKeeper) +} + +// MigratePolicies migrates policies from observer to authority +func MigratePolicies(ctx sdk.Context, observerKeeper observerKeeper) error { + params := observerKeeper.GetParams(ctx) + authorityKeeper := observerKeeper.GetAuthorityKeeper() + + var policies authoritytypes.Policies + + // convert observer policies to authority policies + for _, adminPolicy := range params.AdminPolicy { + if adminPolicy != nil { + + // Convert group1 -> emergency and group2 -> admin + policyType := authoritytypes.PolicyType_groupAdmin + if adminPolicy.PolicyType == types.Policy_Type_group1 { + policyType = authoritytypes.PolicyType_groupEmergency + } + + policies.Items = append(policies.Items, &authoritytypes.Policy{ + Address: adminPolicy.Address, + PolicyType: policyType, + }) + } + } + + // ensure policies are valid + if err := policies.Validate(); err != nil { + return err + } + + // set policies in authority + authorityKeeper.SetPolicies(ctx, policies) + return nil +} diff --git a/x/observer/migrations/v7/migrate_test.go b/x/observer/migrations/v7/migrate_test.go new file mode 100644 index 0000000000..d0379ece3e --- /dev/null +++ b/x/observer/migrations/v7/migrate_test.go @@ -0,0 +1,163 @@ +package v7_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + v7 "github.com/zeta-chain/zetacore/x/observer/migrations/v7" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMigrateStore(t *testing.T) { + t.Run("Migrate store from v6 to v7", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + addr1 := sample.AccAddress() + addr2 := sample.AccAddress() + + k.SetParams(ctx, types.Params{ + AdminPolicy: []*types.Admin_Policy{ + { + PolicyType: types.Policy_Type_group1, + Address: addr1, + }, + { + PolicyType: types.Policy_Type_group2, + Address: addr2, + }, + }, + }) + + // Migrate store + err := v7.MigrateStore(ctx, k) + + // Check if store is migrated + require.NoError(t, err) + }) +} + +func TestMigratePolicies(t *testing.T) { + t.Run("Migrate policies from observer to authority with 2 types", func(t *testing.T) { + k, ctx, _, zk := keepertest.ObserverKeeper(t) + + addr1 := sample.AccAddress() + addr2 := sample.AccAddress() + + k.SetParams(ctx, types.Params{ + AdminPolicy: []*types.Admin_Policy{ + { + PolicyType: types.Policy_Type_group1, + Address: addr1, + }, + { + PolicyType: types.Policy_Type_group2, + Address: addr2, + }, + }, + }) + + // Migrate policies + err := v7.MigratePolicies(ctx, k) + + // Check if policies are migrated + require.NoError(t, err) + policies, found := zk.AuthorityKeeper.GetPolicies(ctx) + require.True(t, found) + items := policies.Items + require.Len(t, items, 2) + require.EqualValues(t, addr1, items[0].Address) + require.EqualValues(t, addr2, items[1].Address) + require.EqualValues(t, authoritytypes.PolicyType_groupEmergency, items[0].PolicyType) + require.EqualValues(t, authoritytypes.PolicyType_groupAdmin, items[1].PolicyType) + }) + + t.Run("Can migrate with just emergency policy", func(t *testing.T) { + k, ctx, _, zk := keepertest.ObserverKeeper(t) + + addr := sample.AccAddress() + + k.SetParams(ctx, types.Params{ + AdminPolicy: []*types.Admin_Policy{ + { + PolicyType: types.Policy_Type_group1, + Address: addr, + }, + }, + }) + + // Migrate policies + err := v7.MigratePolicies(ctx, k) + + // Check if policies are migrated + require.NoError(t, err) + policies, found := zk.AuthorityKeeper.GetPolicies(ctx) + require.True(t, found) + items := policies.Items + require.Len(t, items, 1) + require.EqualValues(t, addr, items[0].Address) + require.EqualValues(t, authoritytypes.PolicyType_groupEmergency, items[0].PolicyType) + }) + + t.Run("Can migrate with just admin policy", func(t *testing.T) { + k, ctx, _, zk := keepertest.ObserverKeeper(t) + + addr := sample.AccAddress() + + k.SetParams(ctx, types.Params{ + AdminPolicy: []*types.Admin_Policy{ + { + PolicyType: types.Policy_Type_group2, + Address: addr, + }, + }, + }) + + // Migrate policies + err := v7.MigratePolicies(ctx, k) + + // Check if policies are migrated + require.NoError(t, err) + policies, found := zk.AuthorityKeeper.GetPolicies(ctx) + require.True(t, found) + items := policies.Items + require.Len(t, items, 1) + require.EqualValues(t, addr, items[0].Address) + require.EqualValues(t, authoritytypes.PolicyType_groupAdmin, items[0].PolicyType) + }) + + t.Run("Can migrate with no policies", func(t *testing.T) { + k, ctx, _, zk := keepertest.ObserverKeeper(t) + + k.SetParams(ctx, types.Params{}) + + // Migrate policies + err := v7.MigratePolicies(ctx, k) + + // Check if policies are migrated + require.NoError(t, err) + policies, found := zk.AuthorityKeeper.GetPolicies(ctx) + require.True(t, found) + items := policies.Items + require.Len(t, items, 0) + }) + + t.Run("Fail to migrate if invalid policy", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + k.SetParams(ctx, types.Params{ + AdminPolicy: []*types.Admin_Policy{ + { + PolicyType: types.Policy_Type_group1, + Address: "invalid", + }, + }, + }) + + // Migrate policies + err := v7.MigratePolicies(ctx, k) + require.Error(t, err) + }) +} diff --git a/x/observer/module.go b/x/observer/module.go index efb851964d..d6f9ba1d11 100644 --- a/x/observer/module.go +++ b/x/observer/module.go @@ -153,6 +153,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 5, m.Migrate5to6); err != nil { panic(err) } + if err := cfg.RegisterMigration(types.ModuleName, 6, m.Migrate6to7); err != nil { + panic(err) + } } // RegisterInvariants registers the observer module's invariants. @@ -177,7 +180,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 6 } +func (AppModule) ConsensusVersion() uint64 { return 7 } // BeginBlock executes all ABCI BeginBlock logic respective to the observer module. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { diff --git a/x/observer/types/expected_keepers.go b/x/observer/types/expected_keepers.go index b72c74a766..ba1c9ab9c2 100644 --- a/x/observer/types/expected_keepers.go +++ b/x/observer/types/expected_keepers.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" ) type StakingKeeper interface { @@ -23,3 +24,10 @@ type StakingHooks interface { AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error } + +type AuthorityKeeper interface { + IsAuthorized(ctx sdk.Context, address string, policyType authoritytypes.PolicyType) bool + + // SetPolicies is solely used for the migration of policies from observer to authority + SetPolicies(ctx sdk.Context, policies authoritytypes.Policies) +} diff --git a/x/observer/types/message_crosschain_flags.go b/x/observer/types/message_crosschain_flags.go index 58d49cd3d3..05ee4d331e 100644 --- a/x/observer/types/message_crosschain_flags.go +++ b/x/observer/types/message_crosschain_flags.go @@ -3,6 +3,8 @@ package types import ( "errors" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" @@ -68,20 +70,22 @@ func (gpf GasPriceIncreaseFlags) Validate() error { return nil } -// GetRequiredGroup returns the required group policy for the message to execute the message -// Group 1 should only be able to stop or disable functiunalities in case of emergency +// GetRequiredPolicyType returns the required policy type for the message to execute the message +// Group emergency should only be able to stop or disable functionalities in case of emergency // this concerns disabling inbound and outbound txs or block header verification -// every other action requires group 2 -func (msg *MsgUpdateCrosschainFlags) GetRequiredGroup() Policy_Type { +// every other action requires group admin +// TODO: add separate message for each group +// https://github.com/zeta-chain/node/issues/1562 +func (msg *MsgUpdateCrosschainFlags) GetRequiredPolicyType() authoritytypes.PolicyType { if msg.IsInboundEnabled || msg.IsOutboundEnabled { - return Policy_Type_group2 + return authoritytypes.PolicyType_groupAdmin } if msg.GasPriceIncreaseFlags != nil { - return Policy_Type_group2 + return authoritytypes.PolicyType_groupAdmin } if msg.BlockHeaderVerificationFlags != nil && (msg.BlockHeaderVerificationFlags.IsEthTypeChainEnabled || msg.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled) { - return Policy_Type_group2 + return authoritytypes.PolicyType_groupAdmin } - return Policy_Type_group1 + return authoritytypes.PolicyType_groupEmergency } diff --git a/x/observer/types/message_crosschain_flags_test.go b/x/observer/types/message_crosschain_flags_test.go index 44fab49bbf..1ccd1d7040 100644 --- a/x/observer/types/message_crosschain_flags_test.go +++ b/x/observer/types/message_crosschain_flags_test.go @@ -6,6 +6,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -117,11 +118,11 @@ func TestGasPriceIncreaseFlags_Validate(t *testing.T) { } } -func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { +func TestMsgUpdateCrosschainFlags_GetRequiredPolicyType(t *testing.T) { tests := []struct { name string msg types.MsgUpdateCrosschainFlags - want types.Policy_Type + want authoritytypes.PolicyType }{ { name: "disabling outbound and inbound allows group 1", @@ -132,7 +133,7 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { BlockHeaderVerificationFlags: nil, GasPriceIncreaseFlags: nil, }, - want: types.Policy_Type_group1, + want: authoritytypes.PolicyType_groupEmergency, }, { name: "disabling outbound and inbound and block header verification allows group 1", @@ -146,7 +147,7 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { }, GasPriceIncreaseFlags: nil, }, - want: types.Policy_Type_group1, + want: authoritytypes.PolicyType_groupEmergency, }, { name: "updating gas price increase flags asserts group 2", @@ -165,7 +166,7 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { MaxPendingCctxs: 100, }, }, - want: types.Policy_Type_group2, + want: authoritytypes.PolicyType_groupAdmin, }, { name: "enabling inbound asserts group 2", @@ -179,7 +180,7 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { }, GasPriceIncreaseFlags: nil, }, - want: types.Policy_Type_group2, + want: authoritytypes.PolicyType_groupAdmin, }, { name: "enabling outbound asserts group 2", @@ -193,7 +194,7 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { }, GasPriceIncreaseFlags: nil, }, - want: types.Policy_Type_group2, + want: authoritytypes.PolicyType_groupAdmin, }, { name: "enabling eth header verification asserts group 2", @@ -207,7 +208,7 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { }, GasPriceIncreaseFlags: nil, }, - want: types.Policy_Type_group2, + want: authoritytypes.PolicyType_groupAdmin, }, { name: "enabling btc header verification asserts group 2", @@ -221,13 +222,13 @@ func TestMsgUpdateCrosschainFlags_GetRequiredGroup(t *testing.T) { }, GasPriceIncreaseFlags: nil, }, - want: types.Policy_Type_group2, + want: authoritytypes.PolicyType_groupAdmin, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.want, tt.msg.GetRequiredGroup()) + require.EqualValues(t, tt.want, tt.msg.GetRequiredPolicyType()) }) } } diff --git a/x/observer/types/params.go b/x/observer/types/params.go index 13910baa97..5dda864446 100644 --- a/x/observer/types/params.go +++ b/x/observer/types/params.go @@ -90,6 +90,7 @@ func validateVotingThresholds(i interface{}) error { } return nil } + func validateAdminPolicy(i interface{}) error { _, ok := i.([]*Admin_Policy) if !ok { @@ -98,6 +99,7 @@ func validateAdminPolicy(i interface{}) error { return nil } + func validateBallotMaturityBlocks(i interface{}) error { _, ok := i.(int64) if !ok { diff --git a/x/observer/types/params.pb.go b/x/observer/types/params.pb.go index 066685bb22..c2d571d7a2 100644 --- a/x/observer/types/params.pb.go +++ b/x/observer/types/params.pb.go @@ -26,6 +26,7 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// Deprecated(v14):Moved into the authority module type Policy_Type int32 const ( @@ -229,7 +230,7 @@ func (m *ChainParams) GetIsSupported() bool { return false } -// Deprecated: Use ChainParamsList +// Deprecated(v13): Use ChainParamsList type ObserverParams struct { Chain *common.Chain `protobuf:"bytes,1,opt,name=chain,proto3" json:"chain,omitempty"` BallotThreshold github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=ballot_threshold,json=ballotThreshold,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"ballot_threshold"` @@ -284,6 +285,7 @@ func (m *ObserverParams) GetIsSupported() bool { return false } +// Deprecated(v14):Moved into the authority module type Admin_Policy struct { PolicyType Policy_Type `protobuf:"varint,1,opt,name=policy_type,json=policyType,proto3,enum=zetachain.zetacore.observer.Policy_Type" json:"policy_type,omitempty"` Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` @@ -338,10 +340,11 @@ func (m *Admin_Policy) GetAddress() string { // Params defines the parameters for the module. type Params struct { - // Deprecated: Use ChainParamsList - ObserverParams []*ObserverParams `protobuf:"bytes,1,rep,name=observer_params,json=observerParams,proto3" json:"observer_params,omitempty"` - AdminPolicy []*Admin_Policy `protobuf:"bytes,2,rep,name=admin_policy,json=adminPolicy,proto3" json:"admin_policy,omitempty"` - BallotMaturityBlocks int64 `protobuf:"varint,3,opt,name=ballot_maturity_blocks,json=ballotMaturityBlocks,proto3" json:"ballot_maturity_blocks,omitempty"` + // Deprecated(v13): Use ChainParamsList + ObserverParams []*ObserverParams `protobuf:"bytes,1,rep,name=observer_params,json=observerParams,proto3" json:"observer_params,omitempty"` + // Deprecated(v14):Moved into the authority module + AdminPolicy []*Admin_Policy `protobuf:"bytes,2,rep,name=admin_policy,json=adminPolicy,proto3" json:"admin_policy,omitempty"` + BallotMaturityBlocks int64 `protobuf:"varint,3,opt,name=ballot_maturity_blocks,json=ballotMaturityBlocks,proto3" json:"ballot_maturity_blocks,omitempty"` } func (m *Params) Reset() { *m = Params{} }