|
|
@ -6,13 +6,9 @@ |
|
|
|
|
|
|
|
const _ = require('lodash') |
|
|
|
const LRU = require('lru-cache') |
|
|
|
const bitcoin = require('bitcoinjs-lib') |
|
|
|
const util = require('../lib/util') |
|
|
|
const db = require('../lib/db/mysql-db-wrapper') |
|
|
|
const addrHelper = require('../lib/bitcoin/addresses-helper') |
|
|
|
const network = require('../lib/bitcoin/network') |
|
|
|
const keys = require('../keys')[network.key] |
|
|
|
const activeNet = network.network |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
@ -64,51 +60,47 @@ class TransactionsBundle { |
|
|
|
|
|
|
|
/** |
|
|
|
* Find the transactions of interest |
|
|
|
* based on theirs inputs |
|
|
|
* @returns {object[]} returns an array of transactions objects |
|
|
|
*/ |
|
|
|
async prefilterTransactions() { |
|
|
|
async prefilterByInputs() { |
|
|
|
// Process transactions by slices of 5000 transactions
|
|
|
|
const MAX_NB_TXS = 5000 |
|
|
|
const lists = util.splitList(this.transactions, MAX_NB_TXS) |
|
|
|
const results = await util.seriesCall(lists, txs => this._prefilterTransactions(txs)) |
|
|
|
const results = await util.parallelCall(lists, txs => this._prefilterByInputs(txs)) |
|
|
|
return _.flatten(results) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Find the transactions of interest (internal implementation) |
|
|
|
* @params {object[]} transactions - array of transactions objects |
|
|
|
* Find the transactions of interest |
|
|
|
* based on theirs outputs |
|
|
|
* @returns {object[]} returns an array of transactions objects |
|
|
|
*/ |
|
|
|
async _prefilterTransactions(transactions) { |
|
|
|
let inputs = [] |
|
|
|
let outputs = [] |
|
|
|
async prefilterByOutputs() { |
|
|
|
// Process transactions by slices of 5000 transactions
|
|
|
|
const MAX_NB_TXS = 5000 |
|
|
|
const lists = util.splitList(this.transactions, MAX_NB_TXS) |
|
|
|
const results = await util.parallelCall(lists, txs => this._prefilterByOutputs(txs)) |
|
|
|
return _.flatten(results) |
|
|
|
} |
|
|
|
|
|
|
|
// Store indices of txs to be processed
|
|
|
|
/** |
|
|
|
* Find the transactions of interest |
|
|
|
* based on theirs outputs (internal implementation) |
|
|
|
* @params {object[]} txs - array of transactions objects |
|
|
|
* @returns {object[]} returns an array of transactions objects |
|
|
|
*/ |
|
|
|
async _prefilterByOutputs(txs) { |
|
|
|
let addresses = [] |
|
|
|
let filteredIdxTxs = [] |
|
|
|
|
|
|
|
// Store txs indices, keyed by `txid-outindex`.
|
|
|
|
// Values are arrays of txs indices (for double spends)
|
|
|
|
let indexedInputs = {} |
|
|
|
|
|
|
|
// Store txs indices, keyed by address.
|
|
|
|
// Values are arrays of txs indices
|
|
|
|
let indexedOutputs = {} |
|
|
|
|
|
|
|
// Stores txs indices, keyed by txids
|
|
|
|
let indexedTxs = {} |
|
|
|
|
|
|
|
//
|
|
|
|
// Prefilter against the outputs
|
|
|
|
//
|
|
|
|
|
|
|
|
// Index the transaction outputs
|
|
|
|
for (const i in transactions) { |
|
|
|
const tx = transactions[i] |
|
|
|
console.time('outputScript2Address') |
|
|
|
for (const i in txs) { |
|
|
|
const tx = txs[i] |
|
|
|
const txid = tx.getId() |
|
|
|
|
|
|
|
indexedTxs[txid] = i |
|
|
|
|
|
|
|
// If we already checked this tx
|
|
|
|
if (TransactionsBundle.cache.has(txid)) |
|
|
|
continue |
|
|
|
|
|
|
@ -116,18 +108,17 @@ class TransactionsBundle { |
|
|
|
try { |
|
|
|
const script = tx.outs[j].script |
|
|
|
const address = addrHelper.outputScript2Address(script) |
|
|
|
outputs.push(address) |
|
|
|
// Index the output
|
|
|
|
addresses.push(address) |
|
|
|
if (!indexedOutputs[address]) |
|
|
|
indexedOutputs[address] = [] |
|
|
|
indexedOutputs[address].push(i) |
|
|
|
} catch (e) {} |
|
|
|
} |
|
|
|
} |
|
|
|
console.timeEnd('outputScript2Address') |
|
|
|
|
|
|
|
// Prefilter
|
|
|
|
const outRes = await db.getUngroupedHDAccountsByAddresses(outputs) |
|
|
|
|
|
|
|
const outRes = await db.getUngroupedHDAccountsByAddresses(addresses) |
|
|
|
for (const i in outRes) { |
|
|
|
const key = outRes[i].addrAddress |
|
|
|
const idxTxs = indexedOutputs[key] |
|
|
@ -138,35 +129,36 @@ class TransactionsBundle { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//
|
|
|
|
// Prefilter against the inputs
|
|
|
|
//
|
|
|
|
return filteredIdxTxs.map(x => txs[x]) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Find the transactions of interest |
|
|
|
* based on theirs inputs (internal implementation) |
|
|
|
* @params {object[]} txs - array of transactions objects |
|
|
|
* @returns {object[]} returns an array of transactions objects |
|
|
|
*/ |
|
|
|
async _prefilterByInputs(txs) { |
|
|
|
let inputs = [] |
|
|
|
let filteredIdxTxs = [] |
|
|
|
let indexedInputs = {} |
|
|
|
|
|
|
|
// Index the transaction inputs
|
|
|
|
for (const i in transactions) { |
|
|
|
const tx = transactions[i] |
|
|
|
for (const i in txs) { |
|
|
|
const tx = txs[i] |
|
|
|
const txid = tx.getId() |
|
|
|
|
|
|
|
// If we already checked this tx
|
|
|
|
if (TransactionsBundle.cache.has(txid)) |
|
|
|
continue |
|
|
|
|
|
|
|
for (const j in tx.ins) { |
|
|
|
const spendHash = tx.ins[j].hash |
|
|
|
const spendTxid = Buffer.from(spendHash).reverse().toString('hex') |
|
|
|
// Check if this input consumes an output
|
|
|
|
// generated by a transaction from this block
|
|
|
|
if (filteredIdxTxs.indexOf(indexedTxs[spendTxid]) > -1 && filteredIdxTxs.indexOf(i) == -1) { |
|
|
|
filteredIdxTxs.push(i) |
|
|
|
} else { |
|
|
|
const spendIdx = tx.ins[j].index |
|
|
|
inputs.push({txid: spendTxid, index: spendIdx}) |
|
|
|
// Index the input
|
|
|
|
const key = spendTxid + '-' + spendIdx |
|
|
|
if (!indexedInputs[key]) |
|
|
|
indexedInputs[key] = [] |
|
|
|
indexedInputs[key].push(i) |
|
|
|
} |
|
|
|
const spendIdx = tx.ins[j].index |
|
|
|
inputs.push({txid: spendTxid, index: spendIdx}) |
|
|
|
const key = spendTxid + '-' + spendIdx |
|
|
|
if (!indexedInputs[key]) |
|
|
|
indexedInputs[key] = [] |
|
|
|
indexedInputs[key].push(i) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -174,7 +166,6 @@ class TransactionsBundle { |
|
|
|
const lists = util.splitList(inputs, 1000) |
|
|
|
const results = await util.parallelCall(lists, list => db.getOutputSpends(list)) |
|
|
|
const inRes = _.flatten(results) |
|
|
|
|
|
|
|
for (const i in inRes) { |
|
|
|
const key = inRes[i].txnTxid + '-' + inRes[i].outIndex |
|
|
|
const idxTxs = indexedInputs[key] |
|
|
@ -185,11 +176,7 @@ class TransactionsBundle { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//
|
|
|
|
// Returns the matching transactions
|
|
|
|
//
|
|
|
|
filteredIdxTxs.sort((a, b) => a - b); |
|
|
|
return filteredIdxTxs.map(x => transactions[x]) |
|
|
|
return filteredIdxTxs.map(x => txs[x]) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|