FIX: legacy wallet can now derive UTXO from transactions in case if fetching listUnspent from network is impossible (closes #2528)

This commit is contained in:
Overtorment 2021-01-25 15:29:12 +00:00
parent d6812a367c
commit a3d7f75ea7
2 changed files with 76 additions and 5 deletions

View file

@ -1,6 +1,7 @@
import { randomBytes } from '../rng';
import { AbstractWallet } from './abstract-wallet';
import { HDSegwitBech32Wallet } from '..';
import BigNumber from 'bignumber.js';
const bitcoin = require('bitcoinjs-lib');
const BlueElectrum = require('../../blue_modules/BlueElectrum');
const coinSelectAccumulative = require('coinselect/accumulative');
@ -161,12 +162,72 @@ export class LegacyWallet extends AbstractWallet {
if (!u.confirmations && u.height) u.confirmations = BlueElectrum.estimateCurrentBlockheight() - u.height;
ret.push(u);
}
if (ret.length === 0) {
ret = this.getDerivedUtxoFromOurTransaction(); // oy vey, no stored utxo. lets attempt to derive it from stored transactions
}
if (!respectFrozen) {
ret = ret.filter(({ txid, vout }) => !this.getUTXOMetadata(txid, vout).frozen);
}
return ret;
}
getDerivedUtxoFromOurTransaction(returnSpentUtxoAsWell = false) {
const utxos = [];
const ownedAddressesHashmap = {};
ownedAddressesHashmap[this.getAddress()] = true;
/**
* below copypasted from
* @see AbstractHDElectrumWallet.getDerivedUtxoFromOurTransaction
*/
for (const tx of this.getTransactions()) {
for (const output of tx.outputs) {
let address = false;
if (output.scriptPubKey && output.scriptPubKey.addresses && output.scriptPubKey.addresses[0]) {
address = output.scriptPubKey.addresses[0];
}
if (ownedAddressesHashmap[address]) {
const value = new BigNumber(output.value).multipliedBy(100000000).toNumber();
utxos.push({
txid: tx.txid,
txId: tx.txid,
vout: output.n,
address,
value,
amount: value,
confirmations: tx.confirmations,
wif: false,
height: BlueElectrum.estimateCurrentBlockheight() - tx.confirmations,
});
}
}
}
if (returnSpentUtxoAsWell) return utxos;
// got all utxos we ever had. lets filter out the ones that are spent:
const ret = [];
for (const utxo of utxos) {
let spent = false;
for (const tx of this.getTransactions()) {
for (const input of tx.inputs) {
if (input.txid === utxo.txid && input.vout === utxo.vout) spent = true;
// utxo we got previously was actually spent right here ^^
}
}
if (!spent) {
ret.push(utxo);
}
}
return ret;
}
/**
* Fetches transactions via Electrum. Returns VOID.
* Use getter to get the actual list. *

View file

@ -54,11 +54,11 @@ describe('LegacyWallet', function () {
assert.ok(w._lastBalanceFetch > 0);
});
it('can fetch TXs', async () => {
it('can fetch TXs and derive UTXO from them', async () => {
const w = new LegacyWallet();
w._address = '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG';
w._address = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
await w.fetchTransactions();
assert.strictEqual(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 1);
for (const tx of w.getTransactions()) {
assert.ok(tx.hash);
@ -67,9 +67,19 @@ describe('LegacyWallet', function () {
assert.ok(tx.confirmations > 1);
}
assert.ok(w.weOwnTransaction('4924f3a29acdee007ebcf6084d2c9e1752c4eb7f26f7d1a06ef808780bf5fe6d'));
assert.ok(w.weOwnTransaction('d0432027a86119c63a0be8fa453275c2333b59067f1e559389cd3e0e377c8b96'));
assert.ok(w.weOwnTransaction('b2ac59bc282083498d1e87805d89bef9d3f3bc216c1d2c4dfaa2e2911b547100'));
assert.ok(!w.weOwnTransaction('825c12f277d1f84911ac15ad1f41a3de28e9d906868a930b0a7bca61b17c8881'));
assert.strictEqual(w.getUtxo().length, 1);
for (const tx of w.getUtxo()) {
assert.strictEqual(tx.txid, 'b2ac59bc282083498d1e87805d89bef9d3f3bc216c1d2c4dfaa2e2911b547100');
assert.strictEqual(tx.vout, 0);
assert.strictEqual(tx.address, '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK');
assert.strictEqual(tx.value, 51432);
assert.strictEqual(tx.value, tx.amount);
assert.ok(tx.confirmations > 0);
}
});
it.each([