From b5a7c9c600ae9ea2ef1a97373bb7159be6fc5ed3 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Mon, 28 Oct 2024 14:39:33 -0300 Subject: [PATCH 1/2] export address info interfaces --- src/quais.ts | 2 ++ src/wallet/index.ts | 4 ++-- src/wallet/qi-hdwallet.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/quais.ts b/src/quais.ts index d5ddcce7..a7d0a14c 100644 --- a/src/quais.ts +++ b/src/quais.ts @@ -218,6 +218,8 @@ export { encryptKeystoreJsonSync, SerializedHDWallet, SerializedQiHDWallet, + QiAddressInfo, + NeuteredAddressInfo, } from './wallet/index.js'; // WORDLIST diff --git a/src/wallet/index.ts b/src/wallet/index.ts index 85043b2d..dec13e6d 100644 --- a/src/wallet/index.ts +++ b/src/wallet/index.ts @@ -13,7 +13,7 @@ export { BaseWallet } from './base-wallet.js'; -export type { SerializedHDWallet } from './hdwallet.js'; +export type { SerializedHDWallet, NeuteredAddressInfo } from './hdwallet.js'; export { QuaiHDWallet } from './quai-hdwallet.js'; @@ -31,6 +31,6 @@ export { Wallet } from './wallet.js'; export type { KeystoreAccount, EncryptOptions } from './json-keystore.js'; -export { QiHDWallet, SerializedQiHDWallet } from './qi-hdwallet.js'; +export { QiHDWallet, SerializedQiHDWallet, QiAddressInfo } from './qi-hdwallet.js'; export { HDNodeVoidWallet, HDNodeWallet } from './hdnodewallet.js'; diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index 46f20253..14eb915a 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -62,7 +62,7 @@ type DerivationPath = 'BIP44:external' | 'BIP44:change' | string; // string for * * @extends NeuteredAddressInfo */ -interface QiAddressInfo extends NeuteredAddressInfo { +export interface QiAddressInfo extends NeuteredAddressInfo { status: AddressStatus; derivationPath: DerivationPath; } From ed9b11f4a8a77535185defc5929931efdfd1d4e0 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Mon, 28 Oct 2024 17:39:03 -0300 Subject: [PATCH 2/2] add unit test for Qi address derivation --- ...qihdwallet-address-derivation.unit.test.ts | 113 ++++++++++++++++++ testcases/qi-address-derivation.json.gz | Bin 0 -> 2941 bytes 2 files changed, 113 insertions(+) create mode 100644 src/_tests/unit/qihdwallet-address-derivation.unit.test.ts create mode 100644 testcases/qi-address-derivation.json.gz diff --git a/src/_tests/unit/qihdwallet-address-derivation.unit.test.ts b/src/_tests/unit/qihdwallet-address-derivation.unit.test.ts new file mode 100644 index 00000000..5060bad9 --- /dev/null +++ b/src/_tests/unit/qihdwallet-address-derivation.unit.test.ts @@ -0,0 +1,113 @@ +import assert from 'assert'; +import { loadTests } from '../utils.js'; +import { Mnemonic, QiHDWallet, Zone, QiAddressInfo } from '../../index.js'; + +interface TestCaseQiAddressDerivation { + mnemonic: string; + externalAddresses: Array<{ + zone: string; + addresses: Array; + }>; + changeAddresses: Array<{ + zone: string; + addresses: Array; + }>; + paymentCodeAddresses: { + bobMnemonic: string; + sendAddresses: Array<{ + zone: string; + addresses: Array; + }>; + receiveAddresses: Array<{ + zone: string; + addresses: Array; + }>; + }; +} + +describe('QiHDWallet Address Derivation', function () { + this.timeout(2 * 60 * 1000); + const tests = loadTests('qi-address-derivation'); + + for (const test of tests) { + it('derives external addresses correctly', function () { + const mnemonic = Mnemonic.fromPhrase(test.mnemonic); + const qiWallet = QiHDWallet.fromMnemonic(mnemonic); + + for (const externalAddressesInfo of test.externalAddresses) { + const zone = externalAddressesInfo.zone as Zone; + for (const expectedAddressInfo of externalAddressesInfo.addresses) { + const derivedAddressInfo = qiWallet.getNextAddressSync(0, zone); + assert.deepEqual( + derivedAddressInfo, + expectedAddressInfo, + `External address mismatch for zone ${zone}, expected: ${JSON.stringify(expectedAddressInfo)}, derived: ${JSON.stringify(derivedAddressInfo)}`, + ); + } + } + }); + + it('derives change addresses correctly', function () { + const mnemonic = Mnemonic.fromPhrase(test.mnemonic); + const qiWallet = QiHDWallet.fromMnemonic(mnemonic); + + for (const changeAddressesInfo of test.changeAddresses) { + const zone = changeAddressesInfo.zone as Zone; + for (const expectedAddressInfo of changeAddressesInfo.addresses) { + const derivedAddressInfo = qiWallet.getNextChangeAddressSync(0, zone); + assert.deepEqual( + derivedAddressInfo, + expectedAddressInfo, + `Change address mismatch for zone ${zone}, expected: ${JSON.stringify(expectedAddressInfo)}, derived: ${JSON.stringify(derivedAddressInfo)}`, + ); + } + } + }); + + it('derives payment code send addresses correctly', function () { + const mnemonic = Mnemonic.fromPhrase(test.mnemonic); + const qiWallet = QiHDWallet.fromMnemonic(mnemonic); + + const bobMnemonic = Mnemonic.fromPhrase(test.paymentCodeAddresses.bobMnemonic); + const bobQiWallet = QiHDWallet.fromMnemonic(bobMnemonic); + const bobPaymentCode = bobQiWallet.getPaymentCode(0); + + qiWallet.openChannel(bobPaymentCode); + + for (const sendAddressesInfo of test.paymentCodeAddresses.sendAddresses) { + const zone = sendAddressesInfo.zone as Zone; + for (const expectedAddressInfo of sendAddressesInfo.addresses) { + const derivedAddressInfo = qiWallet.getNextSendAddress(bobPaymentCode, zone); + assert.deepEqual( + derivedAddressInfo, + expectedAddressInfo, + `Payment code send address mismatch, expected: ${JSON.stringify(expectedAddressInfo)}, derived: ${JSON.stringify(derivedAddressInfo)}`, + ); + } + } + }); + + it('derives payment code receive addresses correctly', function () { + const mnemonic = Mnemonic.fromPhrase(test.mnemonic); + const qiWallet = QiHDWallet.fromMnemonic(mnemonic); + + const bobMnemonic = Mnemonic.fromPhrase(test.paymentCodeAddresses.bobMnemonic); + const bobQiWallet = QiHDWallet.fromMnemonic(bobMnemonic); + const bobPaymentCode = bobQiWallet.getPaymentCode(0); + + qiWallet.openChannel(bobPaymentCode); + + for (const receiveAddressesInfo of test.paymentCodeAddresses.receiveAddresses) { + const zone = receiveAddressesInfo.zone as Zone; + for (const expectedAddressInfo of receiveAddressesInfo.addresses) { + const derivedAddressInfo = qiWallet.getNextReceiveAddress(bobPaymentCode, zone); + assert.deepEqual( + derivedAddressInfo, + expectedAddressInfo, + `Payment code receive address mismatch, expected: ${JSON.stringify(expectedAddressInfo)}, derived: ${JSON.stringify(derivedAddressInfo)}`, + ); + } + } + }); + } +}); diff --git a/testcases/qi-address-derivation.json.gz b/testcases/qi-address-derivation.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..023247746e817e8a6189d2b6501e43bf614c46a9 GIT binary patch literal 2941 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