UNPKG

lightning

Version:
159 lines (133 loc) 5 kB
const asyncAuto = require('async/auto'); const {returnResult} = require('asyncjs-util'); const {Transaction} = require('bitcoinjs-lib'); const getChainTransactions = require('./get_chain_transactions'); const {isLnd} = require('./../../lnd_requests'); const {fromHex} = Transaction; const {isArray} = Array; const method = 'listSweeps'; const notSupportedError = 'unknown service walletrpc.WalletKit'; const type = 'wallet'; /** Get self-transfer spend transactions related to channel closes Requires `onchain:read` permission Requires LND built with `walletrpc` build tag `after` is not supported in LND 0.17.3 or below { [after]: <Confirmed After Block Height Number> lnd: <Authenticated LND API Object> } @returns via cbk or Promise { transactions: [{ [block_id]: <Block Hash String> [confirmation_count]: <Confirmation Count Number> [confirmation_height]: <Confirmation Block Height Number> created_at: <Created ISO 8601 Date String> [fee]: <Fees Paid Tokens Number> id: <Transaction Id String> is_confirmed: <Is Confirmed Bool> is_outgoing: <Transaction Outbound Bool> output_addresses: [<Address String>] spends: [{ [tokens]: <Output Tokens Number> transaction_id: <Spend Transaction Id Hex String> transaction_vout: <Spend Transaction Output Index Number> }] tokens: <Tokens Including Fee Number> [transaction]: <Raw Transaction Hex String> }] } */ module.exports = ({after, lnd}, cbk) => { return new Promise((resolve, reject) => { return asyncAuto({ // Check arguments validate: cbk => { if (!isLnd({lnd, method, type})) { return cbk([400, 'ExpectedLndToGetSweepTransactions']); } return cbk(); }, // Get sweep transaction ids getSweeps: ['validate', ({}, cbk) => { return lnd[type][method]({start_height: after}, (err, res) => { if (!!err && err.details === notSupportedError) { return cbk([501, 'BackingLndDoesNotSupportListingSweeps']); } if (!!err) { return cbk([503, 'UnexpectedGetSweepTxError', {err}]); } if (!res) { return cbk([503, 'ExpectedResponseForGetSweepTxRequest']); } if (!res.transaction_ids) { return cbk([503, 'ExpectedTransactionIdsInSweepTxResponse']); } if (!isArray(res.transaction_ids.transaction_ids)) { return cbk([503, 'ExpectedArrayOfTransactionIdsInSweepsResponse']); } return cbk(null, res.transaction_ids.transaction_ids); }); }], // Get chain transactions for context adding getTransactions: ['validate', ({}, cbk) => { return getChainTransactions({lnd}, cbk); }], // Combine sweeps with transactions for context sweeps: [ 'getSweeps', 'getTransactions', ({getSweeps, getTransactions}, cbk) => { const transactions = getTransactions.transactions .filter(({id}) => getSweeps.includes(id)) .filter(({transaction}) => !!transaction) .map(tx => { const {ins} = fromHex(tx.transaction); const spends = ins.map(input => { return { transaction_id: input.hash.reverse().toString('hex'), transaction_vout: input.index, }; }); const relatedUtxos = spends.map(spend => { const spending = getTransactions.transactions.find(({id}) => { return id === spend.transaction_id; }); // Exit early when spending an unknown transaction if (!spending) { return { transaction_id: spend.transaction_id, transaction_vout: spend.transaction_vout, }; } const parentTx = spending.transaction; const {value} = fromHex(parentTx).outs[spend.transaction_vout]; return { tokens: value, transaction_id: spend.transaction_id, transaction_vout: spend.transaction_vout, }; }); return { block_id: tx.block_id, confirmation_count: tx.confirmation_count, confirmation_height: tx.confirmation_height, created_at: tx.created_at, description: tx.description, fee: tx.fee, id: tx.id, is_confirmed: tx.is_confirmed, is_outgoing: tx.is_outgoing, output_addresses: tx.output_addresses, spends: relatedUtxos, tokens: tx.tokens, transaction: tx.transaction, }; }); return cbk(null, {transactions}); }], }, returnResult({reject, resolve, of: 'sweeps'}, cbk)); }); };