Merge pull request #385 from matiu/feature/unspent-outputs
add scriptpubkey to utxo
This commit is contained in:
commit
adfd363c76
|
@ -4,10 +4,14 @@ require('classtool');
|
||||||
|
|
||||||
|
|
||||||
function spec() {
|
function spec() {
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var BitcoreAddress = require('bitcore/Address').class();
|
var BitcoreAddress = require('bitcore/Address').class();
|
||||||
var BitcoreUtil = require('bitcore/util/util');
|
var BitcoreUtil = require('bitcore/util/util');
|
||||||
var TransactionDb = require('../../lib/TransactionDb').class();
|
var TransactionDb = require('../../lib/TransactionDb').class();
|
||||||
|
var BitcoreTransaction = require('bitcore/Transaction').class();
|
||||||
|
var Parser = require('bitcore/util/BinaryParser').class();
|
||||||
|
var Buffer = require('buffer').Buffer;
|
||||||
|
var CONCURRENCY = 5;
|
||||||
|
|
||||||
function Address(addrStr) {
|
function Address(addrStr) {
|
||||||
this.balanceSat = 0;
|
this.balanceSat = 0;
|
||||||
|
@ -69,6 +73,14 @@ function spec() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Address.prototype._getScriptPubKey = function(hex,n) {
|
||||||
|
// ScriptPubKey is not provided by bitcoind RPC, so we parse it from tx hex.
|
||||||
|
|
||||||
|
var parser = new Parser(new Buffer(hex,'hex'));
|
||||||
|
var tx = new BitcoreTransaction();
|
||||||
|
tx.parse(parser);
|
||||||
|
return (tx.outs[n].s.toString('hex'));
|
||||||
|
};
|
||||||
|
|
||||||
Address.prototype.getUtxo = function(next) {
|
Address.prototype.getUtxo = function(next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -80,22 +92,30 @@ function spec() {
|
||||||
db.fromAddr(self.addrStr, function(err,txOut){
|
db.fromAddr(self.addrStr, function(err,txOut){
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
txOut.forEach(function(txItem){
|
// Complete utxo info
|
||||||
|
async.eachLimit(txOut,CONCURRENCY,function (txItem, a_c) {
|
||||||
|
db.fromIdInfoSimple(txItem.txid, function(err, info) {
|
||||||
|
|
||||||
// we are filtering out even unconfirmed spents!
|
var scriptPubKey = self._getScriptPubKey(info.hex, txItem.index);
|
||||||
// add || !txItem.spentIsConfirmed
|
|
||||||
if (!txItem.spentTxId) {
|
// we are filtering out even unconfirmed spents!
|
||||||
ret.push({
|
// add || !txItem.spentIsConfirmed
|
||||||
address: self.addrStr,
|
if (!txItem.spentTxId) {
|
||||||
txid: txItem.txid,
|
ret.push({
|
||||||
vout: txItem.index,
|
address: self.addrStr,
|
||||||
ts: txItem.ts,
|
txid: txItem.txid,
|
||||||
amount: txItem.value_sat / BitcoreUtil.COIN,
|
vout: txItem.index,
|
||||||
confirmations: txItem.isConfirmed ? 1 : 0, // TODO => actually is 1+
|
ts: txItem.ts,
|
||||||
});
|
scriptPubKey: scriptPubKey,
|
||||||
}
|
amount: txItem.value_sat / BitcoreUtil.COIN,
|
||||||
|
confirmations: txItem.isConfirmed ? info.confirmations : 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return a_c(err);
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
return next(err,ret);
|
||||||
});
|
});
|
||||||
return next(err,ret);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
13
lib/Rpc.js
13
lib/Rpc.js
|
@ -18,7 +18,7 @@ function spec(b) {
|
||||||
Rpc._parseTxResult = function(info) {
|
Rpc._parseTxResult = function(info) {
|
||||||
var b = new Buffer(info.hex,'hex');
|
var b = new Buffer(info.hex,'hex');
|
||||||
|
|
||||||
// remove fields we dont need, to speed, and adapt the information
|
// remove fields we dont need, to speed and adapt the information
|
||||||
delete info['hex'];
|
delete info['hex'];
|
||||||
|
|
||||||
// Inputs => add index + coinBase flag
|
// Inputs => add index + coinBase flag
|
||||||
|
@ -53,17 +53,20 @@ function spec(b) {
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
|
|
||||||
Rpc.getTxInfo = function(txid, cb) {
|
Rpc.getTxInfo = function(txid, doNotParse, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
bitcoreRpc.getRawTransaction(txid, 1, function(err, txInfo) {
|
if (typeof doNotParse === 'function') {
|
||||||
|
cb = doNotParse;
|
||||||
|
doNotParse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitcoreRpc.getRawTransaction(txid, 1, function(err, txInfo) {
|
||||||
// Not found?
|
// Not found?
|
||||||
if (err && err.code === -5) return cb();
|
if (err && err.code === -5) return cb();
|
||||||
if (err) return cb(self.errMsg(err));
|
if (err) return cb(self.errMsg(err));
|
||||||
|
|
||||||
var info = self._parseTxResult(txInfo.result);
|
var info = doNotParse ? txInfo.result : self._parseTxResult(txInfo.result);
|
||||||
|
|
||||||
return cb(null,info);
|
return cb(null,info);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -267,13 +267,21 @@ isspent
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Simplified / faster Info version: No spent / outpoints info.
|
||||||
|
TransactionDb.prototype.fromIdInfoSimple = function(txid, cb) {
|
||||||
|
Rpc.getTxInfo(txid, true, function(err, info) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
if (!info) return cb();
|
||||||
|
return cb(err, info);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
TransactionDb.prototype.fromIdWithInfo = function(txid, cb) {
|
TransactionDb.prototype.fromIdWithInfo = function(txid, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self._getInfo(txid, function(err, info) {
|
self._getInfo(txid, function(err, info) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (!info) return cb();
|
if (!info) return cb();
|
||||||
//console.log('[TransactionDb.js.278:info:]',info); //TODO
|
|
||||||
return cb(err, {
|
return cb(err, {
|
||||||
txid: txid,
|
txid: txid,
|
||||||
info: info
|
info: info
|
||||||
|
@ -720,7 +728,6 @@ isspent
|
||||||
return self.createFromArray(b.tx, b.hash, next);
|
return self.createFromArray(b.tx, b.hash, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return TransactionDb;
|
return TransactionDb;
|
||||||
}
|
}
|
||||||
module.defineClass(spec);
|
module.defineClass(spec);
|
||||||
|
|
|
@ -8,7 +8,8 @@ var assert = require('assert'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
Address = require('../../app/models/Address').class(),
|
Address = require('../../app/models/Address').class(),
|
||||||
TransactionDb = require('../../lib/TransactionDb').class(),
|
TransactionDb = require('../../lib/TransactionDb').class(),
|
||||||
addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json'));
|
addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json')),
|
||||||
|
utxoValid = JSON.parse(fs.readFileSync('test/integration/utxo.json'));
|
||||||
|
|
||||||
var txDb;
|
var txDb;
|
||||||
describe('Address balances', function() {
|
describe('Address balances', function() {
|
||||||
|
@ -52,3 +53,29 @@ describe('Address balances', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('Address utxo', function() {
|
||||||
|
utxoValid.forEach(function(v) {
|
||||||
|
if (v.disabled) {
|
||||||
|
console.log(v.addr + ' => disabled in JSON');
|
||||||
|
} else {
|
||||||
|
it('Address utxo for: ' + v.addr, function(done) {
|
||||||
|
this.timeout(50000);
|
||||||
|
|
||||||
|
var a = new Address(v.addr, txDb);
|
||||||
|
a.getUtxo(function(err, utxo) {
|
||||||
|
if (err) done(err);
|
||||||
|
assert.equal(v.addr, a.addrStr);
|
||||||
|
if (v.length) assert.equal(v.length, utxo.length, 'length: ' + utxo.length);
|
||||||
|
if (v.tx0id) assert.equal(v.tx0id, utxo[0].txid, 'have tx: ' + utxo[0].txid);
|
||||||
|
if (v.tx0scriptPubKey)
|
||||||
|
assert.equal(v.tx0scriptPubKey, utxo[0].scriptPubKey, 'have tx: ' + utxo[0].scriptPubKey);
|
||||||
|
if (v.tx0amount)
|
||||||
|
assert.equal(v.tx0amount, utxo[0].amount, 'amount: ' + utxo[0].amount);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH",
|
||||||
|
"length": 1,
|
||||||
|
"tx0id": "eeabc70063d3f266e190e8735bc4599c811d3a79d138da1364e88502069b029c",
|
||||||
|
"tx0scriptPubKey": "76a9149e9f6515c70db535abdbbc983c7d8d1bff6c20cd88ac",
|
||||||
|
"tx0amount": 0.38571339
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM",
|
||||||
|
"length": 1,
|
||||||
|
"tx0id": "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5",
|
||||||
|
"tx0scriptPubKey": "76a91408cf4ceb2b7278043fcc7f545e6e6e73ef9a644f88ac",
|
||||||
|
"tx0amount": 0.01979459
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in New Issue