From 6ca2f007888558d942a83ef382d6eac6cd71063d Mon Sep 17 00:00:00 2001 From: sotnikov-s Date: Tue, 17 Sep 2024 11:49:28 +0300 Subject: [PATCH 1/3] add chain manager software upgrade strategies tests --- .../run_in_band/chain_manager.test.ts | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) diff --git a/src/testcases/run_in_band/chain_manager.test.ts b/src/testcases/run_in_band/chain_manager.test.ts index a2f78d41..c8c6f348 100644 --- a/src/testcases/run_in_band/chain_manager.test.ts +++ b/src/testcases/run_in_band/chain_manager.test.ts @@ -19,9 +19,12 @@ import { setupSubDaoTimelockSet } from '../../helpers/dao'; import { QueryClientImpl as CronQueryClient } from '@neutron-org/neutronjs/neutron/cron/query.rpc.Query'; import { QueryClientImpl as AdminQueryClient } from '@neutron-org/neutronjs/cosmos/adminmodule/adminmodule/query.rpc.Query'; import { QueryClientImpl as TokenfactoryQueryClient } from '@neutron-org/neutronjs/osmosis/tokenfactory/v1beta1/query.rpc.Query'; +import { QueryClientImpl as UpgradeQueryClient } from '@neutron-org/neutronjs/cosmos/upgrade/v1beta1/query.rpc.Query'; import { QueryClientImpl as DexQueryClient } from '@neutron-org/neutronjs/neutron/dex/query.rpc.Query'; import { SigningNeutronClient } from '../../helpers/signing_neutron_client'; import config from '../../config.json'; +import { Wallet } from '../../helpers/wallet'; +import { ADMIN_MODULE_ADDRESS } from '@neutron-org/neutronjsplus/dist/constants'; describe('Neutron / Chain Manager', () => { let testState: LocalState; @@ -34,6 +37,7 @@ describe('Neutron / Chain Manager', () => { let cronQuerier: CronQueryClient; let tokenfactoryQuerier: TokenfactoryQueryClient; let dexQuerier: DexQueryClient; + let upgradeQuerier: UpgradeQueryClient; let chainManagerAddress: string; beforeAll(async (suite: RunnerTestSuite) => { @@ -90,6 +94,7 @@ describe('Neutron / Chain Manager', () => { tokenfactoryQuerier = new TokenfactoryQueryClient(neutronRpcClient); cronQuerier = new CronQueryClient(neutronRpcClient); dexQuerier = new DexQueryClient(neutronRpcClient); + upgradeQuerier = new UpgradeQueryClient(neutronRpcClient); }); // We need to do this because the real main dao has a super long voting period. @@ -314,6 +319,7 @@ describe('Neutron / Chain Manager', () => { ]); }); }); + describe('ALLOW_ONLY: change DEX parameters', () => { let proposalId: number; const newParams = { @@ -356,4 +362,305 @@ describe('Neutron / Chain Manager', () => { expect(dexParams.params.goodTilPurgeAllowance).toEqual(50000n); }); }); + + describe('ALLOW_ONLY: software upgrade', () => { + // only upgrade permission + let upgradeOnlyClient: SigningNeutronClient; + let upgradeOnlyWallet: Wallet; + + // only cancel upgrade permission + let cancelUpgradeOnlyClient: SigningNeutronClient; + let cancelUpgradeOnlyWallet: Wallet; + + // upgrade and cancel upgrade permissions + let fullUpgradeAccessClient: SigningNeutronClient; + let fullUpgradeAccessWallet: Wallet; + + beforeAll(async () => { + upgradeOnlyWallet = await testState.nextWallet('neutron'); + upgradeOnlyClient = await SigningNeutronClient.connectWithSigner( + testState.rpcNeutron, + upgradeOnlyWallet.directwallet, + upgradeOnlyWallet.address, + ); + + cancelUpgradeOnlyWallet = await testState.nextWallet('neutron'); + cancelUpgradeOnlyClient = await SigningNeutronClient.connectWithSigner( + testState.rpcNeutron, + cancelUpgradeOnlyWallet.directwallet, + cancelUpgradeOnlyWallet.address, + ); + + fullUpgradeAccessWallet = await testState.nextWallet('neutron'); + fullUpgradeAccessClient = await SigningNeutronClient.connectWithSigner( + testState.rpcNeutron, + fullUpgradeAccessWallet.directwallet, + fullUpgradeAccessWallet.address, + ); + }); + + // grant the expected chain manager strategies to the wallets using Neutron DAO proposal + describe('assign software upgrade permissions', async () => { + let proposalId: number; + it('create proposal', async () => { + const assignUpgradeMsg = buildAddSoftwareUpgradeStrategyMsg( + upgradeOnlyWallet.address, + true, + false, + ); + const assignCancelMsg = buildAddSoftwareUpgradeStrategyMsg( + cancelUpgradeOnlyWallet.address, + false, + true, + ); + const assignFullMsg = buildAddSoftwareUpgradeStrategyMsg( + fullUpgradeAccessWallet.address, + true, + true, + ); + + proposalId = await mainDaoMember.submitSingleChoiceProposal( + 'Assign software upgrade permissions', + 'Assign mixed software upgrade permissions to three authorities', + [ + { + wasm: { + execute: { + contract_addr: chainManagerAddress, + msg: Buffer.from(JSON.stringify(assignUpgradeMsg)).toString( + 'base64', + ), + funds: [], + }, + }, + }, + { + wasm: { + execute: { + contract_addr: chainManagerAddress, + msg: Buffer.from(JSON.stringify(assignCancelMsg)).toString( + 'base64', + ), + funds: [], + }, + }, + }, + { + wasm: { + execute: { + contract_addr: chainManagerAddress, + msg: Buffer.from(JSON.stringify(assignFullMsg)).toString( + 'base64', + ), + funds: [], + }, + }, + }, + ], + '1000', + ); + }); + + it('vote and execute', async () => { + await mainDaoMember.voteYes(proposalId); + await mainDao.checkPassedProposal(proposalId); + await mainDaoMember.executeProposalWithAttempts(proposalId); + }); + }); + + describe('check software upgrade permissions', () => { + it('random account cannot set or cancel', async () => { + await expect( + neutronClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildSetSoftwareUpgradeMsg('name', 100000, 'info')], + }, + }, + [], + 'auto', + ), + ).rejects.toThrow(/Unauthorized/); + await expect( + neutronClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildCancelSoftwareUpgradeMsg()], + }, + }, + [], + 'auto', + ), + ).rejects.toThrow(/Unauthorized/); + }); + + it('only cancel cannot set', async () => { + await expect( + cancelUpgradeOnlyClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildSetSoftwareUpgradeMsg('name', 100000, 'info')], + }, + }, + [], + 'auto', + ), + ).rejects.toThrow(/Unauthorized/); + }); + + it('only set cannot cancel', async () => { + await expect( + upgradeOnlyClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildCancelSoftwareUpgradeMsg()], + }, + }, + [], + 'auto', + ), + ).rejects.toThrow(/Unauthorized/); + }); + + describe('full access can both set and cancel', () => { + it('assert no plan is currently configured', async () => { + const plan = await upgradeQuerier.currentPlan({}); + expect(plan.plan).toBeUndefined(); + }); + + it('set software upgrade', async () => { + await fullUpgradeAccessClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildSetSoftwareUpgradeMsg('name', 100000, 'info')], + }, + }, + [], + 'auto', + ); + + const plan = await upgradeQuerier.currentPlan({}); + expect(plan.plan.height.toString()).toEqual('100000'); + }); + + it('cancel software upgrade', async () => { + await fullUpgradeAccessClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildCancelSoftwareUpgradeMsg()], + }, + }, + [], + 'auto', + ); + + const plan = await upgradeQuerier.currentPlan({}); + expect(plan.plan).toBeUndefined(); + }); + }); + + describe('limited access can do what they are granted to do', () => { + it('only set can set', async () => { + await upgradeOnlyClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildSetSoftwareUpgradeMsg('name', 100000, 'info')], + }, + }, + [], + 'auto', + ); + + const plan = await upgradeQuerier.currentPlan({}); + expect(plan.plan.height.toString()).toEqual('100000'); + }); + + it('only cancel can cancel', async () => { + await cancelUpgradeOnlyClient.execute( + chainManagerAddress, + { + execute_messages: { + messages: [buildCancelSoftwareUpgradeMsg()], + }, + }, + [], + 'auto', + ); + + const plan = await upgradeQuerier.currentPlan({}); + expect(plan.plan).toBeUndefined(); + }); + }); + }); + }); +}); + +// returns a wasm execute message to a chain manager that adds software upgrade permissions to +// a given authority. +const buildAddSoftwareUpgradeStrategyMsg = ( + authority: string, + upgrade: boolean, + cancelUpgrade: boolean, +): any => ({ + add_strategy: { + address: authority, + strategy: { + allow_only: [ + { + software_upgrade_permission: { + upgrade: upgrade, + cancel_upgrade: cancelUpgrade, + }, + }, + ], + }, + }, +}); + +// returns an adminmodule admin proposal message that creates a software upgrade plan. +const buildSetSoftwareUpgradeMsg = ( + name: string, + height: number, + info: string, +): any => ({ + custom: { + submit_admin_proposal: { + admin_proposal: { + proposal_execute_message: { + message: JSON.stringify({ + '@type': '/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade', + authority: ADMIN_MODULE_ADDRESS, + plan: { + name, + height, + info, + }, + }), + }, + }, + }, + }, +}); + +// returns an adminmodule admin proposal message that cancens the current software upgrade plan. +const buildCancelSoftwareUpgradeMsg = (): any => ({ + custom: { + submit_admin_proposal: { + admin_proposal: { + proposal_execute_message: { + message: JSON.stringify({ + '@type': '/cosmos.upgrade.v1beta1.MsgCancelUpgrade', + authority: ADMIN_MODULE_ADDRESS, + }), + }, + }, + }, + }, }); From 7d3a62bd99540155286b572d3dc61f1c7d091e86 Mon Sep 17 00:00:00 2001 From: sotnikov-s Date: Tue, 17 Sep 2024 19:09:09 +0300 Subject: [PATCH 2/3] prettify chain manager test code description --- src/testcases/run_in_band/chain_manager.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/testcases/run_in_band/chain_manager.test.ts b/src/testcases/run_in_band/chain_manager.test.ts index c8c6f348..783c7eab 100644 --- a/src/testcases/run_in_band/chain_manager.test.ts +++ b/src/testcases/run_in_band/chain_manager.test.ts @@ -469,6 +469,8 @@ describe('Neutron / Chain Manager', () => { }); describe('check software upgrade permissions', () => { + // checking neutronClient's interaction with chain manager whereas this account + // hasn't been given any permissions (the proposal above contains other authorities) it('random account cannot set or cancel', async () => { await expect( neutronClient.execute( @@ -496,7 +498,7 @@ describe('Neutron / Chain Manager', () => { ).rejects.toThrow(/Unauthorized/); }); - it('only cancel cannot set', async () => { + it('onlyCancel cannot set', async () => { await expect( cancelUpgradeOnlyClient.execute( chainManagerAddress, @@ -511,7 +513,7 @@ describe('Neutron / Chain Manager', () => { ).rejects.toThrow(/Unauthorized/); }); - it('only set cannot cancel', async () => { + it('onlySet cannot cancel', async () => { await expect( upgradeOnlyClient.execute( chainManagerAddress, @@ -566,7 +568,7 @@ describe('Neutron / Chain Manager', () => { }); describe('limited access can do what they are granted to do', () => { - it('only set can set', async () => { + it('onlySet can set', async () => { await upgradeOnlyClient.execute( chainManagerAddress, { @@ -582,7 +584,7 @@ describe('Neutron / Chain Manager', () => { expect(plan.plan.height.toString()).toEqual('100000'); }); - it('only cancel can cancel', async () => { + it('onlyCancel can cancel', async () => { await cancelUpgradeOnlyClient.execute( chainManagerAddress, { From e0363a012ab68d10bb1eb71a59439cec6eae15ec Mon Sep 17 00:00:00 2001 From: sotnikov-s Date: Wed, 18 Sep 2024 00:15:38 +0300 Subject: [PATCH 3/3] rm redundant sleep in cron chain manager test --- src/testcases/run_in_band/chain_manager.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/testcases/run_in_band/chain_manager.test.ts b/src/testcases/run_in_band/chain_manager.test.ts index 960c9687..afc70de1 100644 --- a/src/testcases/run_in_band/chain_manager.test.ts +++ b/src/testcases/run_in_band/chain_manager.test.ts @@ -416,7 +416,6 @@ describe('Neutron / Chain Manager', () => { const scheduleName = 'schedule1'; test('add cron schedule', async () => { - await neutronClient.waitBlocks(5); await cronAccessClient.execute( chainManagerAddress, {