Skip to content

Commit

Permalink
fix: fund-redeem test (#3982)
Browse files Browse the repository at this point in the history
* fix: fund-redeem test

* test: add multi governance members test to concurrent

* chore: fmt
  • Loading branch information
kylezs authored Sep 11, 2023
1 parent bf0f2d0 commit 48e5697
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 115 deletions.
4 changes: 1 addition & 3 deletions bouncer/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,4 @@ if [[ $LOCALNET == false ]]; then
else
echo "🚀 Running tests that require localnet"
./tests/swap_after_temp_disconnecting_chains.ts
fi

./tests/multiple_members_governance.ts
fi
8 changes: 4 additions & 4 deletions bouncer/shared/fund_flip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { observeEvent, getChainflipApi, amountToFineAmount } from '../shared/utils';
import { approveErc20 } from './approve_erc20';

export async function fundFlip(address: string, flipAmount: string) {
export async function fundFlip(scAddress: string, flipAmount: string) {
const chainflip = await getChainflipApi();
await cryptoWaitReady();

Expand Down Expand Up @@ -44,10 +44,10 @@ export async function fundFlip(address: string, flipAmount: string) {
nonce: BigInt(await getNextEthNonce()),
} as const;

console.log('Funding ' + flipAmount + ' FLIP to ' + address);
let pubkey = address;
console.log('Funding ' + flipAmount + ' FLIP to ' + scAddress);
let pubkey = scAddress;
try {
pubkey = decodeFlipAddressForContract(address);
pubkey = decodeFlipAddressForContract(scAddress);
} catch {
// ignore error
}
Expand Down
29 changes: 29 additions & 0 deletions bouncer/shared/fund_redeem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { HexString } from '@polkadot/util/types';
import { newAddress, observeBalanceIncrease } from '../shared/utils';
import { getBalance } from '../shared/get_balance';
import { fundFlip } from '../shared/fund_flip';
import { redeemFlip } from '../shared/redeem_flip';
import { newStatechainAddress } from '../shared/new_statechain_address';

// Uses the seed to generate a new SC address and ETH address.
// It then funds the SC address with FLIP, and redeems the FLIP to the ETH address
// checking that the balance has increased.
export async function testFundRedeem(seed: string) {
const redeemSCAddress = await newStatechainAddress(seed);
const redeemEthAddress = await newAddress('ETH', seed);
console.log(`FLIP Redeem address: ${redeemSCAddress}`);
console.log(`ETH Redeem address: ${redeemEthAddress}`);
const initBalance = await getBalance('FLIP', redeemEthAddress);
console.log(`Initial ERC20-FLIP balance: ${initBalance.toString()}`);
const amount = 1000;
// We fund to a specific SC address.
await fundFlip(redeemSCAddress, amount.toString());

// The ERC20 FLIP is sent back to an ETH address, and the registered claim can only be executed by that address.
await redeemFlip(seed, redeemEthAddress as HexString, (amount / 2).toString());
console.log('Observed RedemptionSettled event');
const newBalance = await observeBalanceIncrease('FLIP', redeemEthAddress, initBalance);
console.log(`Redemption success! New balance: ${newBalance.toString()}`);
console.log('=== Fund/Redeem Test Success ===');
process.exit(0);
}
72 changes: 72 additions & 0 deletions bouncer/shared/multiple_members_governance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Keyring from '@polkadot/keyring';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { assert } from '@polkadot/util';
import { getChainflipApi, observeEvent } from '../shared/utils';
import { snowWhite, submitGovernanceExtrinsic } from '../shared/cf_governance';

async function getGovernanceMembers(): Promise<string[]> {
const chainflip = await getChainflipApi();

const res = (await chainflip.query.governance.members()).toJSON();
return res as string[];
}

async function setGovernanceMembers(members: string[]) {
const chainflip = await getChainflipApi();

await submitGovernanceExtrinsic(chainflip.tx.governance.newMembershipSet(members));
}

await cryptoWaitReady();
const keyring = new Keyring({ type: 'sr25519' });
keyring.setSS58Format(2112);

const alice = keyring.createFromUri('//Alice');

async function addAliceToGovernance() {
const initMembers = await getGovernanceMembers();
assert(initMembers.length === 1, 'Governance should only have 1 member');

const newMembers = [...initMembers, alice.address];

await setGovernanceMembers(newMembers);

const chainflip = await getChainflipApi();
await observeEvent('governance:Executed', chainflip);

assert((await getGovernanceMembers()).length === 2, 'Governance should now have 2 members');

console.log('Added Alice to governance!');
}

async function submitWithMultipleGovernanceMembers() {
const chainflip = await getChainflipApi();

// Killing 2 birds with 1 stone: testing governance execution with multiple
// members *and* restoring governance to its original state
await submitGovernanceExtrinsic(chainflip.tx.governance.newMembershipSet([snowWhite.address]));

const proposalId = Number((await observeEvent('governance:Proposed', chainflip)).data);

// Note that with two members, we need to approve with the other account:
await chainflip.tx.governance.approve(proposalId).signAndSend(alice, { nonce: -1 });

const executedProposalId = Number((await observeEvent('governance:Executed', chainflip)).data);
assert(proposalId === executedProposalId, 'Proposal Ids should match');

assert(
(await getGovernanceMembers()).length === 1,
'Governance should have been restored to 1 member',
);

console.log('Removed Alice from governance!');
}

export async function testMultipleMembersGovernance() {
console.log('=== Testing multiple members governance ===');
await addAliceToGovernance();
await submitWithMultipleGovernanceMembers();

console.log('=== Multiple members governance test complete ===');
process.exit(0);
}
3 changes: 2 additions & 1 deletion bouncer/shared/redeem_flip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function redeemFlip(flipSeed: string, ethAddress: HexString, flipAm
(event) => event.data.accountId === flipWallet.address,
);
await chainflip.tx.funding
.redeem({ Exact: flipperinoAmount }, ethAddress)
.redeem({ Exact: flipperinoAmount }, ethAddress, null)
.signAndSend(flipWallet, { nonce: -1 }, handleSubstrateError(chainflip));
await redemptionRequestHandle;

Expand All @@ -52,6 +52,7 @@ export async function redeemFlip(flipSeed: string, ethAddress: HexString, flipAm
ethAddress,
'*',
'*',
'*',
]);

const delay = await getRedemptionDelay(networkOptions);
Expand Down
10 changes: 9 additions & 1 deletion bouncer/tests/all_concurrent_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import { testLpDepositExpiry } from '../shared/lp_deposit_expiry';
import { testAllSwaps } from '../shared/swapping';
import { testEthereumDeposits } from '../shared/ethereum_deposits';
import { runWithTimeout, observeBadEvents } from '../shared/utils';
import { testFundRedeem } from '../shared/fund_redeem';
import { testMultipleMembersGovernance } from '../shared/multiple_members_governance';

async function runAllConcurrentTests() {
let stopObserving = false;
const observingBadEvents = observeBadEvents(':BroadcastAborted', () => stopObserving);

await Promise.all([testAllSwaps(), testLpDepositExpiry(), testEthereumDeposits()]);
await Promise.all([
testAllSwaps(),
testLpDepositExpiry(),
testEthereumDeposits(),
testFundRedeem('redeem'),
testMultipleMembersGovernance(),
]);

// Gracefully exit the broadcast abort observer
stopObserving = true;
Expand Down
27 changes: 3 additions & 24 deletions bouncer/tests/fund_redeem.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,8 @@
#!/usr/bin/env -S pnpm tsx
import { HexString } from '@polkadot/util/types';
import { newAddress, observeBalanceIncrease, runWithTimeout } from '../shared/utils';
import { getBalance } from '../shared/get_balance';
import { fundFlip } from '../shared/fund_flip';
import { redeemFlip } from '../shared/redeem_flip';
import { newStatechainAddress } from '../shared/new_statechain_address';
import { testFundRedeem } from '../shared/fund_redeem';
import { runWithTimeout } from '../shared/utils';

export async function fundRedeemTest() {
const seed = 'redeem';
const redeemFlipAddress = await newStatechainAddress(seed);
const redeemEthAddress = await newAddress('ETH', seed);
console.log(`FLIP Redeem address: ${redeemFlipAddress}`);
console.log(`ETH Redeem address: ${redeemEthAddress}`);
const initBalance = await getBalance('FLIP', redeemEthAddress);
console.log(`Initial ERC20-FLIP balance: ${initBalance.toString()}`);
const amount = 1000;
await fundFlip(redeemFlipAddress, amount.toString());
await redeemFlip(seed, redeemEthAddress as HexString, (amount / 2).toString());
console.log('Observed RedemptionSettled event');
const newBalance = await observeBalanceIncrease('FLIP', redeemEthAddress, initBalance);
console.log(`Redemption success! New balance: ${newBalance.toString()}`);
process.exit(0);
}

runWithTimeout(fundRedeemTest(), 600000).catch((error) => {
runWithTimeout(testFundRedeem('redeem'), 600000).catch((error) => {
console.error(error);
process.exit(-1);
});
75 changes: 3 additions & 72 deletions bouncer/tests/multiple_members_governance.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,9 @@
#!/usr/bin/env -S pnpm tsx
import Keyring from '@polkadot/keyring';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { assert } from '@polkadot/util';
import { getChainflipApi, observeEvent, runWithTimeout } from '../shared/utils';
import { snowWhite, submitGovernanceExtrinsic } from '../shared/cf_governance';

async function getGovernanceMembers(): Promise<string[]> {
const chainflip = await getChainflipApi();
import { runWithTimeout } from '../shared/utils';
import { testMultipleMembersGovernance } from '../shared/multiple_members_governance';

const res = (await chainflip.query.governance.members()).toJSON();
return res as string[];
}

async function setGovernanceMembers(members: string[]) {
const chainflip = await getChainflipApi();

await submitGovernanceExtrinsic(chainflip.tx.governance.newMembershipSet(members));
}

await cryptoWaitReady();
const keyring = new Keyring({ type: 'sr25519' });
keyring.setSS58Format(2112);

const alice = keyring.createFromUri('//Alice');

async function addAliceToGovernance() {
const initMembers = await getGovernanceMembers();
assert(initMembers.length === 1, 'Governance should only have 1 member');

const newMembers = [...initMembers, alice.address];

await setGovernanceMembers(newMembers);

const chainflip = await getChainflipApi();
await observeEvent('governance:Executed', chainflip);

assert((await getGovernanceMembers()).length === 2, 'Governance should now have 2 members');

console.log('Added Alice to governance!');
}

async function submitWithMultipleGovernanceMembers() {
const chainflip = await getChainflipApi();

// Killing 2 birds with 1 stone: testing governance execution with multiple
// members *and* restoring governance to its original state
await submitGovernanceExtrinsic(chainflip.tx.governance.newMembershipSet([snowWhite.address]));

const proposalId = Number((await observeEvent('governance:Proposed', chainflip)).data);

// Note that with two members, we need to approve with the other account:
await chainflip.tx.governance.approve(proposalId).signAndSend(alice, { nonce: -1 });

const executedProposalId = Number((await observeEvent('governance:Executed', chainflip)).data);
assert(proposalId === executedProposalId, 'Proposal Ids should match');

assert(
(await getGovernanceMembers()).length === 1,
'Governance should have been restored to 1 member',
);

console.log('Removed Alice from governance!');
}

async function main() {
console.log('=== Testing multiple members governance ===');
await addAliceToGovernance();
await submitWithMultipleGovernanceMembers();

console.log('=== Multiple members governance test complete ===');
process.exit(0);
}

runWithTimeout(main(), 120000).catch((error) => {
runWithTimeout(testMultipleMembersGovernance(), 120000).catch((error) => {
console.error(error);
process.exit(-1);
});
11 changes: 1 addition & 10 deletions state-chain/pallets/cf-funding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,22 +343,13 @@ pub mod pallet {
/// An account can only have one pending redemption at a time, the funds wrapped up in the
/// pending redemption are inaccessible and are not counted towards a Validator's Auction
/// Bid.
///
/// ## Events
///
/// - None
///
/// ## Errors
///
/// - [PendingRedemption](Error::PendingRedemption)
/// - [AuctionPhase](Error::AuctionPhase)
/// - [WithdrawalAddressRestricted](Error::WithdrawalAddressRestricted)
#[pallet::call_index(1)]
#[pallet::weight({ if matches!(amount, RedemptionAmount::Exact(_)) { T::WeightInfo::redeem() } else { T::WeightInfo::redeem_all() }})]
pub fn redeem(
origin: OriginFor<T>,
amount: RedemptionAmount<FlipBalance<T>>,
address: EthereumAddress,
// Only this address can execute the claim.
executor: Option<EthereumAddress>,
) -> DispatchResultWithPostInfo {
let account_id = ensure_signed(origin)?;
Expand Down

0 comments on commit 48e5697

Please sign in to comment.