mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-04 04:13:49 +01:00
REF: refactored and improved BIP47
This commit is contained in:
parent
49619e4666
commit
b0b7529ff1
17 changed files with 355 additions and 168 deletions
|
@ -84,6 +84,7 @@ import SettingsPrivacy from './screen/settings/SettingsPrivacy';
|
||||||
import LNDViewAdditionalInvoicePreImage from './screen/lnd/lndViewAdditionalInvoicePreImage';
|
import LNDViewAdditionalInvoicePreImage from './screen/lnd/lndViewAdditionalInvoicePreImage';
|
||||||
import LdkViewLogs from './screen/wallets/ldkViewLogs';
|
import LdkViewLogs from './screen/wallets/ldkViewLogs';
|
||||||
import PaymentCode from './screen/wallets/paymentCode';
|
import PaymentCode from './screen/wallets/paymentCode';
|
||||||
|
import PaymentCodesList from './screen/wallets/paymentCodesList';
|
||||||
import loc from './loc';
|
import loc from './loc';
|
||||||
|
|
||||||
const WalletsStack = createNativeStackNavigator();
|
const WalletsStack = createNativeStackNavigator();
|
||||||
|
@ -472,6 +473,11 @@ const PaymentCodeStackRoot = () => {
|
||||||
return (
|
return (
|
||||||
<PaymentCodeStack.Navigator name="PaymentCodeRoot" screenOptions={{ headerHideShadow: true }} initialRouteName="PaymentCode">
|
<PaymentCodeStack.Navigator name="PaymentCodeRoot" screenOptions={{ headerHideShadow: true }} initialRouteName="PaymentCode">
|
||||||
<PaymentCodeStack.Screen name="PaymentCode" component={PaymentCode} options={{ headerTitle: loc.bip47.payment_code }} />
|
<PaymentCodeStack.Screen name="PaymentCode" component={PaymentCode} options={{ headerTitle: loc.bip47.payment_code }} />
|
||||||
|
<PaymentCodeStack.Screen
|
||||||
|
name="PaymentCodesList"
|
||||||
|
component={PaymentCodesList}
|
||||||
|
options={{ headerTitle: loc.bip47.payment_codes_list }}
|
||||||
|
/>
|
||||||
</PaymentCodeStack.Navigator>
|
</PaymentCodeStack.Navigator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1053,24 +1053,3 @@ function txhexToElectrumTransaction(txhex) {
|
||||||
|
|
||||||
// exported only to be used in unit tests
|
// exported only to be used in unit tests
|
||||||
module.exports.txhexToElectrumTransaction = txhexToElectrumTransaction;
|
module.exports.txhexToElectrumTransaction = txhexToElectrumTransaction;
|
||||||
|
|
||||||
// module.exports.multiGetTransactionHexByTxid = async function (txids, batchsize = 100) {
|
|
||||||
// if (!mainClient) throw new Error('Electrum client is not connected');
|
|
||||||
|
|
||||||
// const chunks = splitIntoChunks(txids, batchsize);
|
|
||||||
// const result = {};
|
|
||||||
// for (const chunk of chunks) {
|
|
||||||
// if (!disableBatching) {
|
|
||||||
// await Promise.all(
|
|
||||||
// chunk.map(async txid => {
|
|
||||||
// const hex = await mainClient.blockchainTransaction_get(txid);
|
|
||||||
// result[txid] = hex;
|
|
||||||
// }),
|
|
||||||
// );
|
|
||||||
// } else {
|
|
||||||
// const res = await mainClient.blockchainTransaction_getBatch(chunk);
|
|
||||||
// res.forEach(({ result: r, param }) => (result[param] = r));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
// };
|
|
||||||
|
|
|
@ -730,6 +730,7 @@ export class AppStorage {
|
||||||
console.log('fetchSenderPaymentCodes for wallet#', typeof index === 'undefined' ? '(all)' : index);
|
console.log('fetchSenderPaymentCodes for wallet#', typeof index === 'undefined' ? '(all)' : index);
|
||||||
if (index || index === 0) {
|
if (index || index === 0) {
|
||||||
try {
|
try {
|
||||||
|
if (!this.wallets[index].allowBIP47()) return;
|
||||||
await this.wallets[index].fetchBIP47SenderPaymentCodes();
|
await this.wallets[index].fetchBIP47SenderPaymentCodes();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch sender payment codes for wallet', index, error);
|
console.error('Failed to fetch sender payment codes for wallet', index, error);
|
||||||
|
@ -737,6 +738,7 @@ export class AppStorage {
|
||||||
} else {
|
} else {
|
||||||
for (const wallet of this.wallets) {
|
for (const wallet of this.wallets) {
|
||||||
try {
|
try {
|
||||||
|
if (!wallet.allowBIP47()) continue;
|
||||||
await wallet.fetchBIP47SenderPaymentCodes();
|
await wallet.fetchBIP47SenderPaymentCodes();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch sender payment codes for wallet', wallet.label, error);
|
console.error('Failed to fetch sender payment codes for wallet', wallet.label, error);
|
||||||
|
|
|
@ -115,14 +115,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
async generate() {
|
async generate() {
|
||||||
const buf = await randomBytes(16);
|
const buf = await randomBytes(16);
|
||||||
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
||||||
this.setBIP47PaymentCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateFromEntropy(user: Buffer) {
|
async generateFromEntropy(user: Buffer) {
|
||||||
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
|
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
|
||||||
const buf = Buffer.concat([user, random], 32);
|
const buf = Buffer.concat([user, random], 32);
|
||||||
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
|
||||||
this.setBIP47PaymentCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getExternalWIFByIndex(index: number): string | false {
|
_getExternalWIFByIndex(index: number): string | false {
|
||||||
|
@ -175,12 +173,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
let address;
|
let address;
|
||||||
if (node === 0) {
|
if (node === 0) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
address = this.constructor._nodeToBech32SegwitAddress(this._node0.derive(index));
|
address = this._hdNodeToAddress(this._node0.derive(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node === 1) {
|
if (node === 1) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
address = this.constructor._nodeToBech32SegwitAddress(this._node1.derive(index));
|
address = this._hdNodeToAddress(this._node1.derive(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node === 0) {
|
if (node === 0) {
|
||||||
|
@ -709,7 +707,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
this.next_free_address_index = await this._binarySearchIterationForExternalAddress(1000);
|
this.next_free_address_index = await this._binarySearchIterationForExternalAddress(1000);
|
||||||
if (this._sender_payment_codes) {
|
if (this._sender_payment_codes) {
|
||||||
for (const pc of this._sender_payment_codes) {
|
for (const pc of this._sender_payment_codes) {
|
||||||
this._next_free_payment_code_address_index[pc] = await this._binarySearchIterationForBIP47Address(pc, 10);
|
this._next_free_payment_code_address_index[pc] = await this._binarySearchIterationForBIP47Address(pc, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end rescanning fresh wallet
|
} // end rescanning fresh wallet
|
||||||
|
@ -1058,7 +1056,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
if (this._getInternalAddressByIndex(c) === address) return this._getNodePubkeyByIndex(1, c);
|
if (this._getInternalAddressByIndex(c) === address) return this._getNodePubkeyByIndex(1, c);
|
||||||
}
|
}
|
||||||
for (const pc of this._sender_payment_codes) {
|
for (const pc of this._sender_payment_codes) {
|
||||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||||
if (this._getBIP47Address(pc, c) === address) return this._getBIP47PubkeyByIndex(pc, c);
|
if (this._getBIP47Address(pc, c) === address) return this._getBIP47PubkeyByIndex(pc, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1080,7 +1078,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
if (this._getInternalAddressByIndex(c) === address) return this._getWIFByIndex(true, c);
|
if (this._getInternalAddressByIndex(c) === address) return this._getWIFByIndex(true, c);
|
||||||
}
|
}
|
||||||
for (const pc of this._sender_payment_codes) {
|
for (const pc of this._sender_payment_codes) {
|
||||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||||
if (this._getBIP47Address(pc, c) === address) return this._getBIP47WIF(pc, c);
|
if (this._getBIP47Address(pc, c) === address) return this._getBIP47WIF(pc, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1100,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
if (this._getInternalAddressByIndex(c) === cleanAddress) return true;
|
if (this._getInternalAddressByIndex(c) === cleanAddress) return true;
|
||||||
}
|
}
|
||||||
for (const pc of this._sender_payment_codes) {
|
for (const pc of this._sender_payment_codes) {
|
||||||
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
|
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
|
||||||
if (this._getBIP47Address(pc, c) === address) return true;
|
if (this._getBIP47Address(pc, c) === address) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1438,35 +1436,13 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
return AbstractHDElectrumWallet.seedToFingerprint(seed);
|
return AbstractHDElectrumWallet.seedToFingerprint(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as that in AbstractHDWallet, but also sets the BIP47 payment code
|
prepareForSerialization() {
|
||||||
setSecret(newSecret: string): this {
|
super.prepareForSerialization();
|
||||||
this.secret = newSecret.trim().toLowerCase();
|
delete this._bip47_instance;
|
||||||
this.secret = this.secret.replace(/[^a-zA-Z0-9]/g, ' ').replace(/\s+/g, ' ');
|
|
||||||
|
|
||||||
// Try to match words to the default bip39 wordlist and complete partial words
|
|
||||||
const wordlist = bip39.wordlists[bip39.getDefaultWordlist()];
|
|
||||||
const lookupMap = wordlist.reduce((map, word) => {
|
|
||||||
const prefix3 = word.substr(0, 3);
|
|
||||||
const prefix4 = word.substr(0, 4);
|
|
||||||
|
|
||||||
map.set(prefix3, !map.has(prefix3) ? word : false);
|
|
||||||
map.set(prefix4, !map.has(prefix4) ? word : false);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}, new Map<string, string | false>());
|
|
||||||
|
|
||||||
this.secret = this.secret
|
|
||||||
.split(' ')
|
|
||||||
.map(word => lookupMap.get(word) || word)
|
|
||||||
.join(' ');
|
|
||||||
|
|
||||||
this.setBIP47PaymentCode();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether BIP47 is enabled
|
* Whether BIP47 is enabled. This is per-wallet setting that can be changed, NOT a feature-flag
|
||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
isBIP47Enabled(): boolean {
|
isBIP47Enabled(): boolean {
|
||||||
|
@ -1478,15 +1454,18 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBIP47FromSeed(): BIP47Interface {
|
getBIP47FromSeed(): BIP47Interface {
|
||||||
if (!this._bip47_instance) this._bip47_instance = bip47.fromBip39Seed(this.secret, undefined, this.passphrase);
|
if (!this._bip47_instance) {
|
||||||
|
this._bip47_instance = bip47.fromBip39Seed(this.secret, undefined, this.passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
return this._bip47_instance;
|
return this._bip47_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBIP47PaymentCode(): void {
|
getBIP47PaymentCode(): string {
|
||||||
|
if (!this._payment_code) {
|
||||||
this._payment_code = this.getBIP47FromSeed().getSerializedPaymentCode();
|
this._payment_code = this.getBIP47FromSeed().getSerializedPaymentCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
getBIP47PaymentCode(): string {
|
|
||||||
return this._payment_code;
|
return this._payment_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1516,6 +1495,10 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
return this._sender_payment_codes;
|
return this._sender_payment_codes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_hdNodeToAddress(hdNode: BIP32Interface): string {
|
||||||
|
return this.constructor._nodeToBech32SegwitAddress(hdNode);
|
||||||
|
}
|
||||||
|
|
||||||
_getBIP47Address(paymentCode: string, index: number): string {
|
_getBIP47Address(paymentCode: string, index: number): string {
|
||||||
if (!this._addresses_by_payment_code[paymentCode]) this._addresses_by_payment_code[paymentCode] = [];
|
if (!this._addresses_by_payment_code[paymentCode]) this._addresses_by_payment_code[paymentCode] = [];
|
||||||
|
|
||||||
|
@ -1527,7 +1510,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
const senderBIP47_instance = bip47.fromPaymentCode(paymentCode);
|
const senderBIP47_instance = bip47.fromPaymentCode(paymentCode);
|
||||||
const remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
|
const remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
|
||||||
const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
|
const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
|
||||||
const address = bip47_instance.getAddressFromNode(hdNode, bip47_instance.network);
|
const address = this._hdNodeToAddress(hdNode);
|
||||||
this._address_to_wif_cache[address] = hdNode.toWIF();
|
this._address_to_wif_cache[address] = hdNode.toWIF();
|
||||||
this._addresses_by_payment_code[paymentCode][index] = address;
|
this._addresses_by_payment_code[paymentCode][index] = address;
|
||||||
return address;
|
return address;
|
||||||
|
|
|
@ -143,6 +143,10 @@ export class AbstractWallet {
|
||||||
return BitcoinUnit.BTC;
|
return BitcoinUnit.BTC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowBIP47(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
allowReceive(): boolean {
|
allowReceive(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,10 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowBIP47() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
getXpub() {
|
getXpub() {
|
||||||
if (this._xpub) {
|
if (this._xpub) {
|
||||||
return this._xpub; // cache hit
|
return this._xpub; // cache hit
|
||||||
|
@ -48,44 +52,8 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
||||||
return this._xpub;
|
return this._xpub;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNodeAddressByIndex(node, index) {
|
_hdNodeToAddress(hdNode) {
|
||||||
index = index * 1; // cast to int
|
return this.constructor._nodeToLegacyAddress(hdNode);
|
||||||
if (node === 0) {
|
|
||||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1) {
|
|
||||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 0 && !this._node0) {
|
|
||||||
const xpub = this.getXpub();
|
|
||||||
const hdNode = bip32.fromBase58(xpub);
|
|
||||||
this._node0 = hdNode.derive(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1 && !this._node1) {
|
|
||||||
const xpub = this.getXpub();
|
|
||||||
const hdNode = bip32.fromBase58(xpub);
|
|
||||||
this._node1 = hdNode.derive(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
let address;
|
|
||||||
if (node === 0) {
|
|
||||||
address = this.constructor._nodeToLegacyAddress(this._node0.derive(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1) {
|
|
||||||
address = this.constructor._nodeToLegacyAddress(this._node1.derive(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 0) {
|
|
||||||
return (this.external_addresses_cache[index] = address);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1) {
|
|
||||||
return (this.internal_addresses_cache[index] = address);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchUtxo() {
|
async fetchUtxo() {
|
||||||
|
|
|
@ -46,4 +46,8 @@ export class HDSegwitBech32Wallet extends AbstractHDElectrumWallet {
|
||||||
allowXpub() {
|
allowXpub() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowBIP47() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,44 +40,8 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNodeAddressByIndex(node, index) {
|
_hdNodeToAddress(hdNode) {
|
||||||
index = index * 1; // cast to int
|
return this.constructor._nodeToP2shSegwitAddress(hdNode);
|
||||||
if (node === 0) {
|
|
||||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1) {
|
|
||||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 0 && !this._node0) {
|
|
||||||
const xpub = this.constructor._ypubToXpub(this.getXpub());
|
|
||||||
const hdNode = bip32.fromBase58(xpub);
|
|
||||||
this._node0 = hdNode.derive(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1 && !this._node1) {
|
|
||||||
const xpub = this.constructor._ypubToXpub(this.getXpub());
|
|
||||||
const hdNode = bip32.fromBase58(xpub);
|
|
||||||
this._node1 = hdNode.derive(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let address;
|
|
||||||
if (node === 0) {
|
|
||||||
address = this.constructor._nodeToP2shSegwitAddress(this._node0.derive(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1) {
|
|
||||||
address = this.constructor._nodeToP2shSegwitAddress(this._node1.derive(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 0) {
|
|
||||||
return (this.external_addresses_cache[index] = address);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node === 1) {
|
|
||||||
return (this.internal_addresses_cache[index] = address);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -100,6 +100,7 @@ export class WatchOnlyWallet extends LegacyWallet {
|
||||||
if (this._hdWalletInstance) {
|
if (this._hdWalletInstance) {
|
||||||
delete this._hdWalletInstance._node0;
|
delete this._hdWalletInstance._node0;
|
||||||
delete this._hdWalletInstance._node1;
|
delete this._hdWalletInstance._node1;
|
||||||
|
delete this._hdWalletInstance._bip47_instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ export default class TransactionsNavigationHeader extends Component {
|
||||||
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
|
<Text style={styles.manageFundsButtonText}>{loc.lnd.title}</Text>
|
||||||
</ToolTipMenu>
|
</ToolTipMenu>
|
||||||
)}
|
)}
|
||||||
{this.state.wallet.isBIP47Enabled() && (
|
{this.state.wallet.allowBIP47() && this.state.wallet.isBIP47Enabled() && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
|
|
@ -593,6 +593,8 @@
|
||||||
},
|
},
|
||||||
"bip47": {
|
"bip47": {
|
||||||
"payment_code": "Payment Code",
|
"payment_code": "Payment Code",
|
||||||
|
"payment_codes_list": "Payment Codes List",
|
||||||
|
"who_can_pay_me": "Who can pay me:",
|
||||||
"purpose": "Reusable and shareable code (BIP47)"
|
"purpose": "Reusable and shareable code (BIP47)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,7 @@ const WalletDetails = () => {
|
||||||
}
|
}
|
||||||
}, [wallet]);
|
}, [wallet]);
|
||||||
|
|
||||||
const setLabel = () => {
|
const save = () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (walletName.trim().length > 0) {
|
if (walletName.trim().length > 0) {
|
||||||
wallet.setLabel(walletName.trim());
|
wallet.setLabel(walletName.trim());
|
||||||
|
@ -211,7 +211,7 @@ const WalletDetails = () => {
|
||||||
testID="Save"
|
testID="Save"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
style={[styles.save, stylesHook.save]}
|
style={[styles.save, stylesHook.save]}
|
||||||
onPress={setLabel}
|
onPress={save}
|
||||||
>
|
>
|
||||||
<Text style={[styles.saveText, stylesHook.saveText]}>{loc.wallets.details_save}</Text>
|
<Text style={[styles.saveText, stylesHook.saveText]}>{loc.wallets.details_save}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
@ -312,6 +312,14 @@ const WalletDetails = () => {
|
||||||
walletID: wallet.getID(),
|
walletID: wallet.getID(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const navigateToPaymentCodes = () =>
|
||||||
|
navigate('PaymentCodeRoot', {
|
||||||
|
screen: 'PaymentCodesList',
|
||||||
|
params: {
|
||||||
|
walletID: wallet.getID(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const exportInternals = async () => {
|
const exportInternals = async () => {
|
||||||
if (backdoorPressed < 10) return setBackdoorPressed(backdoorPressed + 1);
|
if (backdoorPressed < 10) return setBackdoorPressed(backdoorPressed + 1);
|
||||||
setBackdoorPressed(0);
|
setBackdoorPressed(0);
|
||||||
|
@ -568,6 +576,7 @@ const WalletDetails = () => {
|
||||||
<BlueText>{wallet.getTransactions().length}</BlueText>
|
<BlueText>{wallet.getTransactions().length}</BlueText>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
|
{wallet.allowBIP47() ? (
|
||||||
<>
|
<>
|
||||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.bip47.payment_code}</Text>
|
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.bip47.payment_code}</Text>
|
||||||
<View style={styles.hardware}>
|
<View style={styles.hardware}>
|
||||||
|
@ -575,6 +584,7 @@ const WalletDetails = () => {
|
||||||
<Switch value={isBIP47Enabled} onValueChange={setIsBIP47Enabled} />
|
<Switch value={isBIP47Enabled} onValueChange={setIsBIP47Enabled} />
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<View>
|
<View>
|
||||||
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && (
|
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||||
|
@ -611,6 +621,7 @@ const WalletDetails = () => {
|
||||||
{(wallet instanceof AbstractHDElectrumWallet || (wallet.type === WatchOnlyWallet.type && wallet.isHd())) && (
|
{(wallet instanceof AbstractHDElectrumWallet || (wallet.type === WatchOnlyWallet.type && wallet.isHd())) && (
|
||||||
<BlueListItem onPress={navigateToAddresses} title={loc.wallets.details_show_addresses} chevron />
|
<BlueListItem onPress={navigateToAddresses} title={loc.wallets.details_show_addresses} chevron />
|
||||||
)}
|
)}
|
||||||
|
{wallet.allowBIP47() && isBIP47Enabled && <BlueListItem onPress={navigateToPaymentCodes} title="Show payment codes" chevron />}
|
||||||
<BlueCard style={styles.address}>
|
<BlueCard style={styles.address}>
|
||||||
<View>
|
<View>
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
|
|
71
screen/wallets/paymentCodesList.tsx
Normal file
71
screen/wallets/paymentCodesList.tsx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { SectionList, StyleSheet, Text, View } from 'react-native';
|
||||||
|
import { NativeStackScreenProps } from 'react-native-screens/lib/typescript/native-stack';
|
||||||
|
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||||
|
import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electrum-wallet';
|
||||||
|
import { BlueCopyTextToClipboard } from '../../BlueComponents';
|
||||||
|
import loc from '../../loc';
|
||||||
|
|
||||||
|
type PaymentCodesListStackParamList = {
|
||||||
|
PaymentCodesList: { walletID: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DataSection {
|
||||||
|
title: string;
|
||||||
|
data: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PaymentCodesList({ route }: NativeStackScreenProps<PaymentCodesListStackParamList, 'PaymentCodesList'>) {
|
||||||
|
const { walletID } = route.params;
|
||||||
|
const { wallets } = useContext(BlueStorageContext);
|
||||||
|
const [data, setData] = useState<DataSection[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!walletID) return;
|
||||||
|
|
||||||
|
const foundWallet: AbstractHDElectrumWallet = wallets.find((w: AbstractHDElectrumWallet) => w.getID() === walletID);
|
||||||
|
if (!foundWallet) return;
|
||||||
|
|
||||||
|
const newData: DataSection[] = [
|
||||||
|
{
|
||||||
|
title: loc.bip47.who_can_pay_me,
|
||||||
|
data: foundWallet.getBIP47SenderPaymentCodes(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setData(newData);
|
||||||
|
}, [walletID, wallets]);
|
||||||
|
|
||||||
|
const shortenPC = (pc: string): string => {
|
||||||
|
return pc.substring(0, 8) + '...' + pc.substring(pc.length - 8);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{!walletID ? (
|
||||||
|
<Text>Internal error</Text>
|
||||||
|
) : (
|
||||||
|
<View>
|
||||||
|
<SectionList
|
||||||
|
sections={data}
|
||||||
|
keyExtractor={(item, index) => item + index}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<View>
|
||||||
|
<BlueCopyTextToClipboard truncated={shortenPC(item)} text={item} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
renderSectionHeader={({ section: { title } }) => <Text style={styles.titleText}>{title}</Text>}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
titleText: { fontSize: 20 },
|
||||||
|
});
|
|
@ -178,8 +178,12 @@ const WalletTransactions = ({ navigation }) => {
|
||||||
refreshLnNodeInfo();
|
refreshLnNodeInfo();
|
||||||
// await BlueElectrum.ping();
|
// await BlueElectrum.ping();
|
||||||
await BlueElectrum.waitTillConnected();
|
await BlueElectrum.waitTillConnected();
|
||||||
/** @type {LegacyWallet} */
|
if (wallet.allowBIP47()) {
|
||||||
await wallet.fetchBIP47SenderPaymentCodes(); // FIXME:
|
const pcStart = +new Date();
|
||||||
|
await wallet.fetchBIP47SenderPaymentCodes();
|
||||||
|
const pcEnd = +new Date();
|
||||||
|
console.log(wallet.getLabel(), 'fetch payment codes took', (pcEnd - pcStart) / 1000, 'sec');
|
||||||
|
}
|
||||||
const balanceStart = +new Date();
|
const balanceStart = +new Date();
|
||||||
const oldBalance = wallet.getBalance();
|
const oldBalance = wallet.getBalance();
|
||||||
await wallet.fetchBalance();
|
await wallet.fetchBalance();
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
// import assert from 'assert';
|
// import assert from 'assert';
|
||||||
|
import { ECPairFactory } from 'ecpair';
|
||||||
|
|
||||||
import { HDSegwitBech32Wallet } from '../../class';
|
import { HDLegacyP2PKHWallet, HDSegwitBech32Wallet } from '../../class';
|
||||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||||
|
import BIP47Factory from '@spsina/bip47';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
const bitcoin = require('bitcoinjs-lib');
|
||||||
|
const ecc = require('tiny-secp256k1');
|
||||||
|
const ECPair = ECPairFactory(ecc);
|
||||||
|
|
||||||
jest.setTimeout(30 * 1000);
|
jest.setTimeout(30 * 1000);
|
||||||
|
|
||||||
|
@ -18,32 +25,115 @@ beforeAll(async () => {
|
||||||
|
|
||||||
describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
||||||
it('should work', async () => {
|
it('should work', async () => {
|
||||||
if (!process.env.BIP47_HD_MNEMONIC) {
|
const hd = new HDLegacyP2PKHWallet();
|
||||||
console.error('process.env.BIP47_HD_MNEMONIC not set, skipped');
|
// @see https://gist.github.com/SamouraiDev/6aad669604c5930864bd
|
||||||
return;
|
hd.setSecret('reward upper indicate eight swift arch injury crystal super wrestle already dentist');
|
||||||
}
|
|
||||||
|
|
||||||
const hd = new HDSegwitBech32Wallet();
|
|
||||||
hd.gap_limit = 1;
|
|
||||||
hd.setSecret(process.env.BIP47_HD_MNEMONIC);
|
|
||||||
|
|
||||||
expect(hd.getBIP47PaymentCode()).toEqual(
|
expect(hd.getBIP47PaymentCode()).toEqual(
|
||||||
'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97',
|
'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(hd.allowBIP47()).toEqual(true);
|
||||||
|
|
||||||
await hd.fetchBIP47SenderPaymentCodes();
|
await hd.fetchBIP47SenderPaymentCodes();
|
||||||
expect(hd._sender_payment_codes.length).toBeGreaterThanOrEqual(3);
|
expect(hd.getBIP47SenderPaymentCodes().length).toBeGreaterThanOrEqual(3);
|
||||||
expect(hd._sender_payment_codes).toContain(
|
expect(hd.getBIP47SenderPaymentCodes()).toContain(
|
||||||
'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA',
|
'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA',
|
||||||
);
|
);
|
||||||
expect(hd._sender_payment_codes).toContain(
|
expect(hd.getBIP47SenderPaymentCodes()).toContain(
|
||||||
'PM8TJgndZSWCBPG5zCsqdXmCKLi7sP13jXuRp6b5X7G9geA3vRXQKAoXDf4Eym2RJB3vvcBdpDQT4vbo5QX7UfeV2ddjM8s79ERUTFS2ScKggSrciUsU',
|
'PM8TJgndZSWCBPG5zCsqdXmCKLi7sP13jXuRp6b5X7G9geA3vRXQKAoXDf4Eym2RJB3vvcBdpDQT4vbo5QX7UfeV2ddjM8s79ERUTFS2ScKggSrciUsU',
|
||||||
);
|
);
|
||||||
expect(hd._sender_payment_codes).toContain(
|
expect(hd.getBIP47SenderPaymentCodes()).toContain(
|
||||||
'PM8TJNiWKcyiA2MsWCfuAr9jvhA5qMEdEkjNypEnUbxMRa1D5ttQWdggQ7ib9VNFbRBSuw7i6RkqPSkCMR1XGPSikJHaCSfqWtsb1fn4WNAXjp5JVL5z',
|
'PM8TJNiWKcyiA2MsWCfuAr9jvhA5qMEdEkjNypEnUbxMRa1D5ttQWdggQ7ib9VNFbRBSuw7i6RkqPSkCMR1XGPSikJHaCSfqWtsb1fn4WNAXjp5JVL5z',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await hd.fetchBalance();
|
||||||
await hd.fetchTransactions();
|
await hd.fetchTransactions();
|
||||||
expect(hd.getTransactions().length).toBeGreaterThanOrEqual(4);
|
expect(hd.getTransactions().length).toBeGreaterThanOrEqual(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work (samurai)', 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');
|
||||||
|
|
||||||
|
expect(w.getBIP47PaymentCode()).toEqual(
|
||||||
|
'PM8TJXuZNUtSibuXKFM6bhCxpNaSye6r4px2GXRV5v86uRdH9Raa8ZtXEkG7S4zLREf4ierjMsxLXSFTbRVUnRmvjw9qnc7zZbyXyBstSmjcb7uVcDYF',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(w._getExternalAddressByIndex(0)).toEqual('bc1q07l355j4yd5kyut36vjxn2u60d3dknnpt39t6y');
|
||||||
|
|
||||||
|
const bip47 = BIP47Factory(ecc).fromBip39Seed(w.getSecret(), undefined, w.getPassphrase());
|
||||||
|
const ourNotificationAddress = bip47.getNotificationAddress();
|
||||||
|
|
||||||
|
const publicBip47 = BIP47Factory(ecc).fromPaymentCode(w.getBIP47PaymentCode());
|
||||||
|
expect(ourNotificationAddress).toEqual(publicBip47.getNotificationAddress());
|
||||||
|
|
||||||
|
expect(ourNotificationAddress).toEqual('1EiP2kSqxNqRhn8MPMkrtSEqaWiCWLYyTS'); // our notif address
|
||||||
|
|
||||||
|
await w.fetchBIP47SenderPaymentCodes();
|
||||||
|
assert.ok(
|
||||||
|
w
|
||||||
|
.getBIP47SenderPaymentCodes()
|
||||||
|
.includes('PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo'),
|
||||||
|
); // sparrow payment code
|
||||||
|
|
||||||
|
assert.ok(w.weOwnAddress('bc1q57nwf9vfq2qsl80q37wq5h0tjytsk95vgjq4fe')); // this is an address that was derived (and paid) from counterparty payment code
|
||||||
|
|
||||||
|
const keyPair2 = ECPair.fromWIF(w._getWIFbyAddress('bc1q57nwf9vfq2qsl80q37wq5h0tjytsk95vgjq4fe') || '');
|
||||||
|
const address = bitcoin.payments.p2wpkh({
|
||||||
|
pubkey: keyPair2.publicKey,
|
||||||
|
}).address;
|
||||||
|
assert.strictEqual(address, 'bc1q57nwf9vfq2qsl80q37wq5h0tjytsk95vgjq4fe');
|
||||||
|
|
||||||
|
await w.fetchTransactions();
|
||||||
|
|
||||||
|
assert.ok(w.getTransactions().length >= 3);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
w.getTransactions().find(tx => tx.txid === '64058a49bb75481fc0bebbb0d84a4aceebe319f9d32929e73cefb21d83342e9f')?.value,
|
||||||
|
100000,
|
||||||
|
); // initial deposit from sparrow after sparrow made a notification tx
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
w.getTransactions().find(tx => tx.txid === '06b4c14587182fd0474f265a77b156519b4778769a99c21623863a8194d0fa4f')?.value,
|
||||||
|
-22692,
|
||||||
|
); // notification tx to sparrow so we can pay sparrow
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
w.getTransactions().find(tx => tx.txid === '73a2ac70858c5b306b101a861d582f40c456a692096a4e4805aa739258c4400d')?.value,
|
||||||
|
-77308,
|
||||||
|
); // paying to sparrow
|
||||||
|
|
||||||
|
// now, constructing OP_RETURN data to notify sparrow about us
|
||||||
|
|
||||||
|
const aliceBip47 = bip47;
|
||||||
|
const keyPair = ECPair.fromWIF(w._getWIFbyAddress('bc1q57nwf9vfq2qsl80q37wq5h0tjytsk95vgjq4fe') || '');
|
||||||
|
const bobBip47 = BIP47Factory(ecc).fromPaymentCode(
|
||||||
|
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||||
|
);
|
||||||
|
const blindedPaymentCode = aliceBip47.getBlindedPaymentCode(
|
||||||
|
bobBip47,
|
||||||
|
keyPair.privateKey as Buffer,
|
||||||
|
// txid is reversed, as well as output number ()
|
||||||
|
Buffer.from('64058a49bb75481fc0bebbb0d84a4aceebe319f9d32929e73cefb21d83342e9f', 'hex').reverse().toString('hex') + '01000000',
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
blindedPaymentCode,
|
||||||
|
'0100039da7642943ec5d16c9bce09b71f240fe246d891fa3b52a7d236fece98318e1ae972f3747672f7e79a23fc88c4dc91a8d014233e14a9e4417e132405b6a6c166d00000000000000000000000000',
|
||||||
|
);
|
||||||
|
|
||||||
|
// checking that this is exactly a data payload we have in an actual notification transaction we have sent:
|
||||||
|
assert.strictEqual(
|
||||||
|
w.getTransactions().find(tx => tx.txid === '06b4c14587182fd0474f265a77b156519b4778769a99c21623863a8194d0fa4f')?.outputs?.[0]
|
||||||
|
?.scriptPubKey.hex,
|
||||||
|
'6a4c50' + blindedPaymentCode,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,8 +5,9 @@ import mockClipboard from '@react-native-clipboard/clipboard/jest/clipboard-mock
|
||||||
const consoleWarnOrig = console.warn;
|
const consoleWarnOrig = console.warn;
|
||||||
console.warn = (...args) => {
|
console.warn = (...args) => {
|
||||||
if (
|
if (
|
||||||
args[0]?.startsWith('WARNING: Sending to a future segwit version address can lead to loss of funds') ||
|
typeof args[0] === 'string' &&
|
||||||
args[0]?.startsWith('only compressed public keys are good')
|
(args[0]?.startsWith('WARNING: Sending to a future segwit version address can lead to loss of funds') ||
|
||||||
|
args[0]?.startsWith('only compressed public keys are good'))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -16,10 +17,11 @@ console.warn = (...args) => {
|
||||||
const consoleLogOrig = console.log;
|
const consoleLogOrig = console.log;
|
||||||
console.log = (...args) => {
|
console.log = (...args) => {
|
||||||
if (
|
if (
|
||||||
args[0]?.startsWith('updating exchange rate') ||
|
typeof args[0] === 'string' &&
|
||||||
|
(args[0]?.startsWith('updating exchange rate') ||
|
||||||
args[0]?.startsWith('begin connection') ||
|
args[0]?.startsWith('begin connection') ||
|
||||||
args[0]?.startsWith('TLS Connected to') ||
|
args[0]?.startsWith('TLS Connected to') ||
|
||||||
args[0]?.startsWith('connected to')
|
args[0]?.startsWith('connected to'))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
96
tests/unit/bip47.test.ts
Normal file
96
tests/unit/bip47.test.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import BIP47Factory from '@spsina/bip47';
|
||||||
|
import ecc from 'tiny-secp256k1';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import { HDSegwitBech32Wallet, WatchOnlyWallet } from '../../class';
|
||||||
|
|
||||||
|
describe('Bech32 Segwit HD (BIP84) with BIP47', () => {
|
||||||
|
it('should work', async () => {
|
||||||
|
const bobWallet = new HDSegwitBech32Wallet();
|
||||||
|
// @see https://gist.github.com/SamouraiDev/6aad669604c5930864bd
|
||||||
|
bobWallet.setSecret('reward upper indicate eight swift arch injury crystal super wrestle already dentist');
|
||||||
|
|
||||||
|
expect(bobWallet.getBIP47PaymentCode()).toEqual(
|
||||||
|
'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97',
|
||||||
|
);
|
||||||
|
|
||||||
|
const bip47 = BIP47Factory(ecc).fromBip39Seed(bobWallet.getSecret(), undefined, '');
|
||||||
|
const bobNotificationAddress = bip47.getNotificationAddress();
|
||||||
|
|
||||||
|
expect(bobNotificationAddress).toEqual('1ChvUUvht2hUQufHBXF8NgLhW8SwE2ecGV'); // our notif address
|
||||||
|
|
||||||
|
assert.ok(!bobWallet.weOwnAddress('1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW')); // alice notif address, we dont own it
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getters, setters, flags work', async () => {
|
||||||
|
const w = new HDSegwitBech32Wallet();
|
||||||
|
await w.generate();
|
||||||
|
|
||||||
|
expect(w.allowBIP47()).toEqual(true);
|
||||||
|
|
||||||
|
expect(w.isBIP47Enabled()).toEqual(false);
|
||||||
|
w.switchBIP47(true);
|
||||||
|
expect(w.isBIP47Enabled()).toEqual(true);
|
||||||
|
w.switchBIP47(false);
|
||||||
|
expect(w.isBIP47Enabled()).toEqual(false);
|
||||||
|
|
||||||
|
// checking that derived watch-only does not support that:
|
||||||
|
const ww = new WatchOnlyWallet();
|
||||||
|
ww.setSecret(w.getXpub());
|
||||||
|
expect(ww.allowBIP47()).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work (samurai)', 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');
|
||||||
|
|
||||||
|
expect(w.getBIP47PaymentCode()).toEqual(
|
||||||
|
'PM8TJXuZNUtSibuXKFM6bhCxpNaSye6r4px2GXRV5v86uRdH9Raa8ZtXEkG7S4zLREf4ierjMsxLXSFTbRVUnRmvjw9qnc7zZbyXyBstSmjcb7uVcDYF',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(w._getExternalAddressByIndex(0)).toEqual('bc1q07l355j4yd5kyut36vjxn2u60d3dknnpt39t6y');
|
||||||
|
|
||||||
|
const bip47 = BIP47Factory(ecc).fromBip39Seed(w.getSecret(), undefined, w.getPassphrase());
|
||||||
|
const ourNotificationAddress = bip47.getNotificationAddress();
|
||||||
|
|
||||||
|
const publicBip47 = BIP47Factory(ecc).fromPaymentCode(w.getBIP47PaymentCode());
|
||||||
|
expect(ourNotificationAddress).toEqual(publicBip47.getNotificationAddress());
|
||||||
|
|
||||||
|
expect(ourNotificationAddress).toEqual('1EiP2kSqxNqRhn8MPMkrtSEqaWiCWLYyTS'); // our notif address
|
||||||
|
|
||||||
|
assert.ok(!w.weOwnAddress('1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW')); // alice notif address, we dont own it
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work (sparrow)', 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(':')[1]);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
w.getXpub(),
|
||||||
|
'zpub6r4KaQRsLuhHSGx8b9wGHh18UnawBs49jtiDzZYh9DSgKGwD72jWR3v54fkyy1UKVxt9HvCkYHmMAUe2YjKefofWzYp9YD62sUp6nNsEDMs',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(w.getBIP47PaymentCode()).toEqual(
|
||||||
|
'PM8TJi1RuCrgSHTzGMoayUf8xUW6zYBGXBPSWwTiMhMMwqto7G6NA4z9pN5Kn8Pbhryo2eaHMFRRcidCGdB3VCDXJD4DdPD2ZyG3ScLMEvtStAetvPMo',
|
||||||
|
);
|
||||||
|
|
||||||
|
const bip47 = BIP47Factory(ecc).fromBip39Seed(w.getSecret(), undefined, w.getPassphrase());
|
||||||
|
const ourNotificationAddress = bip47.getNotificationAddress();
|
||||||
|
|
||||||
|
const publicBip47 = BIP47Factory(ecc).fromPaymentCode(w.getBIP47PaymentCode());
|
||||||
|
expect(ourNotificationAddress).toEqual(publicBip47.getNotificationAddress());
|
||||||
|
|
||||||
|
expect(ourNotificationAddress).toEqual('16xPugarxLzuNdhDu6XCMJBsMYrTN2fghN'); // our notif address
|
||||||
|
});
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue