|
|
@ -46,6 +46,38 @@ class Transactions { |
|
|
|
this.rpcClient = new RpcClient() |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Get the transactions for a given array of txids |
|
|
|
* @param {string[]} txids - txids of the transaction to be retrieved |
|
|
|
* @param {boolean} fees - true if fees must be computed, false otherwise |
|
|
|
* @returns {Promise} return an array of transactions (object[]) |
|
|
|
*/ |
|
|
|
async getTransactions(txids, fees) { |
|
|
|
try { |
|
|
|
const rpcCalls = txids.map(txid => { |
|
|
|
return { |
|
|
|
'method': 'getrawtransaction', |
|
|
|
'params': [txid, true] |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
const txs = await this.rpcClient.batch(rpcCalls) |
|
|
|
|
|
|
|
return await util.seriesCall(txs, async tx => { |
|
|
|
if (tx.result == null) { |
|
|
|
Logger.info(` got null for ${txids[tx.id]}`) |
|
|
|
return null |
|
|
|
} else { |
|
|
|
return this._prepareTxResult(tx.result, fees) |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
} catch(e) { |
|
|
|
Logger.error(e, 'Transaction.getTransactions()') |
|
|
|
return Promise.reject(errors.generic.GEN) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Get the transaction for a given txid |
|
|
|
* @param {string} txid - txid of the transaction to be retrieved |
|
|
@ -61,64 +93,73 @@ class Transactions { |
|
|
|
|
|
|
|
try { |
|
|
|
const tx = await this.rpcClient.getrawtransaction(txid, true) |
|
|
|
const ret = await this._prepareTxResult(tx) |
|
|
|
// Store the result in cache
|
|
|
|
if (ret.block && ret.block.hash) |
|
|
|
this.txCache.set(txid, ret) |
|
|
|
return ret |
|
|
|
} catch(e) { |
|
|
|
Logger.error(e, 'Transaction.getTransaction()') |
|
|
|
return Promise.reject(errors.generic.GEN) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const ret = { |
|
|
|
txid: tx.txid, |
|
|
|
size: tx.size, |
|
|
|
vsize: tx.vsize, |
|
|
|
version: tx.version, |
|
|
|
locktime: tx.locktime, |
|
|
|
inputs: [], |
|
|
|
outputs: [] |
|
|
|
} |
|
|
|
/** |
|
|
|
* Formats a transaction object returned by the RPC API |
|
|
|
* @param {object} tx - transaction |
|
|
|
* @param {boolean} fees - true if fees must be computed, false otherwise |
|
|
|
* @returns {Promise} return an array of inputs (object[]) |
|
|
|
*/ |
|
|
|
async _prepareTxResult(tx, fees) { |
|
|
|
const ret = { |
|
|
|
txid: tx.txid, |
|
|
|
size: tx.size, |
|
|
|
vsize: tx.vsize, |
|
|
|
version: tx.version, |
|
|
|
locktime: tx.locktime, |
|
|
|
inputs: [], |
|
|
|
outputs: [] |
|
|
|
} |
|
|
|
|
|
|
|
if (!ret.vsize) |
|
|
|
delete ret.vsize |
|
|
|
if (!ret.vsize) |
|
|
|
delete ret.vsize |
|
|
|
|
|
|
|
if (tx.time) |
|
|
|
ret.created = tx.time |
|
|
|
if (tx.time) |
|
|
|
ret.created = tx.time |
|
|
|
|
|
|
|
// Process block informations
|
|
|
|
if (tx.blockhash && tx.confirmations && tx.blocktime) { |
|
|
|
ret.block = { |
|
|
|
height: rpcLatestBlock.height - tx.confirmations + 1, |
|
|
|
hash: tx.blockhash, |
|
|
|
time: tx.blocktime |
|
|
|
} |
|
|
|
// Process block informations
|
|
|
|
if (tx.blockhash && tx.confirmations && tx.blocktime) { |
|
|
|
ret.block = { |
|
|
|
height: rpcLatestBlock.height - tx.confirmations + 1, |
|
|
|
hash: tx.blockhash, |
|
|
|
time: tx.blocktime |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
let inAmount = 0 |
|
|
|
let outAmount = 0 |
|
|
|
|
|
|
|
// Process the inputs
|
|
|
|
ret.inputs = await this._getInputs(tx, fees) |
|
|
|
inAmount = ret.inputs.reduce((prev, cur) => prev + cur.outpoint.value, 0) |
|
|
|
|
|
|
|
// Process the outputs
|
|
|
|
ret.outputs = await this._getOutputs(tx) |
|
|
|
outAmount = ret.outputs.reduce((prev, cur) => prev + cur.value, 0) |
|
|
|
let inAmount = 0 |
|
|
|
let outAmount = 0 |
|
|
|
|
|
|
|
// Process the fees (if needed)
|
|
|
|
if (fees) { |
|
|
|
ret.fees = inAmount - outAmount |
|
|
|
if (ret.fees > 0 && ret.size) |
|
|
|
ret.feerate = Math.round(ret.fees / ret.size) |
|
|
|
if (ret.fees > 0 && ret.vsize) |
|
|
|
ret.vfeerate = Math.round(ret.fees / ret.vsize) |
|
|
|
} |
|
|
|
// Process the inputs
|
|
|
|
ret.inputs = await this._getInputs(tx, fees) |
|
|
|
inAmount = ret.inputs.reduce((prev, cur) => prev + cur.outpoint.value, 0) |
|
|
|
|
|
|
|
// Store in cache
|
|
|
|
if (ret.block && ret.block.hash) |
|
|
|
this.txCache.set(keyCache, ret) |
|
|
|
// Process the outputs
|
|
|
|
ret.outputs = await this._getOutputs(tx) |
|
|
|
outAmount = ret.outputs.reduce((prev, cur) => prev + cur.value, 0) |
|
|
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
} catch(e) { |
|
|
|
Logger.error(e, 'Transaction.getTransaction()') |
|
|
|
return Promise.reject(errors.generic.GEN) |
|
|
|
// Process the fees (if needed)
|
|
|
|
if (fees) { |
|
|
|
ret.fees = inAmount - outAmount |
|
|
|
if (ret.fees > 0 && ret.size) |
|
|
|
ret.feerate = Math.round(ret.fees / ret.size) |
|
|
|
if (ret.fees > 0 && ret.vsize) |
|
|
|
ret.vfeerate = Math.round(ret.fees / ret.vsize) |
|
|
|
} |
|
|
|
|
|
|
|
return ret |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Extract information about the inputs of a transaction |
|
|
|
* @param {object} tx - transaction |
|
|
@ -180,7 +221,6 @@ class Transactions { |
|
|
|
/** |
|
|
|
* Extract information about the outputs of a transaction |
|
|
|
* @param {object} tx - transaction |
|
|
|
* @param {boolean} fees - true if fees must be computed, false otherwise |
|
|
|
* @returns {Promise} return an array of outputs (object[]) |
|
|
|
*/ |
|
|
|
async _getOutputs(tx) { |
|
|
|