251 lines
6.4 KiB
JavaScript
251 lines
6.4 KiB
JavaScript
'use strict';
|
|
|
|
var bitcore = require('bitcore');
|
|
var common = require('./common');
|
|
var async = require('async');
|
|
|
|
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()
|
|
})
|
|
}
|
|
|
|
req.transaction = self.transformTransaction(transaction);
|
|
next();
|
|
});
|
|
});
|
|
};
|
|
|
|
TxController.prototype.transformTransaction = function(transaction) {
|
|
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));
|
|
}
|
|
|
|
transformed.vout = txObj.outputs.map(this.transformOutput.bind(this));
|
|
|
|
transformed.blockhash = transaction.__blockHash;
|
|
transformed.confirmations = confirmations;
|
|
transformed.time = transaction.__timestamp ? transaction.__timestamp : Date.now(); // can we get this from bitcoind?
|
|
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;
|
|
}
|
|
|
|
return transformed;
|
|
};
|
|
|
|
TxController.prototype.transformInput = function(input, index) {
|
|
var transformed = {
|
|
txid: input.prevTxId,
|
|
vout: input.outputIndex,
|
|
scriptSig: {
|
|
asm: null, // TODO
|
|
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(output, index) {
|
|
var transformed = {
|
|
value: (output.satoshis / 1e8).toFixed(8),
|
|
n: index,
|
|
scriptPubKey: {
|
|
asm: null, // TODO
|
|
hex: output.script,
|
|
reqSigs: null, // TODO
|
|
type: null // TODO
|
|
},
|
|
spentTxId: null, // TODO
|
|
spentIndex: null, // TODO
|
|
spentTs: null // TODO
|
|
};
|
|
|
|
var address = bitcore.Script(output.script).toAddress(this.node.network).toString();
|
|
if(address !== 'false') {
|
|
transformed.scriptPubKey.addresses = [address];
|
|
}
|
|
|
|
return transformed;
|
|
};
|
|
|
|
TxController.prototype.rawTransaction = function(req, res, next, txid) {
|
|
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;
|
|
var address = req.query.address;
|
|
var page = req.query.pageNum;
|
|
var pageLength = 10;
|
|
var pagesTotal = 1;
|
|
|
|
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;
|
|
var totalTxs = txs.length;
|
|
|
|
if(page) {
|
|
txs = txs.splice(page * pageLength, pageLength);
|
|
pagesTotal = Math.ceil(totalTxs / pageLength);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
next(null, self.transformTransaction(tx));
|
|
});
|
|
}, function(err, transformed) {
|
|
if(err) {
|
|
return common.handleErrors(err, res);
|
|
}
|
|
|
|
res.jsonp({
|
|
pagesTotal: pagesTotal,
|
|
txs: transformed
|
|
});
|
|
});
|
|
});
|
|
} else if(address) {
|
|
self.node.getAddressHistory(address, true, function(err, txinfos) {
|
|
if(err) {
|
|
return common.handleErrors(err, res);
|
|
}
|
|
|
|
var txs = txinfos.map(function(info) {
|
|
return info.tx;
|
|
}).filter(function(value, index, self) {
|
|
return self.indexOf(value) === index;
|
|
});
|
|
|
|
var totalTxs = txs.length;
|
|
|
|
if(page) {
|
|
txs = txs.splice(page * pageLength, pageLength);
|
|
pagesTotal = Math.ceil(totalTxs / pageLength);
|
|
}
|
|
|
|
var transformed = txs.map(self.transformTransaction.bind(self));
|
|
|
|
res.jsonp({
|
|
pagesTotal: pagesTotal,
|
|
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; |