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 {
for (const wallet of this.wallets) {
console.log('fetching balance for', wallet.getLabel());
await wallet.fetchBalance();
}
}
@ -728,10 +729,18 @@ export class AppStorage {
fetchSenderPaymentCodes = async index => {
console.log('fetchSenderPaymentCodes for wallet#', typeof index === 'undefined' ? '(all)' : index);
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 {
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
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;
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] || [];
@ -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);
}
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);
}
}
@ -447,7 +447,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
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 vin of tx.vin) {
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);
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);
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);
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) {
for (const pc of this._sender_payment_codes) {
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
@ -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++) {
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++) {
// console.log(pc, c);
lagAddressesToFetch.push(this._getBIP47Address(pc, c));
}
}
// console.log('lagAddressesToFetch', lagAddressesToFetch.length);
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++) {
const address = this._getExternalAddressByIndex(c);
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++) {
const address = this._getBIP47Address(pc, c);
if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
// 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;
// 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));
}
for (const pc in 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 (const pc of this._sender_payment_codes) {
for (let c = 0; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
addresses2fetch.push(this._getBIP47Address(pc, c));
}
}
// console.log('fetching balance for', addresses2fetch.length, 'addresses');
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
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
const addr = this._getExternalAddressByIndex(c);
@ -836,27 +854,21 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
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);
if (balances.addresses[addr]) {
// first, if balances differ from what we store - we delete transactions for that
// address so next fetchTransactions() will refetch everything
if (this._getBalancesByPaymentCodeIndex(pc).c) {
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];
if (balances.addresses[addr].confirmed || balances.addresses[addr].unconfirmed) {
confirmed = confirmed + balances.addresses[addr].confirmed;
unconfirmed = unconfirmed + balances.addresses[addr].unconfirmed;
// console.log(this._balances_by_payment_code_index);
}
}
// 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,
c: confirmed,
u: unconfirmed,
};
}
}
}
this._lastBalanceFetch = +new Date();
}
@ -1450,20 +1462,24 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
return this._payment_code;
}
getBIP47NotificationAddress(): string {
return this.getBIP47FromSeed().getNotificationAddress();
async fetchBIP47SenderPaymentCodes(): Promise<void> {
const bip47_instance = BIP47Factory(ecc).fromBip39Seed(this.secret, undefined, this.passphrase);
if (!bip47_instance.getNotificationAddress) {
console.error('bip47.getNotificationAddress is not a function');
return;
}
async fetchBIP47SenderPaymentCodes() {
const bip47 = this.getBIP47FromSeed();
const address = bip47.getNotificationAddress();
const address = bip47_instance.getNotificationAddress();
const histories = await BlueElectrum.multiGetHistoryByAddress([address]);
const txHashes = histories[address].map(({ tx_hash }) => tx_hash);
const txHexs = await BlueElectrum.multiGetTransactionHexByTxid(txHashes);
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;
}
_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][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 remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
const address = bip47_instance.getAddressFromNode(hdNode, bip47_instance.network);
this.addresses_by_payment_code[paymentCode][index] = address;
console.log('generated address', address);
return address;
}

View file

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

50
package-lock.json generated
View file

@ -21,7 +21,7 @@
"@react-navigation/drawer": "5.12.9",
"@react-navigation/native": "5.9.8",
"@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",
"assert": "2.0.0",
"base-x": "3.0.9",
@ -5778,8 +5778,9 @@
},
"node_modules/@spsina/bip47": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@spsina/bip47/-/bip47-1.0.1.tgz",
"integrity": "sha512-ZCjDXm9WcVLz1wgBBRPxg0NTg3RvgxmWcAQhsZSYFgp7PLpa7pK/dHfxNUhnBNbaCwRFiyfHnNYHLV0OFTizkw==",
"resolved": "file:../bip47/spsina-bip47-1.0.1.tgz",
"integrity": "sha512-lsgEpiEMDgpiYOA2kizOwiSS3vjTeLe2VnkOTIGnJ7Eu7Mkgl9dLES7oSLAjY64aQXr0VolqCRciRDc2nAC++w==",
"license": "MIT",
"dependencies": {
"bip32": "^3.0.1",
"bip39": "^3.0.4",
@ -5787,23 +5788,12 @@
"bs58check": "^2.1.1",
"create-hmac": "^1.1.7",
"ecpair": "^2.0.1",
"tiny-secp256k1": "^2.2.1"
"tiny-secp256k1": "^1.1.6"
},
"engines": {
"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": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -26312,14 +26302,6 @@
"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": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
@ -31344,9 +31326,8 @@
}
},
"@spsina/bip47": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@spsina/bip47/-/bip47-1.0.1.tgz",
"integrity": "sha512-ZCjDXm9WcVLz1wgBBRPxg0NTg3RvgxmWcAQhsZSYFgp7PLpa7pK/dHfxNUhnBNbaCwRFiyfHnNYHLV0OFTizkw==",
"version": "file:../bip47/spsina-bip47-1.0.1.tgz",
"integrity": "sha512-lsgEpiEMDgpiYOA2kizOwiSS3vjTeLe2VnkOTIGnJ7Eu7Mkgl9dLES7oSLAjY64aQXr0VolqCRciRDc2nAC++w==",
"requires": {
"bip32": "^3.0.1",
"bip39": "^3.0.4",
@ -31354,17 +31335,7 @@
"bs58check": "^2.1.1",
"create-hmac": "^1.1.7",
"ecpair": "^2.0.1",
"tiny-secp256k1": "^2.2.1"
},
"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"
}
}
"tiny-secp256k1": "^1.1.6"
}
},
"@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": {
"version": "1.0.2",
"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/native": "5.9.8",
"@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",
"assert": "2.0.0",
"base-x": "3.0.9",

View file

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