Skip to content

Commit

Permalink
add serialization-deserialization test for qi-hdwallet
Browse files Browse the repository at this point in the history
  • Loading branch information
alejoacosta74 authored and rileystephens28 committed Nov 1, 2024
1 parent 517ec12 commit b2b8a58
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 86 deletions.
17 changes: 0 additions & 17 deletions src/_tests/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,23 +376,6 @@ export interface TestCaseQiTransaction {
signed: string;
}

export interface TestCaseQiSerialization {
name: string;
mnemonic: string;
params: Array<AddrParams>;
outpoints: Array<outpointInfo>;
serialized: {
version: number;
phrase: string;
coinType: number;
addresses: Array<AddressInfo>;
changeAddresses: Array<AddressInfo>;
gapAddresses: Array<AddressInfo>;
changeGapAddresses: Array<AddressInfo>;
outpoints: Array<outpointInfo>;
};
}

export interface TestCaseQiSignMessage {
name: string;
mnemonic: string;
Expand Down
187 changes: 187 additions & 0 deletions src/_tests/unit/qihdwallet-serialization.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import assert from 'assert';
import { loadTests } from '../utils.js';
import { QiHDWallet, SerializedQiHDWallet, Zone, AddressStatus } from '../../index.js';

describe('QiHDWallet Serialization/Deserialization', function () {
this.timeout(10000);
const tests = loadTests<SerializedQiHDWallet>('qi-wallet-serialization');

for (const test of tests) {
it('should correctly deserialize and reserialize wallet state', async function () {
// First deserialize the wallet from test data
const deserializedWallet = await QiHDWallet.deserialize(test);

// Now serialize it back
const serializedWallet = deserializedWallet.serialize();

// Verify all properties match the original test data
assert.strictEqual(serializedWallet.version, test.version, 'Version mismatch');
assert.strictEqual(serializedWallet.phrase, test.phrase, 'Phrase mismatch');
assert.strictEqual(serializedWallet.coinType, test.coinType, 'Coin type mismatch');

// Compare outpoints
assert.deepStrictEqual(serializedWallet.outpoints, test.outpoints, 'Outpoints mismatch');

// Compare pending outpoints
assert.deepStrictEqual(
serializedWallet.pendingOutpoints,
test.pendingOutpoints,
'Pending outpoints mismatch',
);

// Compare addresses
assert.deepStrictEqual(
serializedWallet.addresses.sort((a, b) => a.index - b.index),
test.addresses.sort((a, b) => a.index - b.index),
'Addresses mismatch',
);

// Compare sender payment code info
assert.deepStrictEqual(
serializedWallet.senderPaymentCodeInfo,
test.senderPaymentCodeInfo,
'Sender payment code info mismatch',
);

// Finally compare the entire serialized object
assert.deepStrictEqual(
serializedWallet,
test,
'Complete serialized wallet does not match original test data',
);
});

it('should maintain wallet functionality after deserialization', async function () {
const deserializedWallet = await QiHDWallet.deserialize(test);
const zone = Zone.Cyprus1;

// Verify the wallet has the correct number of addresses
const externalAddresses = deserializedWallet.getAddressesForZone(zone);
assert.strictEqual(
externalAddresses.length,
test.addresses.filter((addr) => addr.derivationPath === 'BIP44:external' && addr.zone === zone).length,
'External addresses count mismatch',
);

// Verify the wallet has the correct number of change addresses
const changeAddresses = deserializedWallet.getChangeAddressesForZone(zone);
assert.strictEqual(
changeAddresses.length,
test.addresses.filter((addr) => addr.derivationPath === 'BIP44:change' && addr.zone === zone).length,
'Change addresses count mismatch',
);

// Verify gap addresses
const gapAddresses = deserializedWallet.getGapAddressesForZone(zone);
assert.strictEqual(
gapAddresses.length,
test.addresses.filter(
(addr) =>
addr.derivationPath === 'BIP44:external' &&
addr.zone === zone &&
addr.status === AddressStatus.UNUSED,
).length,
'Gap addresses count mismatch',
);

// Verify outpoints were correctly imported
const zoneOutpoints = deserializedWallet.getOutpoints(zone);
assert.strictEqual(
zoneOutpoints.length,
test.outpoints.filter((outpoint) => outpoint.zone === zone).length,
'Outpoints count mismatch',
);

// Verify payment channels were correctly restored
const paymentCodes = Object.keys(test.senderPaymentCodeInfo);
for (const paymentCode of paymentCodes) {
// Verify channel is open
assert.strictEqual(
deserializedWallet.channelIsOpen(paymentCode),
true,
`Payment channel ${paymentCode} not restored`,
);

// Verify payment channel addresses for zone
const paymentChannelAddresses = deserializedWallet.getPaymentChannelAddressesForZone(paymentCode, zone);
assert.strictEqual(
paymentChannelAddresses.length,
test.addresses.filter((addr) => addr.derivationPath === paymentCode && addr.zone === zone).length,
'Payment channel addresses count mismatch',
);

// Verify gap payment channel addresses
const gapPaymentChannelAddresses = deserializedWallet.getGapPaymentChannelAddresses(paymentCode);
assert.strictEqual(
gapPaymentChannelAddresses.length,
test.addresses.filter(
(addr) => addr.derivationPath === paymentCode && addr.status === AddressStatus.UNUSED,
).length,
'Gap payment channel addresses count mismatch',
);

// Verify the addresses match the expected ones
const expectedPaymentChannelAddresses = test.addresses
.filter((addr) => addr.derivationPath === paymentCode && addr.zone === zone)
.sort((a, b) => a.index - b.index);

assert.deepStrictEqual(
paymentChannelAddresses.sort((a, b) => a.index - b.index),
expectedPaymentChannelAddresses,
'Payment channel addresses do not match expected addresses',
);
}
});

it('should correctly handle gap addresses and payment channel addresses', async function () {
const deserializedWallet = await QiHDWallet.deserialize(test);
const zone = '0x00' as Zone;

// Test gap addresses functionality
const gapAddresses = deserializedWallet.getGapAddressesForZone(zone);
for (const addr of gapAddresses) {
assert.strictEqual(addr.status, AddressStatus.UNUSED, 'Gap address should be unused');
assert.strictEqual(addr.derivationPath, 'BIP44:external', 'Gap address should be external');
assert.strictEqual(addr.zone, zone, 'Gap address should be in correct zone');
}

// Test payment channel functionality for each payment code
const paymentCodes = Object.keys(test.senderPaymentCodeInfo);
for (const paymentCode of paymentCodes) {
// Test gap payment channel addresses
const gapPaymentAddresses = deserializedWallet.getGapPaymentChannelAddresses(paymentCode);
for (const addr of gapPaymentAddresses) {
assert.strictEqual(addr.status, AddressStatus.UNUSED, 'Gap payment address should be unused');
assert.strictEqual(
addr.derivationPath,
paymentCode,
'Gap payment address should have correct payment code',
);
}

// Test zone-specific payment channel addresses
const zonePaymentAddresses = deserializedWallet.getPaymentChannelAddressesForZone(paymentCode, zone);
for (const addr of zonePaymentAddresses) {
assert.strictEqual(
addr.derivationPath,
paymentCode,
'Payment address should have correct payment code',
);
assert.strictEqual(addr.zone, zone, 'Payment address should be in correct zone');
}

// Verify all payment addresses are in the original test data
const allTestAddresses = test.addresses
.filter((addr) => addr.derivationPath === paymentCode)
.map((addr) => addr.address);

for (const addr of zonePaymentAddresses) {
assert.ok(
allTestAddresses.includes(addr.address),
'Payment address should exist in original test data',
);
}
}
});
}
});
69 changes: 0 additions & 69 deletions src/_tests/unit/qihdwallet.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
TestCaseQiAddresses,
TestCaseQiSignMessage,
TestCaseQiTransaction,
TestCaseQiSerialization,
AddressInfo,
TxInput,
TxOutput,
Expand Down Expand Up @@ -67,74 +66,6 @@ describe('QiHDWallet: Test address generation and retrieval', function () {
}
});

