You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
3.6 KiB
126 lines
3.6 KiB
/*!
|
|
* pushtx/pushtx-processor.js
|
|
* Copyright © 2019 – Katana Cryptographic Ltd. All Rights Reserved.
|
|
*/
|
|
'use strict'
|
|
|
|
const bitcoin = require('bitcoinjs-lib')
|
|
const zmq = require('zeromq')
|
|
const Logger = require('../lib/logger')
|
|
const errors = require('../lib/errors')
|
|
const db = require('../lib/db/mysql-db-wrapper')
|
|
const RpcClient = require('../lib/bitcoind-rpc/rpc-client')
|
|
const network = require('../lib/bitcoin/network')
|
|
const activeNet = network.network
|
|
const keys = require('../keys')[network.key]
|
|
const status = require('./status')
|
|
|
|
let Sources
|
|
if (network.key == 'bitcoin') {
|
|
Sources = require('../lib/remote-importer/sources-mainnet')
|
|
} else {
|
|
Sources = require('../lib/remote-importer/sources-testnet')
|
|
}
|
|
|
|
|
|
/**
|
|
* A singleton providing a wrapper
|
|
* for pushing transactions with the local bitcoind
|
|
*/
|
|
class PushTxProcessor {
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
constructor() {
|
|
this.notifSock = null
|
|
this.sources = new Sources()
|
|
// Initialize the rpc client
|
|
this.rpcClient = new RpcClient()
|
|
}
|
|
|
|
/**
|
|
* Initialize the sockets for notifications
|
|
*/
|
|
initNotifications(config) {
|
|
// Notification socket for the tracker
|
|
this.notifSock = zmq.socket('pub')
|
|
this.notifSock.bindSync(config.uriSocket)
|
|
}
|
|
|
|
/**
|
|
* Enforce a strict verification mode on a list of outputs
|
|
* @param {string} rawtx - raw bitcoin transaction in hex format
|
|
* @param {array} vouts - output indices (integer)
|
|
* @returns {array} returns the indices of the faulty outputs
|
|
*/
|
|
async enforceStrictModeVouts(rawtx, vouts) {
|
|
const faultyOutputs = []
|
|
const addrMap = {}
|
|
let tx
|
|
try {
|
|
tx = bitcoin.Transaction.fromHex(rawtx)
|
|
} catch(e) {
|
|
throw errors.tx.PARSE
|
|
}
|
|
// Check in db if addresses are known and have been used
|
|
for (let vout of vouts) {
|
|
if (vout >= tx.outs.length)
|
|
throw errors.txout.VOUT
|
|
const output = tx.outs[vout]
|
|
const address = bitcoin.address.fromOutputScript(output.script, activeNet)
|
|
const nbTxs = await db.getAddressNbTransactions(address)
|
|
if (nbTxs == null || nbTxs > 0)
|
|
faultyOutputs.push(vout)
|
|
else
|
|
addrMap[address] = vout
|
|
}
|
|
// Checks with indexer if addresses are known and have been used
|
|
if (Object.keys(addrMap).length > 0) {
|
|
if (keys.indexer.active != 'local_bitcoind') {
|
|
const results = await this.sources.getAddresses(Object.keys(addrMap))
|
|
for (let r of results)
|
|
if (r.ntx > 0)
|
|
faultyOutputs.push(addrMap[r.address])
|
|
}
|
|
}
|
|
return faultyOutputs
|
|
}
|
|
|
|
/**
|
|
* Push transactions to the Bitcoin network
|
|
* @param {string} rawtx - raw bitcoin transaction in hex format
|
|
* @returns {string} returns the txid of the transaction
|
|
*/
|
|
async pushTx(rawtx) {
|
|
let value = 0
|
|
|
|
// Attempt to parse incoming TX hex as a bitcoin Transaction
|
|
try {
|
|
const tx = bitcoin.Transaction.fromHex(rawtx)
|
|
for (let output of tx.outs)
|
|
value += output.value
|
|
Logger.info('PushTx : Push for ' + (value / 1e8).toFixed(8) + ' BTC')
|
|
} catch(e) {
|
|
throw errors.tx.PARSE
|
|
}
|
|
|
|
// At this point, the raw hex parses as a legitimate transaction.
|
|
// Attempt to send via RPC to the bitcoind instance
|
|
try {
|
|
const txid = await this.rpcClient.sendrawtransaction(rawtx)
|
|
Logger.info('PushTx : Pushed!')
|
|
// Update the stats
|
|
status.updateStats(value)
|
|
// Notify the tracker
|
|
this.notifSock.send(['pushtx', rawtx])
|
|
return txid
|
|
} catch(err) {
|
|
Logger.info('PushTx : Push failed')
|
|
throw err
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = new PushTxProcessor()
|
|
|