Skip to content

Commit

Permalink
feat: make passkey tests more granular and verbose, check for specifi…
Browse files Browse the repository at this point in the history
…c errors on failures
  • Loading branch information
JoeCap08055 committed Jan 2, 2025
1 parent f4874d9 commit e156c55
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 89 deletions.
139 changes: 91 additions & 48 deletions e2e/passkey/passkeyProxyV2.ethereum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import '@frequency-chain/api-augment';
import assert from 'assert';
import { createAndFundKeypair, EcdsaSignature, getNonce, Sr25519Signature } from '../scaffolding/helpers';
import { KeyringPair } from '@polkadot/keyring/types';
import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers';
import { Extrinsic, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers';
import { getFundingSource } from '../scaffolding/funding';
import { getUnifiedPublicKey, getUnifiedAddress } from '../scaffolding/ethereum';
import { createPassKeyAndSignAccount, createPassKeyCallV2, createPasskeyPayloadV2 } from '../scaffolding/P256';
import { u8aToHex, u8aWrapBytes } from '@polkadot/util';
import { AccountId32 } from '@polkadot/types/interfaces';
import { ISubmittableResult } from '@polkadot/types/types';
const fundingSource = getFundingSource(import.meta.url);

describe('Passkey Pallet Proxy V2 Ethereum Tests', function () {
Expand Down Expand Up @@ -83,72 +85,113 @@ describe('Passkey Pallet Proxy V2 Ethereum Tests', function () {
);
});

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);
describe('successful transfer via passkeys with root ethereum-style key into a polkadot-style key', function () {
let startingReceiverBalance: bigint;
let startingSenderBalance: bigint;
let accountPKey: Uint8Array<ArrayBufferLike>;
let nonce: number;
let target: any;
let transferEvent: any;
let feeEvent: any;
let passkeyProxy: Extrinsic<
{
accountId: AccountId32;
},
ISubmittableResult,
[accountId: AccountId32]
>;

const transferAmount = 66_000_000n;
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedAddress(receiverKeys),
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
// ethereum keys should not have wrapping
const accountSignature = fundedEthereumKeys.sign(passKeyPublicKey);
console.log(`accountSignature ${u8aToHex(accountSignature)}`);
const multiSignature: EcdsaSignature = { Ecdsa: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, transferCalls);
const passkeyPayload = await createPasskeyPayloadV2(
multiSignature,
passKeyPrivateKey,
passKeyPublicKey,
passkeyCall,
false
);
const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundingSource, passkeyPayload);
try {
const {
target,
eventMap: { 'balances.Transfer': transferEvent },
} = await passkeyProxy.sendUnsigned();
assert.notEqual(target, undefined, 'Target event should not be undefined');

before(async function () {
startingReceiverBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
startingSenderBalance = (await ExtrinsicHelper.getAccountInfo(fundedEthereumKeys)).data.free.toBigInt();
accountPKey = getUnifiedPublicKey(fundedEthereumKeys);
console.log(`accountPKey ${u8aToHex(accountPKey)}`);
nonce = await getNonce(fundedEthereumKeys);
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedAddress(receiverKeys),
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
// ethereum keys should not have wrapping
const accountSignature = fundedEthereumKeys.sign(passKeyPublicKey);
console.log(`accountSignature ${u8aToHex(accountSignature)}`);
const multiSignature: EcdsaSignature = { Ecdsa: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, transferCalls);
const passkeyPayload = await createPasskeyPayloadV2(
multiSignature,
passKeyPrivateKey,
passKeyPublicKey,
passkeyCall,
false
);
passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundingSource, passkeyPayload);
});

it('should successfully execute transfer extrinsic', async function () {
await assert.doesNotReject(async () => {
({
target,
eventMap: { 'balances.Transfer': transferEvent, 'balances.Withdraw': feeEvent },
} = await passkeyProxy.sendUnsigned());
});
});

it('should have received a transaction execution success event', async function () {
assert.notEqual(target, undefined, 'Target event should not be undefined');
assert.equal(
ExtrinsicHelper.api.events.balances.Transfer.is(transferEvent),
ExtrinsicHelper.api.events.passkey.TransactionExecutionSuccess.is(target),
true,
'Transfer event should be of correct type'
'Target event should be of correct type'
);
});

it('should have debited & credited correct accounts for the correct amount', async function () {
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(from.toString(), getUnifiedAddress(fundedEthereumKeys), '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}`);
} else {
assert.fail('Transfer event not found');
}
} catch (e: any) {
assert.fail(e);
}
});

it('should have deducted fee from the sender', async function () {
if (feeEvent && ExtrinsicHelper.api.events.balances.Withdraw.is(feeEvent)) {
const { who, amount } = feeEvent.data;
assert.equal(who.toString(), getUnifiedAddress(fundedEthereumKeys), 'Fee should be deducted from the sender');
assert.equal(amount.toBigInt() > 0, true, 'Fee should be greater than 0');
} else {
assert.fail('Fee event not found');
}
});

/*
* 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, 'Nonce should be incremented by 1');
it('should have incremented nonce by 1 for sender', async function () {
const nonceAfter = (await ExtrinsicHelper.getAccountInfo(fundedEthereumKeys)).nonce.toNumber();
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 have changed account balances correctly', async function () {
const endingReceiverBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
const endingSenderBalance = (await ExtrinsicHelper.getAccountInfo(fundedEthereumKeys)).data.free.toBigInt();
assert.equal(
endingReceiverBalance,
startingReceiverBalance + transferAmount,
'Receiver balance should be incremented by transfer amount'
);
assert.equal(
startingSenderBalance - transferAmount >= endingSenderBalance,
true,
'Sender balance should be decremented by at least transfer amount'
);
});
});
});
});
132 changes: 91 additions & 41 deletions e2e/passkey/passkeyProxyV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import '@frequency-chain/api-augment';
import assert from 'assert';
import { createAndFundKeypair, getBlockNumber, getNonce, Sr25519Signature } from '../scaffolding/helpers';
import { KeyringPair } from '@polkadot/keyring/types';
import { ExtrinsicHelper } from '../scaffolding/extrinsicHelpers';
import { Extrinsic, ExtrinsicHelper } from '../scaffolding/extrinsicHelpers';
import { getFundingSource } from '../scaffolding/funding';
import { u8aToHex, u8aWrapBytes } from '@polkadot/util';
import { createPassKeyAndSignAccount, createPassKeyCallV2, createPasskeyPayloadV2 } from '../scaffolding/P256';
import { getUnifiedAddress, getUnifiedPublicKey } from '../scaffolding/ethereum';
import { AccountId32 } from '@polkadot/types/interfaces';
import { ISubmittableResult } from '@polkadot/types/types';
const fundingSource = getFundingSource(import.meta.url);

describe('Passkey Pallet Proxy V2 Tests', function () {
Expand Down Expand Up @@ -37,7 +39,7 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
);

const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedKeys, passkeyPayload);
await assert.rejects(passkeyProxy.fundAndSendUnsigned(fundingSource));
await assert.rejects(passkeyProxy.fundAndSendUnsigned(fundingSource), /Transaction call is not expected/);
});

it('should fail to transfer balance due to bad account ownership proof', async function () {
Expand All @@ -57,7 +59,7 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
);

const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedKeys, passkeyPayload);
await assert.rejects(passkeyProxy.fundAndSendUnsigned(fundingSource));
await assert.rejects(passkeyProxy.fundAndSendUnsigned(fundingSource), /Invalid signing address/);
});

it('should fail to transfer balance due to bad passkey signature', async function () {
Expand All @@ -77,65 +79,113 @@ describe('Passkey Pallet Proxy V2 Tests', function () {
);

const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedKeys, passkeyPayload);
await assert.rejects(passkeyProxy.fundAndSendUnsigned(fundingSource));
await assert.rejects(passkeyProxy.fundAndSendUnsigned(fundingSource), /Custom error: 4/);
});

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);
describe('successful transfer small balance from fundedKeys to receiverKeys', function () {
let startingReceiverBalance: bigint;
let startingSenderBalance: bigint;
let accountPKey: Uint8Array<ArrayBufferLike>;
let nonce: number;
let target: any;
let transferEvent: any;
let feeEvent: any;
let passkeyProxy: Extrinsic<
{
accountId: AccountId32;
},
ISubmittableResult,
[accountId: AccountId32]
>;

const transferAmount = 100_000_000n;
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedPublicKey(receiverKeys),
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey));
const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, transferCalls);
const passkeyPayload = await createPasskeyPayloadV2(
multiSignature,
passKeyPrivateKey,
passKeyPublicKey,
passkeyCall,
false
);
const passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedKeys, passkeyPayload);
try {
const {
target,
eventMap: { 'balances.Transfer': transferEvent },
} = await passkeyProxy.fundAndSendUnsigned(fundingSource);

before(async function () {
startingReceiverBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
startingSenderBalance = (await ExtrinsicHelper.getAccountInfo(fundedKeys)).data.free.toBigInt();
accountPKey = getUnifiedPublicKey(fundedKeys);
nonce = await getNonce(fundedKeys);
const transferCalls = ExtrinsicHelper.api.tx.balances.transferKeepAlive(
getUnifiedPublicKey(receiverKeys),
transferAmount
);
const { passKeyPrivateKey, passKeyPublicKey } = createPassKeyAndSignAccount(accountPKey);
const accountSignature = fundedKeys.sign(u8aWrapBytes(passKeyPublicKey));
const multiSignature: Sr25519Signature = { Sr25519: u8aToHex(accountSignature) };
const passkeyCall = await createPassKeyCallV2(accountPKey, nonce, transferCalls);
const passkeyPayload = await createPasskeyPayloadV2(
multiSignature,
passKeyPrivateKey,
passKeyPublicKey,
passkeyCall,
false
);
passkeyProxy = ExtrinsicHelper.executePassKeyProxyV2(fundedKeys, passkeyPayload);
});

it('should successfully execute transfer extrinsic', async function () {
await assert.doesNotReject(async () => {
({
target,
eventMap: { 'balances.Transfer': transferEvent, 'balances.Withdraw': feeEvent },
} = await passkeyProxy.fundAndSendUnsigned(fundingSource));
});
});

it('should have received a transaction execution success event', async function () {
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'
);
});

it('should have debited and credited correct accounts for the correct amount', async function () {
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}`);
} else {
assert.fail('Transfer event not found');
}
});

it('should have deducted the correct fee from the sender', async function () {
if (feeEvent && ExtrinsicHelper.api.events.balances.Withdraw.is(feeEvent)) {
const { who, amount } = feeEvent.data;
assert.equal(who.toString(), getUnifiedAddress(fundedKeys), 'Fee should be deducted from the sender');
assert.equal(amount.toBigInt() > 0n, true, 'Fee should be greater than 0');
} else {
assert.fail('Fee 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(fundedKeys)).nonce.toNumber();
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 have incremented nonce by 1 for sender', async function () {
const nonceAfter = (await ExtrinsicHelper.getAccountInfo(fundedKeys)).nonce.toNumber();
assert.equal(nonce + 1, nonceAfter, 'Nonce should be incremented by 1');
});

it('should have changed account balances correctly', async function () {
const endingReceiverBalance = (await ExtrinsicHelper.getAccountInfo(receiverKeys)).data.free.toBigInt();
const endingSenderBalance = (await ExtrinsicHelper.getAccountInfo(fundedKeys)).data.free.toBigInt();
assert.equal(
endingReceiverBalance,
startingReceiverBalance + transferAmount,
'Receiver balance should be incremented by transfer amount'
);
assert.equal(
startingSenderBalance - transferAmount >= endingSenderBalance,
true,
'Sender balance should be decremented by at least transfer amount'
);
});
});
});
});

0 comments on commit e156c55

Please sign in to comment.