describe('QiHDWallet: Test serialization and deserialization of QiHDWallet', function () {
this.timeout(10000);
const tests = loadTests<TestCaseQiSerialization>('qi-serialization');
for (const test of tests) {
const mnemonic = Mnemonic.fromPhrase(test.mnemonic);
const qiWallet = QiHDWallet.fromMnemonic(mnemonic);
let serialized: any;
it(`tests serialization QuaiHDWallet: ${test.name}`, async function () {
for (const param of test.params) {
qiWallet.getNextAddressSync(param.account, param.zone);
qiWallet.getNextChangeAddressSync(param.account, param.zone);
}
qiWallet.importOutpoints(test.outpoints);
serialized = qiWallet.serialize();
assert.deepEqual(serialized, test.serialized);
});

it(`tests deserialization QiHDWallet: ${test.name}`, async function () {
const deserialized = await QiHDWallet.deserialize(serialized);
assert.deepEqual(deserialized.serialize(), serialized);
});
}
});

describe('QiHDWallet: Test serialization and deserialization of QiHDWallet with payment codes', function () {
this.timeout(10000);
it('tests serialization and deserialization with payment codes and addresses', async function () {
// Create Alice's wallet
const aliceMnemonic = Mnemonic.fromPhrase(
'empower cook violin million wool twelve involve nice donate author mammal salt royal shiver birth olympic embody hello beef suit isolate mixed text spot',
);
const aliceQiWallet = QiHDWallet.fromMnemonic(aliceMnemonic);

// Create Bob's wallet
const bobMnemonic = Mnemonic.fromPhrase(
'innocent perfect bus miss prevent night oval position aspect nut angle usage expose grace juice',
);
const bobQiWallet = QiHDWallet.fromMnemonic(bobMnemonic);

// Get payment codes
const alicePaymentCode = await aliceQiWallet.getPaymentCode(0);
const bobPaymentCode = await bobQiWallet.getPaymentCode(0);

// Generate addresses
await aliceQiWallet.getNextSendAddress(bobPaymentCode, Zone.Cyprus1);
await aliceQiWallet.getNextReceiveAddress(bobPaymentCode, Zone.Cyprus1);

// Serialize Alice's wallet
const serializedAliceWallet = aliceQiWallet.serialize();

// Deserialize Alice's wallet
const deserializedAliceWallet = await QiHDWallet.deserialize(serializedAliceWallet);

// Assertions
assert.strictEqual(
deserializedAliceWallet.getPaymentCode(0),
alicePaymentCode,
'Payment code should match after deserialization',
);

assert.equal(
deserializedAliceWallet.channelIsOpen(alicePaymentCode),
aliceQiWallet.channelIsOpen(alicePaymentCode),
'Channel should be open',
);
});
});

describe('QiHDWallet: Test transaction signing', function () {
const tests = loadTests<TestCaseQiTransaction>('qi-transaction');
for (const test of tests) {
Expand Down
Binary file added testcases/qi-wallet-serialization.json.gz
Binary file not shown.

0 comments on commit b2b8a58

Please sign in to comment.