2014-01-09 15:24:14 -08:00
|
|
|
'use strict';
|
|
|
|
|
2014-03-10 11:06:10 -07:00
|
|
|
var imports = require('soop').imports();
|
2014-03-05 18:03:56 -08:00
|
|
|
var async = require('async');
|
|
|
|
var BitcoreAddress = require('bitcore/Address');
|
|
|
|
var BitcoreTransaction = require('bitcore/Transaction');
|
|
|
|
var BitcoreUtil = require('bitcore/util/util');
|
|
|
|
var Parser = require('bitcore/util/BinaryParser');
|
|
|
|
var Buffer = require('buffer').Buffer;
|
2014-03-10 11:06:10 -07:00
|
|
|
var TransactionDb = imports.TransactionDb || require('../../lib/TransactionDb').default();
|
2014-03-05 18:03:56 -08:00
|
|
|
var CONCURRENCY = 5;
|
|
|
|
|
|
|
|
function Address(addrStr) {
|
|
|
|
this.balanceSat = 0;
|
|
|
|
this.totalReceivedSat = 0;
|
|
|
|
this.totalSentSat = 0;
|
|
|
|
|
|
|
|
this.unconfirmedBalanceSat = 0;
|
|
|
|
|
|
|
|
this.txApperances = 0;
|
|
|
|
this.unconfirmedTxApperances= 0;
|
|
|
|
|
|
|
|
// TODO store only txids? +index? +all?
|
|
|
|
this.transactions = [];
|
|
|
|
|
|
|
|
var a = new BitcoreAddress(addrStr);
|
|
|
|
a.validate();
|
|
|
|
this.addrStr = addrStr;
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'totalSent', {
|
|
|
|
get: function() {
|
|
|
|
return parseFloat(this.totalSentSat) / parseFloat(BitcoreUtil.COIN);
|
|
|
|
},
|
|
|
|
set: function(i) {
|
|
|
|
this.totalSentSat = i * BitcoreUtil.COIN;
|
|
|
|
},
|
|
|
|
enumerable: 1,
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'balance', {
|
|
|
|
get: function() {
|
|
|
|
return parseFloat(this.balanceSat) / parseFloat(BitcoreUtil.COIN);
|
|
|
|
},
|
|
|
|
set: function(i) {
|
|
|
|
this.balance = i * BitcoreUtil.COIN;
|
|
|
|
},
|
|
|
|
enumerable: 1,
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'totalReceived', {
|
|
|
|
get: function() {
|
|
|
|
return parseFloat(this.totalReceivedSat) / parseFloat(BitcoreUtil.COIN);
|
|
|
|
},
|
|
|
|
set: function(i) {
|
|
|
|
this.totalReceived = i * BitcoreUtil.COIN;
|
|
|
|
},
|
|
|
|
enumerable: 1,
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'unconfirmedBalance', {
|
|
|
|
get: function() {
|
|
|
|
return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN);
|
|
|
|
},
|
|
|
|
set: function(i) {
|
|
|
|
this.unconfirmedBalanceSat = i * BitcoreUtil.COIN;
|
|
|
|
},
|
|
|
|
enumerable: 1,
|
|
|
|
});
|
2014-02-12 14:27:29 -08:00
|
|
|
|
2014-03-05 18:03:56 -08:00
|
|
|
}
|
2014-02-12 14:27:29 -08:00
|
|
|
|
2014-03-05 18:03:56 -08:00
|
|
|
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) {
|
|
|
|
var self = this;
|
|
|
|
if (!self.addrStr) return next();
|
|
|
|
|
|
|
|
var ret = [];
|
2014-03-10 11:06:10 -07:00
|
|
|
var db = TransactionDb;
|
2014-03-05 18:03:56 -08:00
|
|
|
|
|
|
|
db.fromAddr(self.addrStr, function(err,txOut){
|
|
|
|
if (err) return next(err);
|
|
|
|
|
|
|
|
// Complete utxo info
|
|
|
|
async.eachLimit(txOut,CONCURRENCY,function (txItem, a_c) {
|
|
|
|
db.fromIdInfoSimple(txItem.txid, function(err, info) {
|
|
|
|
|
|
|
|
var scriptPubKey = self._getScriptPubKey(info.hex, txItem.index);
|
|
|
|
|
|
|
|
// we are filtering out even unconfirmed spents!
|
|
|
|
// add || !txItem.spentIsConfirmed
|
|
|
|
if (!txItem.spentTxId) {
|
|
|
|
ret.push({
|
|
|
|
address: self.addrStr,
|
|
|
|
txid: txItem.txid,
|
|
|
|
vout: txItem.index,
|
|
|
|
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);
|
2014-02-12 14:27:29 -08:00
|
|
|
});
|
2014-03-05 18:03:56 -08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Address.prototype.update = function(next) {
|
|
|
|
var self = this;
|
|
|
|
if (!self.addrStr) return next();
|
|
|
|
|
|
|
|
var txs = [];
|
2014-03-10 11:06:10 -07:00
|
|
|
var db = TransactionDb;
|
2014-03-05 18:03:56 -08:00
|
|
|
async.series([
|
|
|
|
function (cb) {
|
|
|
|
var seen={};
|
|
|
|
db.fromAddr(self.addrStr, function(err,txOut){
|
|
|
|
if (err) return cb(err);
|
|
|
|
txOut.forEach(function(txItem){
|
|
|
|
var add=0, addSpend=0;
|
|
|
|
var v = txItem.value_sat;
|
|
|
|
|
|
|
|
if ( !seen[txItem.txid] ) {
|
|
|
|
txs.push({txid: txItem.txid, ts: txItem.ts});
|
|
|
|
seen[txItem.txid]=1;
|
|
|
|
add=1;
|
|
|
|
}
|
2014-02-12 14:27:29 -08:00
|
|
|
|
2014-03-05 18:03:56 -08:00
|
|
|
if (txItem.spentTxId && !seen[txItem.spentTxId] ) {
|
|
|
|
txs.push({txid: txItem.spentTxId, ts: txItem.spentTs});
|
|
|
|
seen[txItem.spentTxId]=1;
|
|
|
|
addSpend=1;
|
2014-02-20 11:10:10 -08:00
|
|
|
}
|
2014-02-12 14:27:29 -08:00
|
|
|
|
2014-03-05 18:03:56 -08:00
|
|
|
if (txItem.isConfirmed) {
|
|
|
|
self.txApperances += add;
|
|
|
|
self.totalReceivedSat += v;
|
|
|
|
if (! txItem.spentTxId ) {
|
|
|
|
//unspent
|
|
|
|
self.balanceSat += v;
|
2014-02-12 14:27:29 -08:00
|
|
|
}
|
2014-03-05 18:03:56 -08:00
|
|
|
else if(!txItem.spentIsConfirmed) {
|
|
|
|
// unspent
|
|
|
|
self.balanceSat += v;
|
|
|
|
self.unconfirmedBalanceSat -= v;
|
|
|
|
self.unconfirmedTxApperances += addSpend;
|
2014-02-08 05:57:37 -08:00
|
|
|
}
|
2014-02-12 14:27:29 -08:00
|
|
|
else {
|
2014-03-05 18:03:56 -08:00
|
|
|
// spent
|
|
|
|
self.totalSentSat += v;
|
|
|
|
self.txApperances += addSpend;
|
2014-02-12 14:27:29 -08:00
|
|
|
}
|
2014-03-05 18:03:56 -08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.unconfirmedBalanceSat += v;
|
|
|
|
self.unconfirmedTxApperances += add;
|
|
|
|
}
|
2014-02-12 14:27:29 -08:00
|
|
|
});
|
2014-03-05 18:03:56 -08:00
|
|
|
return cb();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
], function (err) {
|
|
|
|
|
|
|
|
// sort input and outputs togheter
|
|
|
|
txs.sort(
|
|
|
|
function compare(a,b) {
|
|
|
|
if (a.ts < b.ts) return 1;
|
|
|
|
if (a.ts > b.ts) return -1;
|
|
|
|
return 0;
|
|
|
|
});
|
2014-02-12 14:27:29 -08:00
|
|
|
|
2014-03-05 18:03:56 -08:00
|
|
|
self.transactions = txs.map(function(i) { return i.txid; } );
|
|
|
|
return next(err);
|
|
|
|
});
|
|
|
|
};
|
2014-01-12 18:37:36 -08:00
|
|
|
|
2014-03-05 18:03:56 -08:00
|
|
|
module.exports = require('soop')(Address);
|
2014-01-12 18:37:36 -08:00
|
|
|
|