Skip to content

Commit

Permalink
fix: make e2e tests for passkey more rigorous in their success checks
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeCap08055 committed Dec 30, 2024
1 parent 8391ded commit b515dce
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 33 deletions.
105 changes: 86 additions & 19 deletions e2e/passkey/passkeyProxyV2.ethereum.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import '@frequency-chain/api-augment';
import assert from 'assert';
import {
createAndFundKeypair,
EcdsaSignature,
getBlockNumber,
getNonce,
Sr25519Signature,
} from '../scaffolding/helpers';
import { createAndFundKeypair, EcdsaSignature, getNonce, Sr25519Signature } from '../scaffolding/helpers';
import { KeyringPair } from '@polkadot/keyring/types';
import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers';
import { getFundingSource } from '../scaffolding/funding';
Expand All @@ -28,11 +22,13 @@ describe('Passkey Pallet Proxy V2 Ethereum Tests', function () {
});

it('should transfer via passkeys with root sr25519 key into an ethereum style account', async function () {
const startingBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
const accountPKey = getUnifiedPublicKey(fundedSr25519Keys);
const nonce = await getNonce(fundedSr25519Keys);
const transferAmount = 55_000_000n;
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedAddress(receiverKeys),
55_000_000n
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedSr25519Keys.sign(u8aWrapBytes(passKeyPublicKey));
Expand All @@ -46,21 +42,56 @@ describe('Passkey Pallet Proxy V2 Ethereum Tests', function () {
false
);
const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedSr25519Keys, passkeyPayload);
await assert.doesNotReject(passkeyProxy.fundAndSendUnsigned(fundingSource));
await ExtrinsicHelper.waitForFinalization((await getBlockNumber()) + 2);
// adding some delay before fetching the nonce to ensure it is updated
await new Promise((resolve) => setTimeout(resolve, 1000));
try {
const {
target,
eventMap: { 'balances.Transfer': transferEvent },
} = await passkeyProxy.fundAndSendUnsigned(fundingSource);
assert.notEqual(target, undefined, 'Target event should not be undefined');
assert.equal(
ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent),
true,
'Transfer event should be of correct type'
);
if (transferEvent && ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent)) {
const { from, to, amount } = transferEvent.data;
assert.equal(
from.toString(),
getUnifiedAddress(fundedSr25519Keys),
'From address should be the funded sr25519 key'
);
assert.equal(to.toString(), getUnifiedAddress(receiverKeys), 'To address should be the receiver key');
assert.equal(amount.toBigInt(), transferAmount, `Transfer amount should be ${transferAmount}`);
}
} catch (e: any) {
assert.fail(e);
}

/*
* Normally these checks would be unnecessary, but we are testing the passkey pallet
* which has additional logic surrounding mapping account keys, so we want to make sure
* that the nonce and balance are updated correctly.
*/
const nonceAfter = (await ExtrinsicHelper.getAccountInfo(fundedSr25519Keys)).nonce.toNumber();
assert.equal(nonce + 1, nonceAfter);
assert.equal(nonce + 1, nonceAfter, 'Nonce should be incremented by 1');

const balanceAfter = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
assert.equal(
balanceAfter,
startingBalance + transferAmount,
'Receiver balance should be incremented by transfer amount'
);
});

