Skip to content

Instantly share code, notes, and snippets.

@cyborgshead
Last active May 5, 2017 10:53
Show Gist options
  • Save cyborgshead/cc0e4674f316fe996eb1b78991baf7dd to your computer and use it in GitHub Desktop.
Save cyborgshead/cc0e4674f316fe996eb1b78991baf7dd to your computer and use it in GitHub Desktop.
'use strict';
var bitcore = require('bitcore-lib');
var _ = bitcore.deps._;
var $ = bitcore.util.preconditions;
var common = require('./common');
var async = require('async');
var MAXINT = 0xffffffff; // Math.pow(2, 32) - 1;
function TxController(node) {
this.node = node;
}
TxController.prototype.show = function(req, res) {
if (req.transaction) {
res.jsonp(req.transaction);
}
};
/**
* Find transaction by hash ...
*/
TxController.prototype.transaction = function(req, res, next, txid) {
var self = this;
this.node.getTransactionWithBlockInfo(txid, true, function(err, transaction) {
if (err && err instanceof self.node.errors.Transaction.NotFound) {
return common.handleErrors(null, res);
} else if(err) {
return common.handleErrors(err, res);
}
transaction.populateInputs(self.node.services.db, [], function(err) {
if(err) {
return res.send({
error: err.toString()
});
}
self.transformTransaction(transaction, function(err, transformedTransaction) {
if (err) {
return common.handleErrors(err, res);
}
req.transaction = transformedTransaction;
next();
});
});
});
};
TxController.prototype.transformTransaction = function(transaction, callback) {
$.checkArgument(_.isFunction(callback));
var self = this;
var txid = transaction.id;
var txObj = transaction.toObject();
var confirmations = 0;
if(transaction.__height >= 0) {
confirmations = this.node.services.db.tip.__height - transaction.__height + 1;
}
var transformed = {
txid: txObj.hash,
version: txObj.version,
locktime: txObj.nLockTime
};
if(transaction.isCoinbase()) {
transformed.vin = [
{
coinbase: txObj.inputs[0].script,
sequence: txObj.inputs[0].sequenceNumber,
n: 0
}
];
} else {
transformed.vin = txObj.inputs.map(this.transformInput.bind(this));
}
async.map(
Object.keys(txObj.outputs),
function(outputIndex, next) {
outputIndex = parseInt(outputIndex);
var output = txObj.outputs[outputIndex];
self.transformOutput(txid, output, outputIndex, next);
},
function(err, vout) {
if (err) {
return callback(err);
}
transformed.vout = vout;
transformed.blockhash = transaction.__blockHash;
transformed.blockheight = transaction.__height;
transformed.confirmations = confirmations;
var time = transaction.__timestamp ? transaction.__timestamp : Math.round(Date.now() / 1000);
transformed.time = time;
if (transformed.confirmations) {
transformed.blocktime = transformed.time;
}
if(transaction.isCoinbase()) {
transformed.isCoinBase = true;
}
transformed.valueOut = transaction.outputAmount / 1e8;
transformed.size = transaction.toBuffer().length;
if(transaction.hasAllUtxoInfo()) {
transformed.valueIn = transaction.inputAmount / 1e8;
transformed.fees = transaction.getFee() / 1e8;
}
callback(null, transformed);
}
);
};
TxController.prototype.transformInput = function(input, index) {
// Input scripts are validated and can be assumed to be valid
var script = new bitcore.Script(input.script);
var transformed = {
txid: input.prevTxId,
vout: input.outputIndex,
scriptSig: {
asm: script.toASM(),
hex: input.script
},
sequence: input.sequenceNumber,
n: index
};
if(input.output) {
transformed.addr = bitcore.Script(input.output.script).toAddress(this.node.network).toString();
transformed.valueSat = input.output.satoshis;
transformed.value = input.output.satoshis / 1e8;
transformed.doubleSpentTxID = null; // TODO
//transformed.isConfirmed = null; // TODO
//transformed.confirmations = null; // TODO
//transformed.unconfirmedInput = null; // TODO
}
return transformed;
};
TxController.prototype.transformOutput = function(txid, output, index, callback) {
var self = this;
var transformed = {
value: (output.satoshis / 1e8).toFixed(8),
n: index,
scriptPubKey: {
hex: output.script,
//reqSigs: null, // TODO
}
//spentTs: undefined // TODO
};
var script;
try {
// Output scripts can be invalid, so we need to try/catch
script = new bitcore.Script(output.script);
} catch (err) {
script = false;
}
if (script) {
transformed.scriptPubKey.asm = script.toASM();
var address = script.toAddress(this.node.network);
if (address) {
transformed.scriptPubKey.addresses = [address.toString()];
transformed.scriptPubKey.type = address.type;
}
}
var options = {
queryMempool: true
};
self.node.services.address.getInputForOutput(
txid,
index,
options,
function(err, inputResult) {
if (err) {
return callback(err);
}
if (inputResult) {
transformed.spentTxId = inputResult.inputTxId;
transformed.spentIndex = inputResult.inputIndex;
}
callback(null, transformed);
}
);
};
TxController.prototype.transformInvTransaction = function(transaction) {
var self = this;
var valueOut = 0;
var vout = [];
for (var i = 0; i < transaction.outputs.length; i++) {
var output = transaction.outputs[i];
valueOut += output.satoshis;
if (output.script) {
var address = output.script.toAddress(self.node.network);
if (address) {
var obj = {};
obj[address.toString()] = output.satoshis;
vout.push(obj);
}
}
}
var isRBF = _.any(_.pluck(transaction.inputs, 'sequenceNumber'), function(seq) {
return seq < MAXINT - 1;
});
var transformed = {
txid: transaction.hash,
valueOut: valueOut / 1e8,
vout: vout,
isRBF: isRBF,
};
return transformed;
};
TxController.prototype.rawTransaction = function(req, res, next, txid) {
var self = this;
this.node.getTransaction(txid, true, function(err, transaction) {
if (err && err instanceof self.node.errors.Transaction.NotFound) {
return common.handleErrors(null, res);
} else if(err) {
return common.handleErrors(err, res);
}
req.rawTransaction = {
'rawtx': transaction.toBuffer().toString('hex')
};
next();
});
};
TxController.prototype.showRaw = function(req, res) {
if (req.rawTransaction) {
res.jsonp(req.rawTransaction);
}
};
TxController.prototype.list = function(req, res) {
var self = this;
var blockHash = req.query.block;
if(blockHash) {
self.node.getBlock(blockHash, function(err, block) {
if(err && err.message === 'Block not found.') {
return common.handleErrors(null, res);
} else if(err) {
return common.handleErrors(err, res);
}
var blockInfo = self.node.services.bitcoind.getBlockIndex(block.hash);
var txs = block.transactions;
async.mapSeries(txs, function(tx, next) {
tx.__blockHash = block.hash;
tx.__height = blockInfo.height;
tx.__timestamp = block.header.time;
tx.populateInputs(self.node.services.db, [], function(err) {
if(err) {
return next(err);
}
self.transformTransaction(tx, next);
});
}, function(err, transformed) {
if(err) {
return common.handleErrors(err, res);
}
res.jsonp({
txs: transformed
});
});
});
} else {
return common.handleErrors(new Error('Block hash or address expected'), res);
}
};
TxController.prototype.send = function(req, res) {
this.node.sendTransaction(req.body.rawtx, function(err, txid) {
if(err) {
// TODO handle specific errors
return common.handleErrors(err, res);
}
res.json({'txid': txid});
});
};
module.exports = TxController;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment