feat: working balance

This commit is contained in:
abhishandy 2022-12-12 22:09:04 -05:00
parent ffed650961
commit fd61583db5
6 changed files with 76 additions and 83 deletions

View file

@ -682,6 +682,7 @@ export class AppStorage {
} }
} else { } else {
for (const wallet of this.wallets) { for (const wallet of this.wallets) {
console.log('fetching balance for', wallet.getLabel());
await wallet.fetchBalance(); await wallet.fetchBalance();
} }
} }
@ -728,10 +729,18 @@ export class AppStorage {
fetchSenderPaymentCodes = async index => { fetchSenderPaymentCodes = async index => {
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) {
this.wallets[index].fetchBIP47SenderPaymentCodes(); try {
await this.wallets[index].fetchBIP47SenderPaymentCodes();
} catch (error) {
console.error('Failed to fetch sender payment codes for wallet', index, error);
}
} else { } else {
for (const wallet of this.wallets) { for (const wallet of this.wallets) {
wallet.fetchBIP47SenderPaymentCodes(); try {
await wallet.fetchBIP47SenderPaymentCodes();
} catch (error) {
console.error('Failed to fetch sender payment codes for wallet', wallet.label, error);
}
} }
} }
}; };

View file

@ -297,7 +297,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
// next, bip47 addresses // next, bip47 addresses
for (const pc of this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) { for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
let hasUnconfirmed = false; let hasUnconfirmed = false;
this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {}; 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] || []; this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
@ -357,7 +357,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
this._txs_by_internal_index[c] = this._txs_by_internal_index[c].filter(tx => !!tx.confirmations); this._txs_by_internal_index[c] = this._txs_by_internal_index[c].filter(tx => !!tx.confirmations);
} }
for (const pc of this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) { for (let c = 0; c < this._getNextFreePaymentCodeAddress(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); this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c].filter(tx => !!tx.confirmations);
} }
} }
@ -447,7 +447,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
} }
for (const pc of this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) { for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
for (const tx of Object.values(txdatas)) { for (const tx of Object.values(txdatas)) {
for (const vin of tx.vin) { for (const vin of tx.vin) {
if (vin.addresses && vin.addresses.indexOf(this._getBIP47Address(pc, c)) !== -1) { if (vin.addresses && vin.addresses.indexOf(this._getBIP47Address(pc, c)) !== -1) {
@ -607,7 +607,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
) { ) {
const address = this._getInternalAddressByIndex(c); const address = this._getInternalAddressByIndex(c);
if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) { if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unsued lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unused
} }
} }
} }
@ -650,7 +650,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
) { ) {
const address = this._getExternalAddressByIndex(c); const address = this._getExternalAddressByIndex(c);
if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) { if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unsued lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unused
} }
} }
} }
@ -693,7 +693,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
) { ) {
const address = this._getBIP47Address(paymentCode, c); const address = this._getBIP47Address(paymentCode, c);
if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) { if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unsued lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unused
} }
} }
} }
@ -710,6 +710,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
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, 10);
// console.log('new index after binary search', this.next_free_payment_code_address_index[pc]);
} }
} }
} // end rescanning fresh wallet } // end rescanning fresh wallet
@ -733,14 +734,22 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (let c = this.next_free_change_address_index; c < this.next_free_change_address_index + this.gap_limit; c++) { for (let c = this.next_free_change_address_index; c < this.next_free_change_address_index + this.gap_limit; c++) {
lagAddressesToFetch.push(this._getInternalAddressByIndex(c)); lagAddressesToFetch.push(this._getInternalAddressByIndex(c));
} }
for (const pc in this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) { for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
// console.log(pc, c);
lagAddressesToFetch.push(this._getBIP47Address(pc, c)); lagAddressesToFetch.push(this._getBIP47Address(pc, c));
} }
} }
// console.log('lagAddressesToFetch', lagAddressesToFetch.length);
const txs = await BlueElectrum.multiGetHistoryByAddress(lagAddressesToFetch); // <------ electrum call const txs = await BlueElectrum.multiGetHistoryByAddress(lagAddressesToFetch); // <------ electrum call
console.log(
'txs',
Object.values(txs).filter(item => item.length > 0),
);
for (let c = this.next_free_address_index; c < this.next_free_address_index + this.gap_limit; c++) { for (let c = this.next_free_address_index; c < this.next_free_address_index + this.gap_limit; c++) {
const address = this._getExternalAddressByIndex(c); const address = this._getExternalAddressByIndex(c);
if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) { if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
@ -757,12 +766,14 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
} }
} }
for (const pc in this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) { for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
const address = this._getBIP47Address(pc, c); const address = this._getBIP47Address(pc, c);
if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) { if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
// whoa, someone uses our wallet outside! better catch up // whoa, someone uses our wallet outside! better catch up
// console.log('catching up with bip47 address', address, c);
this.next_free_payment_code_address_index[pc] = c + 1; this.next_free_payment_code_address_index[pc] = c + 1;
// console.log('new index', this.next_free_payment_code_address_index[pc]);
} }
} }
} }
@ -785,14 +796,21 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
addresses2fetch.push(this._getInternalAddressByIndex(c)); addresses2fetch.push(this._getInternalAddressByIndex(c));
} }
for (const pc in this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) { for (let c = 0; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
addresses2fetch.push(this._getBIP47Address(pc, c)); addresses2fetch.push(this._getBIP47Address(pc, c));
} }
} }
// console.log('fetching balance for', addresses2fetch.length, 'addresses');
const balances = await BlueElectrum.multiGetBalanceByAddress(addresses2fetch); const balances = await BlueElectrum.multiGetBalanceByAddress(addresses2fetch);
console.log(
'balances',
Object.values(balances.addresses).filter(item => item.confirmed > 0 || item.unconfirmed > 0),
);
// converting to a more compact internal format // converting to a more compact internal format
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) { for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
const addr = this._getExternalAddressByIndex(c); const addr = this._getExternalAddressByIndex(c);
@ -836,26 +854,20 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
} }
for (const pc of this._sender_payment_codes) { for (const pc of this._sender_payment_codes) {
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) { let confirmed = 0;
let unconfirmed = 0;
for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + this.gap_limit; c++) {
const addr = this._getBIP47Address(pc, c); const addr = this._getBIP47Address(pc, c);
if (balances.addresses[addr]) { if (balances.addresses[addr].confirmed || balances.addresses[addr].unconfirmed) {
// first, if balances differ from what we store - we delete transactions for that confirmed = confirmed + balances.addresses[addr].confirmed;
// address so next fetchTransactions() will refetch everything unconfirmed = unconfirmed + balances.addresses[addr].unconfirmed;
if (this._getBalancesByPaymentCodeIndex(pc).c) { // console.log(this._balances_by_payment_code_index);
if (
this._getBalancesByPaymentCodeIndex(pc).c !== balances.addresses[addr].confirmed ||
this._getBalancesByPaymentCodeIndex(pc).u !== balances.addresses[addr].unconfirmed
) {
delete this._txs_by_payment_code_index[pc];
}
}
// update local representation of balances on that address:
this._balances_by_payment_code_index[pc] = {
c: balances.addresses[addr].confirmed,
u: balances.addresses[addr].unconfirmed,
};
} }
} }
this._balances_by_payment_code_index[pc] = {
c: confirmed,
u: unconfirmed,
};
} }
this._lastBalanceFetch = +new Date(); this._lastBalanceFetch = +new Date();
@ -1450,20 +1462,24 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
return this._payment_code; return this._payment_code;
} }
getBIP47NotificationAddress(): string { async fetchBIP47SenderPaymentCodes(): Promise<void> {
return this.getBIP47FromSeed().getNotificationAddress(); const bip47_instance = BIP47Factory(ecc).fromBip39Seed(this.secret, undefined, this.passphrase);
}
async fetchBIP47SenderPaymentCodes() { if (!bip47_instance.getNotificationAddress) {
const bip47 = this.getBIP47FromSeed(); console.error('bip47.getNotificationAddress is not a function');
const address = bip47.getNotificationAddress(); return;
}
const address = bip47_instance.getNotificationAddress();
const histories = await BlueElectrum.multiGetHistoryByAddress([address]); const histories = await BlueElectrum.multiGetHistoryByAddress([address]);
const txHashes = histories[address].map(({ tx_hash }) => tx_hash); const txHashes = histories[address].map(({ tx_hash }) => tx_hash);
const txHexs = await BlueElectrum.multiGetTransactionHexByTxid(txHashes); const txHexs = await BlueElectrum.multiGetTransactionHexByTxid(txHashes);
this._sender_payment_codes = Object.values(txHexs).map(str => { this._sender_payment_codes = Object.values(txHexs).map(str => {
return bip47.getPaymentCodeFromRawNotificationTransaction(str); this.next_free_payment_code_address_index[str] = 0; // initialize
this._balances_by_payment_code_index[str] = { c: 0, u: 0 };
return bip47_instance.getPaymentCodeFromRawNotificationTransaction(str);
}); });
} }
@ -1471,19 +1487,21 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
return this._sender_payment_codes; return this._sender_payment_codes;
} }
_getBIP47Address(paymentCode: string, index: number) { _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] = [];
if (this.addresses_by_payment_code[paymentCode][index]) { if (this.addresses_by_payment_code[paymentCode][index]) {
return this.addresses_by_payment_code[paymentCode][index]; return this.addresses_by_payment_code[paymentCode][index];
} }
const bip47_instance = this.getBIP47FromSeed(); const bip47 = BIP47Factory(ecc);
const bip47_instance = bip47.fromBip39Seed(this.secret, undefined, this.passphrase);
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 = bip47_instance.getAddressFromNode(hdNode, bip47_instance.network);
this.addresses_by_payment_code[paymentCode][index] = address; this.addresses_by_payment_code[paymentCode][index] = address;
console.log('generated address', address);
return address; return address;
} }

View file

@ -38,7 +38,7 @@ export class AbstractHDWallet extends LegacyWallet {
this._xpub = ''; // cache this._xpub = ''; // cache
this.usedAddresses = []; this.usedAddresses = [];
this._address_to_wif_cache = {}; this._address_to_wif_cache = {};
this.gap_limit = 20; this.gap_limit = 5;
this._derivationPath = Constructor.derivationPath; this._derivationPath = Constructor.derivationPath;
} }

50
package-lock.json generated
View file

@ -21,7 +21,7 @@
"@react-navigation/drawer": "5.12.9", "@react-navigation/drawer": "5.12.9",
"@react-navigation/native": "5.9.8", "@react-navigation/native": "5.9.8",
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image", "@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
"@spsina/bip47": "^1.0.1", "@spsina/bip47": "file:../bip47/spsina-bip47-1.0.1.tgz",
"aez": "1.0.1", "aez": "1.0.1",
"assert": "2.0.0", "assert": "2.0.0",
"base-x": "3.0.9", "base-x": "3.0.9",
@ -5778,8 +5778,9 @@
}, },
"node_modules/@spsina/bip47": { "node_modules/@spsina/bip47": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@spsina/bip47/-/bip47-1.0.1.tgz", "resolved": "file:../bip47/spsina-bip47-1.0.1.tgz",
"integrity": "sha512-ZCjDXm9WcVLz1wgBBRPxg0NTg3RvgxmWcAQhsZSYFgp7PLpa7pK/dHfxNUhnBNbaCwRFiyfHnNYHLV0OFTizkw==", "integrity": "sha512-lsgEpiEMDgpiYOA2kizOwiSS3vjTeLe2VnkOTIGnJ7Eu7Mkgl9dLES7oSLAjY64aQXr0VolqCRciRDc2nAC++w==",
"license": "MIT",
"dependencies": { "dependencies": {
"bip32": "^3.0.1", "bip32": "^3.0.1",
"bip39": "^3.0.4", "bip39": "^3.0.4",
@ -5787,23 +5788,12 @@
"bs58check": "^2.1.1", "bs58check": "^2.1.1",
"create-hmac": "^1.1.7", "create-hmac": "^1.1.7",
"ecpair": "^2.0.1", "ecpair": "^2.0.1",
"tiny-secp256k1": "^2.2.1" "tiny-secp256k1": "^1.1.6"
}, },
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@spsina/bip47/node_modules/tiny-secp256k1": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.1.tgz",
"integrity": "sha512-/U4xfVqnVxJXN4YVsru0E6t5wVncu2uunB8+RVR40fYUxkKYUPS10f+ePQZgFBoE/Jbf9H1NBveupF2VmB58Ng==",
"dependencies": {
"uint8array-tools": "0.0.7"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tootallnate/once": { "node_modules/@tootallnate/once": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -26312,14 +26302,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/uint8array-tools": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
"integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/ultron": { "node_modules/ultron": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
@ -31344,9 +31326,8 @@
} }
}, },
"@spsina/bip47": { "@spsina/bip47": {
"version": "1.0.1", "version": "file:../bip47/spsina-bip47-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/@spsina/bip47/-/bip47-1.0.1.tgz", "integrity": "sha512-lsgEpiEMDgpiYOA2kizOwiSS3vjTeLe2VnkOTIGnJ7Eu7Mkgl9dLES7oSLAjY64aQXr0VolqCRciRDc2nAC++w==",
"integrity": "sha512-ZCjDXm9WcVLz1wgBBRPxg0NTg3RvgxmWcAQhsZSYFgp7PLpa7pK/dHfxNUhnBNbaCwRFiyfHnNYHLV0OFTizkw==",
"requires": { "requires": {
"bip32": "^3.0.1", "bip32": "^3.0.1",
"bip39": "^3.0.4", "bip39": "^3.0.4",
@ -31354,17 +31335,7 @@
"bs58check": "^2.1.1", "bs58check": "^2.1.1",
"create-hmac": "^1.1.7", "create-hmac": "^1.1.7",
"ecpair": "^2.0.1", "ecpair": "^2.0.1",
"tiny-secp256k1": "^2.2.1" "tiny-secp256k1": "^1.1.6"
},
"dependencies": {
"tiny-secp256k1": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.1.tgz",
"integrity": "sha512-/U4xfVqnVxJXN4YVsru0E6t5wVncu2uunB8+RVR40fYUxkKYUPS10f+ePQZgFBoE/Jbf9H1NBveupF2VmB58Ng==",
"requires": {
"uint8array-tools": "0.0.7"
}
}
} }
}, },
"@tootallnate/once": { "@tootallnate/once": {
@ -47383,11 +47354,6 @@
} }
} }
}, },
"uint8array-tools": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
"integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ=="
},
"ultron": { "ultron": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",

View file

@ -107,7 +107,7 @@
"@react-navigation/drawer": "5.12.9", "@react-navigation/drawer": "5.12.9",
"@react-navigation/native": "5.9.8", "@react-navigation/native": "5.9.8",
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image", "@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
"@spsina/bip47": "^1.0.1", "@spsina/bip47": "file:../bip47/spsina-bip47-1.0.1.tgz",
"aez": "1.0.1", "aez": "1.0.1",
"assert": "2.0.0", "assert": "2.0.0",
"base-x": "3.0.9", "base-x": "3.0.9",

View file

@ -179,7 +179,7 @@ const WalletTransactions = ({ navigation }) => {
// await BlueElectrum.ping(); // await BlueElectrum.ping();
await BlueElectrum.waitTillConnected(); await BlueElectrum.waitTillConnected();
/** @type {LegacyWallet} */ /** @type {LegacyWallet} */
await wallet.fetchBIP47SenderPaymentCodes(); await wallet.fetchBIP47SenderPaymentCodes(); // FIXME:
const balanceStart = +new Date(); const balanceStart = +new Date();
const oldBalance = wallet.getBalance(); const oldBalance = wallet.getBalance();
await wallet.fetchBalance(); await wallet.fetchBalance();