it('should transfer via passkeys with root ethereum style key into another one', async function () {
const startingBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
const accountPKey = getUnifiedPublicKey(fundedEthereumKeys);
console.log(`accountPKey ${u8aToHex(accountPKey)}`);
const nonce = await getNonce(fundedEthereumKeys);
const transferAmount = 66_000_000n;
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedAddress(receiverKeys),
66_000_000n
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
// ethereum keys should not have wrapping
Expand All @@ -76,12 +107,48 @@ describe('Passkey Pallet Proxy V2 Ethereum Tests', function () {
false
);
const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundingSource, passkeyPayload);
await assert.doesNotReject(passkeyProxy.sendUnsigned());
await ExtrinsicHelper.waitForFinalization((await getBlockNumber()) + 2);
// adding some delay before fetching the nonce to ensure it is updated
await new Promise((resolve) => setTimeout(resolve, 1000));
try {
const {
target,
eventMap: { 'balances.Transfer': transferEvent },
} = await passkeyProxy.sendUnsigned();
assert.notEqual(target, undefined, 'Target event should not be undefined');

assert.equal(
ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent),
true,
'Transfer event should be of correct type'
);
if (transferEvent && ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent)) {
const { from, to, amount } = transferEvent.data;
assert.equal(
from.toString(),
getUnifiedAddress(fundedEthereumKeys),
'From address should be the funded ethereum key'
);
assert.equal(to.toString(), getUnifiedAddress(receiverKeys), 'To address should be the receiver key');
assert.equal(amount.toBigInt(), transferAmount, `Transfer amount should be ${transferAmount}`);
} else {
assert.fail('Transfer event not found');
}
} catch (e: any) {
assert.fail(e);
}

/*
* Normally these checks would be unnecessary, but we are testing the passkey pallet
* which has additional logic surrounding mapping account keys, so we want to make sure
* that the nonce and balance are updated correctly.
*/
const nonceAfter = (await ExtrinsicHelper.getAccountInfo(fundedEthereumKeys)).nonce.toNumber();
assert.equal(nonce + 1, nonceAfter);
assert.equal(nonce + 1, nonceAfter, 'Nonce should be incremented by 1');

const balanceAfter = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
assert.equal(
balanceAfter,
startingBalance + transferAmount,
'Receiver balance should be incremented by transfer amount'
);
});
});
});
53 changes: 41 additions & 12 deletions e2e/passkey/passkeyProxyV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers';
import { getFundingSource } from '../scaffolding/funding';
import { u8aToHex, u8aWrapBytes } from '@polkadot/util';
import { createPassKeyAndSignAccount, createPassKeyCallV2, createPasskeyPayloadV2 } from '../scaffolding/P256';
import { getUnifiedPublicKey } from '../scaffolding/ethereum';
import { getUnifiedAddress, getUnifiedPublicKey } from '../scaffolding/ethereum';
const fundingSource = getFundingSource(import.meta.url);

describe('Passkey Pallet Proxy V2 Tests', function () {
Expand All @@ -24,7 +24,7 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
const nonce = await getNonce(fundedKeys);

const remarksCalls = ExtrinsicHelper.api.tx.system.remark('passkey-test');
const { passKeyPrivateKey, passKeyPublicKey, passkeySignature } = createPassKeyAndSignAccount(accountPKey);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey));
const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, remarksCalls);
Expand All @@ -44,7 +44,7 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
const accountPKey = getUnifiedPublicKey(fundedKeys);
const nonce = await getNonce(fundedKeys);
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(getUnifiedPublicKey(receiverKeys), 0n);
const { passKeyPrivateKey, passKeyPublicKey, passkeySignature } = createPassKeyAndSignAccount(accountPKey);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedKeys.sign('badPasskeyPublicKey');
const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, transferCalls);
Expand All @@ -64,7 +64,7 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
const accountPKey = getUnifiedPublicKey(fundedKeys);
const nonce = await getNonce(fundedKeys);
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(getUnifiedPublicKey(receiverKeys), 0n);
const { passKeyPrivateKey, passKeyPublicKey, passkeySignature } = createPassKeyAndSignAccount(accountPKey);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey));
const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, transferCalls);
Expand All @@ -81,11 +81,13 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
});

it('should transfer small balance from fundedKeys to receiverKeys', async function () {
const startingBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
const accountPKey = getUnifiedPublicKey(fundedKeys);
const nonce = await getNonce(fundedKeys);
const transferAmount = 100_000_000n;
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedPublicKey(receiverKeys),
100_000_000n
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey));
Expand All @@ -99,14 +101,41 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
false
);
const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedKeys, passkeyPayload);
await assert.doesNotReject(passkeyProxy.fundAndSendUnsigned(fundingSource));
await ExtrinsicHelper.waitForFinalization((await getBlockNumber()) + 2);
const receiverBalance = await ExtrinsicHelper.getAccountInfo(receiverKeys);
// adding some delay before fetching the nonce to ensure it is updated
await new Promise((resolve) => setTimeout(resolve, 2000));
try {
const {
target,
eventMap: { 'balances.Transfer': transferEvent },
} = await passkeyProxy.fundAndSendUnsigned(fundingSource);
assert.notEqual(target, undefined, 'Target event should not be undefined');
assert.equal(
ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent),
true,
'Transfer event should be of correct type'
);
if (transferEvent && ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent)) {
const { from, to, amount } = transferEvent.data;
assert.equal(from.toString(), getUnifiedAddress(fundedKeys), 'From address should be the funded key');
assert.equal(to.toString(), getUnifiedAddress(receiverKeys), 'To address should be the receiver key');
assert.equal(amount.toBigInt(), transferAmount, `Transfer amount should be ${transferAmount}`);
}
} catch (e: any) {
assert.fail(e);
}

/*
* Normally these checks would be unnecessary, but we are testing the passkey pallet
* which has additional logic surrounding mapping account keys, so we want to make sure
* that the nonce and balance are updated correctly.
*/
const nonceAfter = (await ExtrinsicHelper.getAccountInfo(fundedKeys)).nonce.toNumber();
assert.equal(nonce + 1, nonceAfter);
assert(receiverBalance.data.free.toBigInt() > 0n);
assert.equal(nonce + 1, nonceAfter, 'Nonce should be incremented by 1');

const balanceAfter = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
assert.equal(
balanceAfter,
startingBalance + transferAmount,
'Receiver balance should be incremented by transfer amount'
);
});
});
});
16 changes: 14 additions & 2 deletions e2e/scaffolding/extrinsicHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ApiPromise, ApiRx } from '@polkadot/api';
import { ApiTypes, AugmentedEvent, SubmittableExtrinsic, SignerOptions } from '@polkadot/api/types';
import { KeyringPair } from '@polkadot/keyring/types';
import { Compact, u128, u16, u32, u64, Vec, Option, Bool } from '@polkadot/types';
import { FrameSystemAccountInfo, PalletPasskeyPasskeyPayload, SpRuntimeDispatchError } from '@polkadot/types/lookup';
import { FrameSystemAccountInfo, SpRuntimeDispatchError } from '@polkadot/types/lookup';
import { AnyJson, AnyNumber, AnyTuple, Codec, IEvent, ISubmittableResult } from '@polkadot/types/types';
import { firstValueFrom, filter, map, pipe, tap } from 'rxjs';
import { getBlockNumber, getExistentialDeposit, getFinalizedBlockNumber, log, MultiSignatureType } from './helpers';
Expand Down Expand Up @@ -290,7 +290,19 @@ export class Extrinsic<N = unknown, T extends ISubmittableResult = ISubmittableR
public async sendUnsigned() {
const op = this.extrinsic();
try {
return await firstValueFrom(op.send().pipe(this.parseResult(this.event)));
return await firstValueFrom(
op.send().pipe(
tap((result) => {
// If we learn a transaction has an error status (this does NOT include RPC errors)
// Then throw an error
if (result.isError) {
throw new CallError(result, `Failed Transaction for ${this.event?.meta.name || 'unknown'}`);
}
}),
filter(({ status }) => status.isInBlock || status.isFinalized),
this.parseResult(this.event)
)
);
} catch (e) {
console.error(e);
if ((e as any).name === 'RpcError') {
Expand Down

0 comments on commit b515dce

Please sign in to comment.