mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 23:08:07 +01:00
REF: BIP47 under the hood
This commit is contained in:
parent
e296f2b80b
commit
e1f18f3a29
5 changed files with 290 additions and 17 deletions
|
@ -333,7 +333,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
|
||||
// next, bip47 addresses
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
let hasUnconfirmed = false;
|
||||
this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
|
||||
this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
|
||||
|
@ -394,7 +394,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
this._txs_by_internal_index[c] = this._txs_by_internal_index[c].filter(tx => !!tx.confirmations);
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c].filter(tx => !!tx.confirmations);
|
||||
}
|
||||
}
|
||||
|
@ -485,7 +485,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
for (const tx of Object.values(txdatas)) {
|
||||
for (const vin of tx.vin) {
|
||||
if (vin.addresses && vin.addresses.indexOf(this._getBIP47AddressReceive(pc, c)) !== -1) {
|
||||
|
@ -563,7 +563,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
if (this._receive_payment_codes)
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + 1; c++) {
|
||||
ownedAddressesHashmap[this._getBIP47AddressReceive(pc, c)] = true;
|
||||
}
|
||||
}
|
||||
|
@ -881,7 +881,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
for (const pc of this._receive_payment_codes) {
|
||||
let confirmed = 0;
|
||||
let unconfirmed = 0;
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
const addr = this._getBIP47AddressReceive(pc, c);
|
||||
if (balances.addresses[addr].confirmed || balances.addresses[addr].unconfirmed) {
|
||||
confirmed = confirmed + balances.addresses[addr].confirmed;
|
||||
|
@ -1003,7 +1003,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
ownedAddressesHashmap[this._getInternalAddressByIndex(c)] = true;
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + 1; c++) {
|
||||
ownedAddressesHashmap[this._getBIP47AddressReceive(pc, c)] = true;
|
||||
}
|
||||
}
|
||||
|
@ -1061,7 +1061,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
if (this._getInternalAddressByIndex(c) === address) return path + '/1/' + c;
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
// not technically correct but well, to have at least somethign in PSBT...
|
||||
if (this._getBIP47AddressReceive(pc, c) === address) return "m/47'/0'/0'/" + c;
|
||||
}
|
||||
|
@ -1083,7 +1083,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
if (this._getInternalAddressByIndex(c) === address) return this._getNodePubkeyByIndex(1, c);
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
if (this._getBIP47AddressReceive(pc, c) === address) return this._getBIP47PubkeyByIndex(pc, c);
|
||||
}
|
||||
}
|
||||
|
@ -1105,7 +1105,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
if (this._getInternalAddressByIndex(c) === address) return this._getWIFByIndex(true, c);
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
if (this._getBIP47AddressReceive(pc, c) === address) return this._getBIP47WIF(pc, c);
|
||||
}
|
||||
}
|
||||
|
@ -1127,7 +1127,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
if (this._getInternalAddressByIndex(c) === cleanAddress) return true;
|
||||
}
|
||||
for (const pc of this._receive_payment_codes) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||
for (let c = 0; c < this._getNextFreePaymentCodeIndexReceive(pc) + this.gap_limit; c++) {
|
||||
if (this._getBIP47AddressReceive(pc, c) === address) return true;
|
||||
}
|
||||
}
|
||||
|
@ -1150,9 +1150,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
targets: CreateTransactionTarget[],
|
||||
feeRate: number,
|
||||
changeAddress: string,
|
||||
sequence: number,
|
||||
sequence: number = AbstractHDElectrumWallet.defaultRBFSequence,
|
||||
skipSigning = false,
|
||||
masterFingerprint: number,
|
||||
masterFingerprint: number = 0,
|
||||
): CreateTransactionResult {
|
||||
if (targets.length === 0) throw new Error('No destination provided');
|
||||
// compensating for coinselect inability to deal with segwit inputs, and overriding script length for proper vbytes calculation
|
||||
|
@ -1240,6 +1240,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
// this is not correct fingerprint, as we dont know realfingerprint - we got zpub with 84/0, but fingerpting
|
||||
// should be from root. basically, fingerprint should be provided from outside by user when importing zpub
|
||||
|
||||
if (output.address?.startsWith('PM')) {
|
||||
// ok its BIP47 payment code, so we need to unwrap a joint address for the receiver and use it instead:
|
||||
output.address = this._getNextFreePaymentCodeAddressSend(output.address);
|
||||
}
|
||||
|
||||
psbt.addOutput({
|
||||
address: output.address,
|
||||
// @ts-ignore types from bitcoinjs are not exported so we cant define outputData separately and add fields conditionally (either address or script should be present)
|
||||
|
@ -1532,6 +1537,60 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
return this._bip47_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* this method goes over all our txs and checks if we sent a notification tx in the past to the given PC
|
||||
*/
|
||||
needToNotifyBIP47(receiverPaymentCode: string): boolean {
|
||||
const publicBip47 = BIP47Factory(ecc).fromPaymentCode(receiverPaymentCode);
|
||||
const remoteNotificationAddress = publicBip47.getNotificationAddress();
|
||||
|
||||
for (const tx of this.getTransactions()) {
|
||||
for (const output of tx.outputs) {
|
||||
if (output.scriptPubKey?.addresses?.includes(remoteNotificationAddress)) return false;
|
||||
// ^^^ if in the past we sent a tx to his notification address - most likely that was a proper notification
|
||||
// transaction with OP_RETURN.
|
||||
// but not gona verify it here, will just trust it
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* return BIP47 payment code of the counterparty of this transaction (someone paid us, or we paid someone)
|
||||
* or false if it was a non-BIP47 transaction
|
||||
*/
|
||||
getSenderByTxid(txid: string): string | false {
|
||||
for (const pc of Object.keys(this._txs_by_payment_code_index)) {
|
||||
// iterating all payment codes
|
||||
|
||||
for (const txs of Object.values(this._txs_by_payment_code_index[pc])) {
|
||||
for (const tx of txs) {
|
||||
if (tx.txid === txid) {
|
||||
return pc; // found it!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checking txs we sent to counterparties
|
||||
|
||||
for (const pc of this._send_payment_codes) {
|
||||
for (const tx of this.getTransactions().filter(transaction => transaction.txid === txid)) {
|
||||
for (const out of tx.outputs) {
|
||||
for (const address of out.scriptPubKey?.addresses ?? []) {
|
||||
if (this._addresses_by_payment_code_send[pc] && Object.values(this._addresses_by_payment_code_send[pc]).includes(address)) {
|
||||
// found it!
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // found nothing
|
||||
}
|
||||
|
||||
createBip47NotificationTransaction(utxos: CreateTransactionUtxo[], receiverPaymentCode: string, feeRate: number, changeAddress: string) {
|
||||
const aliceBip47 = BIP47Factory(ecc).fromBip39Seed(this.getSecret(), undefined, this.getPassphrase());
|
||||
const bobBip47 = BIP47Factory(ecc).fromPaymentCode(receiverPaymentCode);
|
||||
|
@ -1652,7 +1711,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
/**
|
||||
* for counterparties we can pay, we sync shared addresses to find the one we havent used yet.
|
||||
* this method could benefit from rewriting in batch requests, but not necessary - its only going to be called
|
||||
* once in a while (when user decides to pay agiven counterparty again)
|
||||
* once in a while (when user decides to pay a given counterparty again)
|
||||
*/
|
||||
async syncBip47ReceiversAddresses(receiverPaymentCode: string) {
|
||||
this._next_free_payment_code_address_index_send[receiverPaymentCode] =
|
||||
|
@ -1670,6 +1729,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
}
|
||||
|
||||
// empty address, stop here, we found our latest index and filled array with shared addresses
|
||||
this._next_free_payment_code_address_index_send[receiverPaymentCode] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1678,6 +1738,18 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
return this._receive_payment_codes;
|
||||
}
|
||||
|
||||
getBIP47ReceiverPaymentCodes(): string[] {
|
||||
return this._send_payment_codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* adding counterparty whom we can pay. trusting that notificaton transaction is in place already
|
||||
*/
|
||||
addBIP47Receiver(paymentCode: string) {
|
||||
if (this._send_payment_codes.includes(paymentCode)) return; // duplicates
|
||||
this._send_payment_codes.push(paymentCode);
|
||||
}
|
||||
|
||||
_hdNodeToAddress(hdNode: BIP32Interface): string {
|
||||
return this._nodeToBech32SegwitAddress(hdNode);
|
||||
}
|
||||
|
@ -1709,6 +1781,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
if (!this._addresses_by_payment_code_send[paymentCode]) this._addresses_by_payment_code_send[paymentCode] = [];
|
||||
|
||||
if (this._addresses_by_payment_code_send[paymentCode][index]) {
|
||||
// cache hit
|
||||
return this._addresses_by_payment_code_send[paymentCode][index];
|
||||
}
|
||||
|
||||
|
@ -1718,10 +1791,19 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
return address;
|
||||
}
|
||||
|
||||
_getNextFreePaymentCodeAddress(paymentCode: string) {
|
||||
_getNextFreePaymentCodeIndexReceive(paymentCode: string) {
|
||||
return this._next_free_payment_code_address_index_receive[paymentCode] || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* when sending funds to a payee, this method will return next unused joint address for him.
|
||||
* this method assumes that we synced our payee via `syncBip47ReceiversAddresses()`
|
||||
*/
|
||||
_getNextFreePaymentCodeAddressSend(paymentCode: string) {
|
||||
this._next_free_payment_code_address_index_send[paymentCode] = this._next_free_payment_code_address_index_send[paymentCode] || 0;
|
||||
return this._getBIP47AddressSend(paymentCode, this._next_free_payment_code_address_index_send[paymentCode]);
|
||||
}
|
||||
|
||||
_getBalancesByPaymentCodeIndex(paymentCode: string): BalanceByIndex {
|
||||
return this._balances_by_payment_code_index[paymentCode] || { c: 0, u: 0 };
|
||||
}
|
||||
|
|
|
@ -615,6 +615,7 @@
|
|||
"payment_code": "Payment Code",
|
||||
"payment_codes_list": "Payment Codes List",
|
||||
"who_can_pay_me": "Who can pay me:",
|
||||
"whom_can_i_pay": "Whom can I pay:",
|
||||
"purpose": "Reusable and shareable code (BIP47)",
|
||||
"not_found": "Payment code not found"
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { PaymentCodeStackParamList } from '../../Navigation';
|
|||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import loc from '../../loc';
|
||||
import CopyTextToClipboard from '../../components/CopyTextToClipboard';
|
||||
import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electrum-wallet';
|
||||
|
||||
interface DataSection {
|
||||
title: string;
|
||||
|
@ -21,15 +22,18 @@ export default function PaymentCodesList({ route }: Props) {
|
|||
useEffect(() => {
|
||||
if (!walletID) return;
|
||||
|
||||
const foundWallet = wallets.find(w => w.getID() === walletID);
|
||||
const foundWallet = wallets.find(w => w.getID() === walletID) as unknown as AbstractHDElectrumWallet;
|
||||
if (!foundWallet) return;
|
||||
|
||||
const newData: DataSection[] = [
|
||||
{
|
||||
title: loc.bip47.who_can_pay_me,
|
||||
// @ts-ignore remove later
|
||||
data: foundWallet.getBIP47SenderPaymentCodes(),
|
||||
},
|
||||
{
|
||||
title: loc.bip47.whom_can_i_pay,
|
||||
data: foundWallet.getBIP47ReceiverPaymentCodes(),
|
||||
},
|
||||
];
|
||||
setData(newData);
|
||||
}, [walletID, wallets]);
|
||||
|
|
|
@ -143,4 +143,105 @@ describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
|||
bobBip47.getNotificationAddress(),
|
||||
); // transaction is to Bob's notification address
|
||||
});
|
||||
|
||||
it('can tell whom to notify and whom dont', async () => {
|
||||
if (!process.env.BIP47_HD_MNEMONIC) {
|
||||
console.error('process.env.BIP47_HD_MNEMONIC not set, skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
// whom we are going to notify:
|
||||
const bip47instanceReceiver = BIP47Factory(ecc).fromBip39Seed(process.env.BIP47_HD_MNEMONIC.split(':')[0], undefined, '1');
|
||||
|
||||
// notifier:
|
||||
const walletSender = new HDSegwitBech32Wallet();
|
||||
walletSender.setSecret(process.env.BIP47_HD_MNEMONIC.split(':')[1]);
|
||||
walletSender.switchBIP47(true);
|
||||
await walletSender.fetchBIP47SenderPaymentCodes();
|
||||
await walletSender.fetchBalance();
|
||||
await walletSender.fetchTransactions();
|
||||
|
||||
assert.ok(walletSender.getTransactions().length >= 3);
|
||||
assert.ok(walletSender._receive_payment_codes.length === 1);
|
||||
|
||||
assert.strictEqual(walletSender.needToNotifyBIP47(bip47instanceReceiver.getSerializedPaymentCode()), false); // already notified in the past
|
||||
assert.strictEqual(
|
||||
walletSender.needToNotifyBIP47(
|
||||
'PM8TJdfXvRasx4WNpxky25ZKxhvfEiGYW9mka92tfiqDRSL7LQdxnC8uAk9k3okXctZowVwY2PUndjCQR6DHyuVVwqmy2aodmZNHgfFZcJRNTuBAXJCp',
|
||||
),
|
||||
true,
|
||||
); // random PC from interwebz. never interacted with him, so need to notify
|
||||
});
|
||||
|
||||
it('can tell with which counterparty PC transaction is', async () => {
|
||||
if (!process.env.BIP47_HD_MNEMONIC) {
|
||||
console.error('process.env.BIP47_HD_MNEMONIC not set, skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
const w = new HDSegwitBech32Wallet();
|
||||
w.setSecret(process.env.BIP47_HD_MNEMONIC.split(':')[0]);
|
||||
w.setPassphrase('1');
|
||||
|
||||
w.switchBIP47(true);
|
||||
|
||||
await w.fetchBIP47SenderPaymentCodes();
|
||||
await w.fetchBalance();
|
||||
await w.fetchTransactions();
|
||||
|
||||
assert.ok(
|
||||
w
|
||||
.getBIP47SenderPaymentCodes()
|
||||
.includes('PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo'),
|
||||
); // sparrow payment code
|
||||
|
||||
assert.strictEqual(
|
||||
w.getTransactions().find(tx => tx.txid === '64058a49bb75481fc0bebbb0d84a4aceebe319f9d32929e73cefb21d83342e9f')?.value,
|
||||
100000,
|
||||
); // sparrow paid us after sparrow made a notification tx
|
||||
|
||||
assert.ok(
|
||||
!w.needToNotifyBIP47(
|
||||
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||
),
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
w.getSenderByTxid('64058a49bb75481fc0bebbb0d84a4aceebe319f9d32929e73cefb21d83342e9f'),
|
||||
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||
); // we got paid
|
||||
|
||||
// pretending that user added this PC as a counterparty (sent a notif tx) to pay to:
|
||||
w.addBIP47Receiver(
|
||||
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||
);
|
||||
assert.ok(
|
||||
!w.needToNotifyBIP47(
|
||||
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||
),
|
||||
); // dont need to notify
|
||||
|
||||
// prior to sync, we have no info on which joint address shall be available
|
||||
assert.strictEqual(
|
||||
w._next_free_payment_code_address_index_send
|
||||
.PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo,
|
||||
undefined, // basically zero
|
||||
);
|
||||
|
||||
await w.syncBip47ReceiversAddresses(
|
||||
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||
);
|
||||
|
||||
// after sync, we know that index 0 was used so index 1 is next free:
|
||||
assert.strictEqual(
|
||||
w._next_free_payment_code_address_index_send
|
||||
.PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo,
|
||||
1,
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
w.getSenderByTxid('73a2ac70858c5b306b101a861d582f40c456a692096a4e4805aa739258c4400d'),
|
||||
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||
); // we paid sparrow
|
||||
});
|
||||
});
|
||||
|
|
|
@ -157,7 +157,7 @@ describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
|||
|
||||
const changeAddress = 'bc1q7vraw79vcf7qhnefeaul578h7vjc7tr95ywfuq';
|
||||
|
||||
const { tx, fee } = await walletSender.createBip47NotificationTransaction(
|
||||
const { tx, fee } = walletSender.createBip47NotificationTransaction(
|
||||
utxos,
|
||||
bip47instanceReceiver.getSerializedPaymentCode(),
|
||||
33,
|
||||
|
@ -177,6 +177,91 @@ describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
|||
assert.strictEqual(Math.round(actualFeerate), 33);
|
||||
});
|
||||
|
||||
it('should be able to pay to PC', async () => {
|
||||
if (!process.env.BIP47_HD_MNEMONIC) {
|
||||
console.error('process.env.BIP47_HD_MNEMONIC not set, skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
// whom we are going to pay:
|
||||
const bip47instanceReceiver = BIP47Factory(ecc).fromBip39Seed(process.env.BIP47_HD_MNEMONIC.split(':')[0], undefined, '1');
|
||||
|
||||
// notifier:
|
||||
const walletSender = new HDSegwitBech32Wallet();
|
||||
walletSender.setSecret(process.env.BIP47_HD_MNEMONIC.split(':')[1]);
|
||||
walletSender.switchBIP47(true);
|
||||
|
||||
// since we cant do network calls, we hardcode our senders so later `_getWIFbyAddress`
|
||||
// could resolve wif for address deposited by him (funds we want to use reside on addresses from BIP47)
|
||||
walletSender._receive_payment_codes = [
|
||||
'PM8TJXuZNUtSibuXKFM6bhCxpNaSye6r4px2GXRV5v86uRdH9Raa8ZtXEkG7S4zLREf4ierjMsxLXSFTbRVUnRmvjw9qnc7zZbyXyBstSmjcb7uVcDYF',
|
||||
];
|
||||
|
||||
walletSender.addBIP47Receiver(bip47instanceReceiver.getSerializedPaymentCode());
|
||||
|
||||
const utxos: CreateTransactionUtxo[] = [
|
||||
{
|
||||
value: 74822,
|
||||
address: 'bc1qaxxc4gwx6rd6rymq08qwpxhesd4jqu93lvjsyt',
|
||||
txid: '73a2ac70858c5b306b101a861d582f40c456a692096a4e4805aa739258c4400d',
|
||||
vout: 0,
|
||||
wif: walletSender._getWIFbyAddress('bc1qaxxc4gwx6rd6rymq08qwpxhesd4jqu93lvjsyt') + '',
|
||||
},
|
||||
{
|
||||
value: 894626,
|
||||
address: 'bc1qr60ek5gtjs04akcp9f5x25v5gyp2tmspx78jxl',
|
||||
txid: '64058a49bb75481fc0bebbb0d84a4aceebe319f9d32929e73cefb21d83342e9f',
|
||||
vout: 0,
|
||||
wif: walletSender._getWIFbyAddress('bc1qr60ek5gtjs04akcp9f5x25v5gyp2tmspx78jxl') + '',
|
||||
},
|
||||
];
|
||||
|
||||
const changeAddress = 'bc1q7vraw79vcf7qhnefeaul578h7vjc7tr95ywfuq';
|
||||
|
||||
const { tx, fee } = walletSender.createTransaction(
|
||||
utxos,
|
||||
[
|
||||
{ address: bip47instanceReceiver.getSerializedPaymentCode(), value: 10234 },
|
||||
{ address: '13HaCAB4jf7FYSZexJxoczyDDnutzZigjS', value: 22000 },
|
||||
],
|
||||
6,
|
||||
changeAddress,
|
||||
);
|
||||
assert(tx);
|
||||
|
||||
assert.strictEqual(tx.outs[0].value, 10234);
|
||||
assert.strictEqual(
|
||||
bitcoin.address.fromOutputScript(tx.outs[0].script),
|
||||
walletSender._getBIP47AddressSend(bip47instanceReceiver.getSerializedPaymentCode(), 0),
|
||||
);
|
||||
|
||||
assert.strictEqual(tx.outs[1].value, 22000);
|
||||
assert.strictEqual(bitcoin.address.fromOutputScript(tx.outs[1].script), '13HaCAB4jf7FYSZexJxoczyDDnutzZigjS');
|
||||
|
||||
const actualFeerate = fee / tx.virtualSize();
|
||||
assert.strictEqual(Math.round(actualFeerate), 6);
|
||||
|
||||
// lets retry, but pretend that a few sender's addresses were used:
|
||||
|
||||
walletSender._next_free_payment_code_address_index_send[bip47instanceReceiver.getSerializedPaymentCode()] = 6;
|
||||
|
||||
const { tx: tx2 } = walletSender.createTransaction(
|
||||
utxos,
|
||||
[
|
||||
{ address: bip47instanceReceiver.getSerializedPaymentCode(), value: 10234 },
|
||||
{ address: '13HaCAB4jf7FYSZexJxoczyDDnutzZigjS', value: 22000 },
|
||||
],
|
||||
6,
|
||||
changeAddress,
|
||||
);
|
||||
assert(tx2);
|
||||
|
||||
assert.strictEqual(
|
||||
bitcoin.address.fromOutputScript(tx2.outs[0].script),
|
||||
walletSender._getBIP47AddressSend(bip47instanceReceiver.getSerializedPaymentCode(), 6),
|
||||
);
|
||||
});
|
||||
|
||||
it('can unwrap addresses to send & receive', () => {
|
||||
if (!process.env.BIP47_HD_MNEMONIC) {
|
||||
console.error('process.env.BIP47_HD_MNEMONIC not set, skipped');
|
||||
|
|
Loading…
Add table
Reference in a new issue