From 79cba8addc522d7e430f649030bbadd299a34674 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Wed, 23 Oct 2024 13:25:39 -0300 Subject: [PATCH 1/7] fix bug on paymentcode index derivation --- src/wallet/qi-hdwallet.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index e1b1c834..c41f0f64 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -1353,11 +1353,11 @@ export class QiHDWallet extends AbstractHDWallet { const paymentCodeInfoArray = this._paymentCodeSendAddressMap.get(receiverPaymentCode); const lastIndex = this._findLastUsedIndex(paymentCodeInfoArray, account, zone); - let addrIndex = lastIndex; + let addrIndex = lastIndex + 1; for (let attempts = 0; attempts < MAX_ADDRESS_DERIVATION_ATTEMPTS; attempts++) { - const address = receiverPCodePublic.getPaymentAddress(walletPCodePrivate, addrIndex++); + const address = receiverPCodePublic.getPaymentAddress(walletPCodePrivate, addrIndex); if (this.isValidAddressForZone(address, zone)) { - const pubkey = receiverPCodePublic.derivePaymentPublicKey(walletPCodePrivate, addrIndex - 1); + const pubkey = receiverPCodePublic.derivePaymentPublicKey(walletPCodePrivate, addrIndex); const pcInfo: QiAddressInfo = { address, pubKey: hexlify(pubkey), @@ -1375,6 +1375,7 @@ export class QiHDWallet extends AbstractHDWallet { } return pcInfo; } + addrIndex++; } throw new Error( @@ -1402,11 +1403,11 @@ export class QiHDWallet extends AbstractHDWallet { const paymentCodeInfoArray = this._addressesMap.get(senderPaymentCode); const lastIndex = this._findLastUsedIndex(paymentCodeInfoArray, account, zone); - let addrIndex = lastIndex; + let addrIndex = lastIndex + 1; for (let attempts = 0; attempts < MAX_ADDRESS_DERIVATION_ATTEMPTS; attempts++) { - const address = walletPCodePrivate.getPaymentAddress(senderPCodePublic, addrIndex++); + const address = walletPCodePrivate.getPaymentAddress(senderPCodePublic, addrIndex); if (this.isValidAddressForZone(address, zone)) { - const pubkey = walletPCodePrivate.derivePaymentPublicKey(senderPCodePublic, addrIndex - 1); + const pubkey = walletPCodePrivate.derivePaymentPublicKey(senderPCodePublic, addrIndex); const pcInfo: QiAddressInfo = { address, pubKey: hexlify(pubkey), @@ -1424,6 +1425,7 @@ export class QiHDWallet extends AbstractHDWallet { } return pcInfo; } + addrIndex++; } throw new Error( From 5ef0637bbd95562593a5f8a20629df6fdc0e4374 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Thu, 24 Oct 2024 10:38:20 -0300 Subject: [PATCH 2/7] force _findLastUsedIndex to return -1 if no index is found --- src/wallet/qi-hdwallet.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index c41f0f64..1f10c232 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -218,7 +218,7 @@ export class QiHDWallet extends AbstractHDWallet { } /** - * Finds the last used index in an array of QiAddressInfo objects. + * Finds the last used index in an array of QiAddressInfo objects. If no index is found, returns -1. * * @param {QiAddressInfo[]} addresses - The array of QiAddressInfo objects. * @returns {number} The last used index. @@ -227,7 +227,7 @@ export class QiHDWallet extends AbstractHDWallet { const filteredAddresses = addresses?.filter( (addressInfo) => addressInfo.account === account && addressInfo.zone === zone, ); - return filteredAddresses?.reduce((maxIndex, addressInfo) => Math.max(maxIndex, addressInfo.index), -1) || 0; + return filteredAddresses?.reduce((maxIndex, addressInfo) => Math.max(maxIndex, addressInfo.index), -1) || -1; } /** From e7b62d904b0541206d13ff63e8743fcda56af805 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Thu, 24 Oct 2024 14:01:27 -0300 Subject: [PATCH 3/7] fix bug in address calculation in paymentcode logic --- src/wallet/payment-codes.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/wallet/payment-codes.ts b/src/wallet/payment-codes.ts index 2c48260b..e100c852 100644 --- a/src/wallet/payment-codes.ts +++ b/src/wallet/payment-codes.ts @@ -1,8 +1,7 @@ import { BIP32API, BIP32Interface, HDNodeBIP32Adapter } from './bip32/types.js'; import { sha256 } from '@noble/hashes/sha256'; -import { keccak256 } from '../crypto/index.js'; import { getBytes, hexlify } from '../utils/data.js'; -import { getAddress } from '../address/address.js'; +import { computeAddress } from '../address/address.js'; import { bs58check } from './bip32/crypto.js'; import type { TinySecp256k1Interface } from './bip32/types.js'; import { secp256k1 } from '@noble/curves/secp256k1'; @@ -159,7 +158,7 @@ export class PaymentCodePublic { * @protected */ protected getAddressFromPubkey(pubKey: Uint8Array): string { - return getAddress(keccak256('0x' + hexlify(pubKey).substring(4)).substring(26)); + return computeAddress(hexlify(pubKey)); } /** @@ -171,9 +170,8 @@ export class PaymentCodePublic { * @throws {Error} - If unable to derive public key or if an unknown address type is specified. */ getPaymentAddress(paymentCode: PaymentCodePrivate, idx: number): string { - const pubkey = hexlify(this.derivePaymentPublicKey(paymentCode, idx)); - - return getAddress(keccak256('0x' + pubkey.substring(4)).substring(26)); + const pubkey = this.derivePaymentPublicKey(paymentCode, idx); + return this.getAddressFromPubkey(pubkey); } } From d2acbf8e7ff3688696b07594e2a79287c3e7841c Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Thu, 24 Oct 2024 15:20:18 -0300 Subject: [PATCH 4/7] add testing script in examples folder --- examples/signing/pubkey-to-address.js | 37 +++++ .../qi-wallet-alice-bob-send-receive.js | 87 ++++++++++++ examples/wallets/qi-wallet-convert-to-quai.js | 73 ++++++++++ examples/wallets/qi-wallet-send-qi-to-bob.js | 87 ++++++++++++ examples/wallets/utils.js | 132 ++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 examples/signing/pubkey-to-address.js create mode 100644 examples/wallets/qi-wallet-alice-bob-send-receive.js create mode 100644 examples/wallets/qi-wallet-convert-to-quai.js create mode 100644 examples/wallets/qi-wallet-send-qi-to-bob.js create mode 100644 examples/wallets/utils.js diff --git a/examples/signing/pubkey-to-address.js b/examples/signing/pubkey-to-address.js new file mode 100644 index 00000000..f1c91e6f --- /dev/null +++ b/examples/signing/pubkey-to-address.js @@ -0,0 +1,37 @@ +const quais = require('../../lib/commonjs/quais'); + + +async function main() { + // Check if a public key is provided as a command line argument + if (process.argv.length < 3) { + console.error('Please provide a public key as a command line argument'); + process.exit(1); + } + + const pubkey = process.argv[2]; + + // Verify if the provided string is a valid public key of the type 0x0250495cb2f9535c684ebe4687b501c0d41a623d68c118b8dcecd393370f1d90e6 + if (!quais.isHexString(pubkey) || pubkey.length !== 68) { + console.error('Invalid public key format'); + process.exit(1); + } + + + try { + // Compute the address from the public key + const address = quais.computeAddress(pubkey); + console.log(`Public Key: ${pubkey}`); + console.log(`Derived Address: ${address}`); + } catch (error) { + console.error('Error computing address:', error.message); + process.exit(1); + } + +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/qi-wallet-alice-bob-send-receive.js b/examples/wallets/qi-wallet-alice-bob-send-receive.js new file mode 100644 index 00000000..8489da2c --- /dev/null +++ b/examples/wallets/qi-wallet-alice-bob-send-receive.js @@ -0,0 +1,87 @@ +const quais = require('../../lib/commonjs/quais'); +const { printWalletInfo } = require('./utils'); +require('dotenv').config(); + +async function main() { + // Create provider + console.log('RPC URL: ', process.env.RPC_URL); + const provider = new quais.JsonRpcProvider(process.env.RPC_URL); + + // Create wallet and connect to provider + const mnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC); + const aliceQiWallet = quais.QiHDWallet.fromMnemonic(mnemonic); + const alicePaymentCode = aliceQiWallet.getPaymentCode(0); + aliceQiWallet.connect(provider); + + const bobMnemonic = quais.Mnemonic.fromPhrase("innocent perfect bus miss prevent night oval position aspect nut angle usage expose grace juice"); + const bobQiWallet = quais.QiHDWallet.fromMnemonic(bobMnemonic); + bobQiWallet.connect(provider); + const bobPaymentCode = bobQiWallet.getPaymentCode(0); + aliceQiWallet.openChannel(bobPaymentCode); + + console.log('Scanning Alice Qi wallet...'); + await aliceQiWallet.scan(quais.Zone.Cyprus1); + console.log('Alice Qi wallet scan complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // Bob opens a channel with Alice + bobQiWallet.openChannel(alicePaymentCode); + console.log('Scanning Bob Qi wallet...'); + await bobQiWallet.scan(quais.Zone.Cyprus1); + console.log('Bob Qi wallet scan complete'); + printWalletInfo('Bob', bobQiWallet); + + // Alice sends 50 Qi to Bob + console.log('\nAlice sends 50 Qi to Bob'); + const tx = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + // console.log('Transaction sent: ', tx); + console.log(`Transaction hash: ${tx.hash}`); + console.log(`Tx contains ${tx.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx.txInputs)}`); + console.log(`Tx contains ${tx.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response = await tx.wait(); + console.log('Transaction confirmed in block: ', response.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); + + console.log('\nBob sends back 25 Qi to Alice'); + const tx2 = await bobQiWallet.sendTransaction(alicePaymentCode, 25000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + console.log(`Transaction hash: ${tx2.hash}`); + console.log(`Tx contains ${tx2.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx2.txInputs)}`); + console.log(`Tx contains ${tx2.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response2 = await tx2.wait(); + console.log('Transaction confirmed in block: ', response2.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/qi-wallet-convert-to-quai.js b/examples/wallets/qi-wallet-convert-to-quai.js new file mode 100644 index 00000000..94b0355f --- /dev/null +++ b/examples/wallets/qi-wallet-convert-to-quai.js @@ -0,0 +1,73 @@ +const quais = require('../../lib/commonjs/quais'); +const { printWalletInfo } = require('./utils'); +require('dotenv').config(); + +async function main() { + // Create provider + console.log('RPC URL: ', process.env.RPC_URL); + const provider = new quais.JsonRpcProvider(process.env.RPC_URL); + + // Create Alice's Qi wallet and connect to provider + const mnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC); + const aliceQiWallet = quais.QiHDWallet.fromMnemonic(mnemonic); + aliceQiWallet.connect(provider); + + // Initialize Alice's Qi wallet + console.log('\nInitializing Alice Qi wallet...'); + await aliceQiWallet.scan(quais.Zone.Cyprus1); + console.log('Alice Qi wallet scan complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // Create Alice's Quai wallet and connect to provider + const aliceQuaiWallet = quais.QuaiHDWallet.fromMnemonic(mnemonic); + aliceQuaiWallet.connect(provider); + + // derive quai address + const quaiAddressInfo = await aliceQuaiWallet.getNextAddress(0, quais.Zone.Cyprus1); + console.log('\nAlice Quai address:', quaiAddressInfo.address); + + console.log('\nAlice converts 100 Qi to Quai...'); + + const tx = await aliceQiWallet.convertToQuai(quaiAddressInfo.address, 100000); + // console.log('Transaction sent: ', tx); + console.log(`Transaction hash: ${tx.hash}`); + console.log(`Tx contains ${tx.txInputs?.length} inputs`); + console.log(`Tx contains ${tx.txOutputs?.length} outputs`); + // wait for the transaction to be confirmed + console.log('Waiting for transaction to be confirmed...'); + const response = await tx.wait(); + console.log('Transaction confirmed in block: ', response.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // print Alice's Quai address balance + const balance = await provider.getBalance(quaiAddressInfo.address); + console.log('\nAlice Quai address balance:', quais.formatQuai(balance)); + + // repeat the same process of converting 100 Qi to Quai + console.log('Alice converts another 100 Qi to Quai...'); + const tx2 = await aliceQiWallet.convertToQuai(quaiAddressInfo.address, 100000); + console.log(`Tx contains ${tx2.txInputs?.length} inputs`); + console.log(`Tx contains ${tx2.txOutputs?.length} outputs`); + console.log('Waiting for transaction to be confirmed...'); + const response2 = await tx2.wait(); + console.log('Transaction confirmed in block: ', response2.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/qi-wallet-send-qi-to-bob.js b/examples/wallets/qi-wallet-send-qi-to-bob.js new file mode 100644 index 00000000..6581eb25 --- /dev/null +++ b/examples/wallets/qi-wallet-send-qi-to-bob.js @@ -0,0 +1,87 @@ +const quais = require('../../lib/commonjs/quais'); +const { printWalletInfo } = require('./utils'); +require('dotenv').config(); + +async function main() { + // Create provider + console.log('RPC URL: ', process.env.RPC_URL); + const provider = new quais.JsonRpcProvider(process.env.RPC_URL); + + // Create wallet and connect to provider + const mnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC); + const aliceQiWallet = quais.QiHDWallet.fromMnemonic(mnemonic); + const alicePaymentCode = aliceQiWallet.getPaymentCode(0); + aliceQiWallet.connect(provider); + + const bobMnemonic = quais.Mnemonic.fromPhrase("innocent perfect bus miss prevent night oval position aspect nut angle usage expose grace juice"); + const bobQiWallet = quais.QiHDWallet.fromMnemonic(bobMnemonic); + bobQiWallet.connect(provider); + const bobPaymentCode = bobQiWallet.getPaymentCode(0); + aliceQiWallet.openChannel(bobPaymentCode); + + console.log('Scanning Alice Qi wallet...'); + await aliceQiWallet.scan(quais.Zone.Cyprus1); + console.log('Alice Qi wallet scan complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // Bob opens a channel with Alice + bobQiWallet.openChannel(alicePaymentCode); + console.log('Scanning Bob Qi wallet...'); + await bobQiWallet.scan(quais.Zone.Cyprus1); + console.log('Bob Qi wallet scan complete'); + printWalletInfo('Bob', bobQiWallet); + + // Alice sends 50 Qi to Bob + console.log('\nAlice sends 50 Qi to Bob'); + const tx = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + // console.log('Transaction sent: ', tx); + console.log(`Transaction hash: ${tx.hash}`); + console.log(`Tx contains ${tx.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx.txInputs)}`); + console.log(`Tx contains ${tx.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response = await tx.wait(); + console.log('Transaction confirmed in block: ', response.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); + + console.log('\nAlice sends another 50 Qi to Bob'); + const tx2 = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + console.log(`Transaction hash: ${tx2.hash}`); + console.log(`Tx contains ${tx2.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx2.txInputs)}`); + console.log(`Tx contains ${tx2.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response2 = await tx2.wait(); + console.log('Transaction confirmed in block: ', response2.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/utils.js b/examples/wallets/utils.js new file mode 100644 index 00000000..3229874d --- /dev/null +++ b/examples/wallets/utils.js @@ -0,0 +1,132 @@ +const quais = require('../../lib/commonjs/quais'); +const printWalletInfo = (name, wallet) => { + const serializedWallet = wallet.serialize(); + + // Helper function to get addresses by type and status + const getAddressesByType = (type) => { + if (type == 'BIP44:external' || type == 'BIP44:change') { + return serializedWallet.addresses + .filter(addr => addr.derivationPath === type); + } + return serializedWallet.addresses + .filter(addr => !addr.derivationPath.startsWith('BIP44:')); + }; + + const printUnusedAddressesData = (type) => { + const addresses = getAddressesByType(type); + + // Find the index where the last group of UNUSED addresses starts + let lastUnusedGroupStartIndex = addresses.length; + for (let i = addresses.length - 1; i >= 0; i--) { + if (addresses[i].status !== 'UNUSED') { + break; + } + lastUnusedGroupStartIndex = i; + } + + // Filter addresses: UNUSED and not part of the last group + const filteredUnusedAddresses = addresses.filter((addr, index) => + addr.status === 'UNUSED' && index < lastUnusedGroupStartIndex + ); + + if (filteredUnusedAddresses.length > 0) { + const outpoints = serializedWallet.outpoints; + for (const addr of filteredUnusedAddresses) { + const outpoint = outpoints.find(outpoint => outpoint.address === addr.address); + if (outpoint) { + console.log(`\tOutpoint for UNUSED address ${addr.address}: ${JSON.stringify(outpoint)}`); + } else { + console.log(`\tOutpoint for UNUSED address ${addr.address} not found`); + } + } + } + }; + + const summary = { + 'BIP44 External Addresses': getAddressesByType('BIP44:external').length, + 'BIP44 Change Addresses': getAddressesByType('BIP44:change').length, + 'BIP47 Addresses': getAddressesByType('BIP47').length, + 'Available Outpoints': serializedWallet.outpoints.length, + 'Pending Outpoints': serializedWallet.pendingOutpoints.length, + 'Sender Payment Code Info': Object.keys(serializedWallet.senderPaymentCodeInfo).length, + 'Coin Type': serializedWallet.coinType, + 'Version': serializedWallet.version + }; + console.log(`\n**************************************************** ${name} Qi wallet summary: ************************************************\n`); + console.table(summary); + + // Print BIP44 External Addresses + console.log(`\n${name} BIP44 External Addresses:`); + printAddressTable(getAddressesByType('BIP44:external')); + printUnusedAddressesData('BIP44:external'); + + // Print BIP44 Change Addresses + console.log(`\n${name} BIP44 Change Addresses:`); + printAddressTable(getAddressesByType('BIP44:change')); + printUnusedAddressesData('BIP44:change'); + + // Print BIP47 Addresses + console.log(`\n${name} BIP47 Addresses:`); + printAddressTable(getAddressesByType('BIP47')); + printUnusedAddressesData('BIP47'); + + // Print Outpoints + console.log(`\n${name} Wallet Outpoints:`); + printOutpointTable(serializedWallet.outpoints); + + // Print Pending Outpoints + // console.log(`\n${name} Wallet Pending Outpoints:`); + // printOutpointTable(serializedWallet.pendingOutpoints); + + // Print Sender Payment Code Info + console.log(`\n${name} Wallet Sender Payment Code Info:`); + printPaymentCodeInfo(serializedWallet.senderPaymentCodeInfo); + + // Print wallet Qi balance + const walletBalance = wallet.getBalanceForZone(quais.Zone.Cyprus1); + console.log(`\n=> ${name} Wallet balance: ${quais.formatQi(walletBalance)} Qi\n`); +} + +function printAddressTable(addresses) { + const addressTable = addresses.map(addr => ({ + PubKey: addr.pubKey, + Address: addr.address, + Index: addr.index, + Change: addr.change ? 'Yes' : 'No', + Zone: addr.zone, + Status: addr.status, + DerivationPath: addr.derivationPath + })); + console.table(addressTable); +} + +function printOutpointTable(outpoints) { + const outpointTable = outpoints.map(outpoint => ({ + Address: outpoint.address, + Denomination: outpoint.outpoint.denomination, + Index: outpoint.outpoint.index, + TxHash: outpoint.outpoint.txhash, + Zone: outpoint.zone, + Account: outpoint.account, + })); + console.table(outpointTable); +} + +function printPaymentCodeInfo(paymentCodeInfo) { + for (const [paymentCode, addressInfoArray] of Object.entries(paymentCodeInfo)) { + console.log(`Payment Code: ${paymentCode}`); + const paymentCodeTable = addressInfoArray.map(info => ({ + Address: info.address, + PubKey: info.pubKey, + Index: info.index, + Zone: info.zone, + Status: info.status + })); + console.table(paymentCodeTable); + } +} + + +module.exports = { + printWalletInfo, +}; From 5fe1d10835b3d79168613633d134e0b2da409105 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Wed, 23 Oct 2024 14:32:24 -0300 Subject: [PATCH 5/7] change variable names within getChangeAddress function --- src/wallet/qi-hdwallet.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index 1f10c232..dacaccec 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -504,22 +504,22 @@ export class QiHDWallet extends AbstractHDWallet { // 3. Generate as many unused addresses as required to populate the spend outputs const sendAddresses = await getDestinationAddresses(selection.spendOutputs.length); - const getChangeAddresses = async (count: number): Promise => { - const changeAddresses = this._addressesMap.get('BIP44:change') || []; - const addresses: QiAddressInfo[] = []; + const getChangeAddressesForOutputs = async (count: number): Promise => { + const currentChangeAddresses = this._addressesMap.get('BIP44:change') || []; + const outpusChangeAddresses: QiAddressInfo[] = []; - for (let i = 0; i < changeAddresses.length; i++) { - if (changeAddresses[i].status === AddressStatus.UNUSED) { - addresses.push(changeAddresses[i]); + for (let i = 0; i < currentChangeAddresses.length; i++) { + if (currentChangeAddresses[i].status === AddressStatus.UNUSED) { + outpusChangeAddresses.push(currentChangeAddresses[i]); } - if (addresses.length === count) break; + if (outpusChangeAddresses.length === count) break; } // Generate the remaining number of change addresses if needed - const remainingAddressesNeeded = count - addresses.length; + const remainingAddressesNeeded = count - outpusChangeAddresses.length; if (remainingAddressesNeeded > 0) { - addresses.push( + outpusChangeAddresses.push( ...Array(remainingAddressesNeeded) .fill(0) .map(() => this.getNextChangeAddressSync(0, originZone)), @@ -528,8 +528,8 @@ export class QiHDWallet extends AbstractHDWallet { // Combine the existing change addresses with the newly generated addresses and ensure they are unique and sorted by index const mergedChangeAddresses = [ - ...addresses.map((address) => ({ ...address, status: AddressStatus.ATTEMPTED_USE })), - ...changeAddresses, + ...outpusChangeAddresses.map((address) => ({ ...address, status: AddressStatus.ATTEMPTED_USE })), + ...currentChangeAddresses, ]; const sortedAndFilteredChangeAddresses = mergedChangeAddresses .filter((address, index, self) => self.findIndex((t) => t.address === address.address) === index) @@ -538,11 +538,11 @@ export class QiHDWallet extends AbstractHDWallet { // Update the _addressesMap with the modified change addresses and statuses this._addressesMap.set('BIP44:change', sortedAndFilteredChangeAddresses); - return addresses.map((address) => address.address); + return outpusChangeAddresses.map((address) => address.address); }; // 4. Get change addresses - const changeAddresses = await getChangeAddresses(selection.changeOutputs.length); + const changeAddresses = await getChangeAddressesForOutputs(selection.changeOutputs.length); // 5. Create the transaction and sign it using the signTransaction method let inputPubKeys = selection.inputs.map((input) => this.locateAddressInfo(input.address)?.pubKey); @@ -574,8 +574,8 @@ export class QiHDWallet extends AbstractHDWallet { // Determine if new addresses are needed for the change and spend outputs const changeAddressesNeeded = selection.changeOutputs.length - changeAddresses.length; if (changeAddressesNeeded > 0) { - const newChangeAddresses = await getChangeAddresses(changeAddressesNeeded); - changeAddresses.push(...newChangeAddresses); + const outpusChangeAddresses = await getChangeAddressesForOutputs(changeAddressesNeeded); + changeAddresses.push(...outpusChangeAddresses); } const spendAddressesNeeded = selection.spendOutputs.length - sendAddresses.length; From c999fd96cc33519f2caa11865284a2ae1a396085 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Fri, 25 Oct 2024 11:28:46 -0300 Subject: [PATCH 6/7] fix bug on getPrivateKeyForTxInput for paymentcodes --- src/wallet/qi-hdwallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index dacaccec..46f20253 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -872,7 +872,7 @@ export class QiHDWallet extends AbstractHDWallet { // (BIP47 addresses) const pcAddressInfo = addressInfo; const account = pcAddressInfo.account; - const index = pcAddressInfo.index - 1; + const index = pcAddressInfo.index; const counterpartyPaymentCode = pcAddressInfo.derivationPath; if (!counterpartyPaymentCode) { From 7db832e17ad66cfd31bf94c510c753432cfee0f1 Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Fri, 25 Oct 2024 11:37:24 -0300 Subject: [PATCH 7/7] modify testing script so Bob can afford Qi gas payment --- examples/wallets/qi-wallet-alice-bob-send-receive.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/wallets/qi-wallet-alice-bob-send-receive.js b/examples/wallets/qi-wallet-alice-bob-send-receive.js index 8489da2c..bd9cdebb 100644 --- a/examples/wallets/qi-wallet-alice-bob-send-receive.js +++ b/examples/wallets/qi-wallet-alice-bob-send-receive.js @@ -32,9 +32,10 @@ async function main() { console.log('Bob Qi wallet scan complete'); printWalletInfo('Bob', bobQiWallet); - // Alice sends 50 Qi to Bob - console.log('\nAlice sends 50 Qi to Bob'); - const tx = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + const aliceAmount = 5000000n; + // Alice sends 5000 Qi to Bob + console.log(`\nAlice sends ${quais.formatQi(aliceAmount)} Qi to Bob`); + const tx = await aliceQiWallet.sendTransaction(bobPaymentCode, aliceAmount, quais.Zone.Cyprus1, quais.Zone.Cyprus1); // console.log('Transaction sent: ', tx); console.log(`Transaction hash: ${tx.hash}`); console.log(`Tx contains ${tx.txInputs?.length} inputs`); @@ -56,8 +57,9 @@ async function main() { console.log('Bob Qi wallet sync complete'); printWalletInfo('Bob', bobQiWallet); - console.log('\nBob sends back 25 Qi to Alice'); - const tx2 = await bobQiWallet.sendTransaction(alicePaymentCode, 25000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + const bobAmount = 5000n; + console.log(`\nBob sends back ${quais.formatQi(bobAmount)} Qi to Alice`); + const tx2 = await bobQiWallet.sendTransaction(alicePaymentCode, bobAmount, quais.Zone.Cyprus1, quais.Zone.Cyprus1); console.log(`Transaction hash: ${tx2.hash}`); console.log(`Tx contains ${tx2.txInputs?.length} inputs`); console.log(`Tx inputs: ${JSON.stringify(tx2.txInputs)}`);