wip
This commit is contained in:
parent
5de265e094
commit
5e0b2b1f11
@ -50,13 +50,13 @@ AddressService.prototype.stop = function(callback) {
|
||||
|
||||
AddressService.prototype.getAPIMethods = function() {
|
||||
return [
|
||||
['getBalance', this, this.getBalance, 2],
|
||||
['getOutputs', this, this.getOutputs, 2],
|
||||
['getUtxos', this, this.getUtxos, 2],
|
||||
['getInputForOutput', this, this.getInputForOutput, 2],
|
||||
['isSpent', this, this.isSpent, 2],
|
||||
['getAddressHistory', this, this.getAddressHistory, 2],
|
||||
['getAddressSummary', this, this.getAddressSummary, 1]
|
||||
//['getBalance', this, this.getBalance, 2],
|
||||
//['getOutputs', this, this.getOutputs, 2],
|
||||
//['getUtxos', this, this.getUtxos, 2],
|
||||
//['getInputForOutput', this, this.getInputForOutput, 2],
|
||||
//['getAddressHistory', this, this.getAddressHistory, 2],
|
||||
//['getAddressSummary', this, this.getAddressSummary, 1],
|
||||
['getAddressUnspentOutputs', this, this.getAddressUnspentOutputs, 1]
|
||||
];
|
||||
};
|
||||
|
||||
@ -87,117 +87,104 @@ AddressService.prototype._getAddress = function(opts, item) {
|
||||
|
||||
};
|
||||
|
||||
|
||||
AddressService.prototype._getActions = function(connect) {
|
||||
|
||||
var action = 'put';
|
||||
var reverseAction = 'del';
|
||||
|
||||
if (!connect) {
|
||||
action = 'del';
|
||||
reverseAction = 'put';
|
||||
}
|
||||
|
||||
return { action: action, reverseAction: reverseAction };
|
||||
|
||||
};
|
||||
|
||||
AddressService.prototype._processInput = function(opts, input, cb) {
|
||||
|
||||
var self = this;
|
||||
AddressService.prototype._processInput = function(opts, input) {
|
||||
|
||||
var address = this._getAddress(opts, input);
|
||||
|
||||
if (!address) {
|
||||
return setImemdiate(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
var action = self._getAddress(opts.connect);
|
||||
|
||||
var operations = [];
|
||||
|
||||
// address index
|
||||
var addressKey = self._encoding.encodeAddressIndexKey(address, opts.block.height, opts.tx.id);
|
||||
var addressKey = this._encoding.encodeAddressIndexKey(address, opts.block.height, opts.tx.id);
|
||||
|
||||
operations.push({
|
||||
type: action.action,
|
||||
var operations = [{
|
||||
type: opts.action,
|
||||
key: addressKey
|
||||
});
|
||||
}];
|
||||
|
||||
var prevTxId = input.prevTxId.toString('hex');
|
||||
// prev utxo
|
||||
// TODO: ensure this is a good link backward
|
||||
var rec = {
|
||||
type: opts.action,
|
||||
key: this._encoding.encodeUtxoIndexKey(address, input.prevTxId.toString('hex'), input.outputIndex)
|
||||
};
|
||||
|
||||
self._txService.getTransaction(prevTxId, {}, function(err, tx) {
|
||||
// In the event where we are reorg'ing,
|
||||
// this is where we are putting a utxo back in, we don't know what the original height, sats, or scriptBuffer
|
||||
// since this only happens on reorg and the utxo that was spent in the chain we are reorg'ing away from will likely
|
||||
// be spent again sometime soon, we will not add the value back in, just the key
|
||||
|
||||
if (err) {
|
||||
log.debug('Error saving tx inputs.');
|
||||
return self._emit('error', err);
|
||||
}
|
||||
operations.push(rec);
|
||||
|
||||
var utxo = tx.outputs[input.outputIndex];
|
||||
|
||||
// utxo index
|
||||
|
||||
|
||||
// prev utxo
|
||||
var oldUtxoKey = self._encoding.encodeUtxoIndexKey(address, tx.id, input.outputIndex);
|
||||
|
||||
// remove the old utxo
|
||||
operations.push({
|
||||
type: action.reverseAction,
|
||||
key: utxaKxey,
|
||||
value: inputValue
|
||||
});
|
||||
|
||||
return operations;
|
||||
});
|
||||
return operations;
|
||||
};
|
||||
|
||||
AddressService.prototype._processOutput = function(txid, output) {
|
||||
AddressService.prototype._processOutput = function(tx, output, index, opts) {
|
||||
|
||||
var address = self.getAddressString(script);
|
||||
var address = this.getAddressString(output.script);
|
||||
|
||||
if(!address) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
var addressKey = self._encoding.encodeAddressIndexKey(address, block.height, txid);
|
||||
var utxoKey = self._encoding.encodeUtxoIndexKey(address, txid, outputIndex);
|
||||
var utxoValue = self._encoding.encodeUtxoIndexValue(block.height, output.satoshis, output._scriptBuffer);
|
||||
var txid = tx.id;
|
||||
var addressKey = this._encoding.encodeAddressIndexKey(address, opts.block.height, txid);
|
||||
var utxoKey = this._encoding.encodeUtxoIndexKey(address, txid, index);
|
||||
var utxoValue = this._encoding.encodeUtxoIndexValue(opts.block.height, output.satoshis, output._scriptBuffer);
|
||||
|
||||
operations.push({
|
||||
type: action,
|
||||
var operations = [{
|
||||
type: opts.action,
|
||||
key: addressKey
|
||||
});
|
||||
}];
|
||||
|
||||
operations.push({
|
||||
type: action,
|
||||
type: opts.action,
|
||||
key: utxoKey,
|
||||
value: utxoValue
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
AddressService.prototype._processTransactions = function(opts, tx) {
|
||||
AddressService.prototype._processTransaction = function(opts, tx) {
|
||||
|
||||
var self = this;
|
||||
var txid = tx.id;
|
||||
|
||||
var _opts = { opts.block, connect: connect ? true : false };
|
||||
var action = 'put';
|
||||
var reverseAction = 'del';
|
||||
|
||||
var outputOperations = tx.outputs.map(function(tx) {
|
||||
return self._processOutput(tx, opts);
|
||||
if (!opts.connect) {
|
||||
action = 'del';
|
||||
reverseAction = 'put';
|
||||
}
|
||||
|
||||
var _opts = { block: opts.block, action: action, reverseAction: reverseAction };
|
||||
|
||||
var outputOperations = tx.outputs.map(function(output, index) {
|
||||
return self._processOutput(tx, output, index, _opts);
|
||||
});
|
||||
|
||||
opts.outputOperations = outputOperations;
|
||||
outputOperations = _.flatten(_.compact(outputOperations));
|
||||
|
||||
// this is async because we need to look up a utxo
|
||||
var inputOperations = tx.inputs.map(function(tx) {
|
||||
self._processInput(tx, opts, self._onOperations.bind(self));
|
||||
var inputOperations = tx.inputs.map(function(input) {
|
||||
self._processInput(tx, input, _opts);
|
||||
});
|
||||
|
||||
inputOperations = _.flatten(_.compact(inputOperations));
|
||||
|
||||
return outputOperations.concat(inputOperations);
|
||||
|
||||
};
|
||||
|
||||
AddressService.prototype._onOperations = function(operations) {
|
||||
AddressService.prototype._onBlock = function(block, connect) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var operations = [];
|
||||
|
||||
block.transactions.forEach(function(tx) {
|
||||
operations.concat(self._processTransaction(tx, { block: block, connect: connect }));
|
||||
});
|
||||
|
||||
if (operations && operations.length > 0) {
|
||||
|
||||
@ -215,15 +202,6 @@ AddressService.prototype._onOperations = function(operations) {
|
||||
|
||||
};
|
||||
|
||||
AddressService.prototype._onBlock = function(block, connect) {
|
||||
var self = this;
|
||||
|
||||
|
||||
self._processTransactions(txs, { block: block, connect: connect });
|
||||
|
||||
|
||||
};
|
||||
|
||||
AddressService.prototype.getAddressString = function(script, output) {
|
||||
var address = script.toAddress(this.node.network.name);
|
||||
if(address) {
|
||||
@ -1014,7 +992,7 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
||||
|
||||
var txids = [];
|
||||
|
||||
async.eachLimit(addresses, self.concurrency, function(address, next) {
|
||||
async.eachLimit(addresses, 4, function(address, next) {
|
||||
self.getAddressTxids(address, options, function(err, tmpTxids) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
@ -1024,7 +1002,7 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
||||
return next();
|
||||
});
|
||||
}, function() {
|
||||
async.mapLimit(txids, self.concurrency, function(txid, next) {
|
||||
async.mapLimit(txids, 4, function(txid, next) {
|
||||
self.node.services.transaction.getTransaction(txid.toString('hex'), options, function(err, tx) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
@ -1104,22 +1082,94 @@ AddressService.prototype.getAddressTxidsWithHeights = function(address, options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This will give an object with:
|
||||
* balance - confirmed balance
|
||||
* unconfirmedBalance - unconfirmed balance
|
||||
* totalReceived - satoshis received
|
||||
* totalSpent - satoshis spent
|
||||
* appearances - number of transactions
|
||||
* unconfirmedAppearances - number of unconfirmed transactions
|
||||
* txids - list of txids (unless noTxList is set)
|
||||
*
|
||||
* @param {String} address
|
||||
* @param {Object} options
|
||||
* @param {Boolean} [options.noTxList] - if set, txid array will not be included
|
||||
* @param {Function} callback
|
||||
*/
|
||||
AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) {
|
||||
|
||||
var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool;
|
||||
var addresses = utils._normalizeAddressArg(address);
|
||||
var cacheKey = addresses.join('');
|
||||
var utxos = this.utxosCache.get(cacheKey);
|
||||
|
||||
function transformUnspentOutput(delta) {
|
||||
var script = bitcore.Script.fromAddress(delta.address);
|
||||
return {
|
||||
address: delta.address,
|
||||
txid: delta.txid,
|
||||
outputIndex: delta.index,
|
||||
script: script.toHex(),
|
||||
satoshis: delta.satoshis,
|
||||
timestamp: delta.timestamp
|
||||
};
|
||||
}
|
||||
|
||||
function updateWithMempool(confirmedUtxos, mempoolDeltas) {
|
||||
if (!mempoolDeltas || !mempoolDeltas.length) {
|
||||
return confirmedUtxos;
|
||||
}
|
||||
var isSpentOutputs = false;
|
||||
var mempoolUnspentOutputs = [];
|
||||
var spentOutputs = [];
|
||||
|
||||
for (var i = 0; i < mempoolDeltas.length; i++) {
|
||||
var delta = mempoolDeltas[i];
|
||||
if (delta.prevtxid && delta.satoshis <= 0) {
|
||||
if (!spentOutputs[delta.prevtxid]) {
|
||||
spentOutputs[delta.prevtxid] = [delta.prevout];
|
||||
} else {
|
||||
spentOutputs[delta.prevtxid].push(delta.prevout);
|
||||
}
|
||||
isSpentOutputs = true;
|
||||
} else {
|
||||
mempoolUnspentOutputs.push(transformUnspentOutput(delta));
|
||||
}
|
||||
}
|
||||
|
||||
var utxos = mempoolUnspentOutputs.reverse().concat(confirmedUtxos);
|
||||
|
||||
if (isSpentOutputs) {
|
||||
return utxos.filter(function(utxo) {
|
||||
if (!spentOutputs[utxo.txid]) {
|
||||
return true;
|
||||
} else {
|
||||
return (spentOutputs[utxo.txid].indexOf(utxo.outputIndex) === -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return utxos;
|
||||
}
|
||||
|
||||
function finish(mempoolDeltas) {
|
||||
if (utxos) {
|
||||
return setImmediate(function() {
|
||||
callback(null, updateWithMempool(utxos, mempoolDeltas));
|
||||
});
|
||||
} else {
|
||||
self.client.getAddressUtxos({addresses: addresses}, function(err, response) {
|
||||
if (err) {
|
||||
return callback(self._wrapRPCError(err));
|
||||
}
|
||||
var utxos = response.result.reverse();
|
||||
self.utxosCache.set(cacheKey, utxos);
|
||||
callback(null, updateWithMempool(utxos, mempoolDeltas));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (queryMempool) {
|
||||
self.client.getAddressMempool({addresses: addresses}, function(err, response) {
|
||||
if (err) {
|
||||
return callback(self._wrapRPCError(err));
|
||||
}
|
||||
finish(response.result);
|
||||
});
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
AddressService.prototype.getAddressSummary = function(addressArg, options, callback) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var startTime = new Date();
|
||||
|
||||
12
lib/utils.js
12
lib/utils.js
@ -3,6 +3,8 @@
|
||||
var bitcore = require('bitcore-lib');
|
||||
var BufferUtil = bitcore.util.buffer;
|
||||
var MAX_SAFE_INTEGER = 0x1fffffffffffff; // 2 ^ 53 - 1
|
||||
var crypto = require('crypto');
|
||||
var _ = require('lodash');
|
||||
|
||||
var utils = {};
|
||||
utils.isHash = function(value) {
|
||||
@ -93,7 +95,15 @@ utils.toJSONL = function(obj) {
|
||||
var str = JSON.stringify(obj);
|
||||
str = str.replace(/\n/g, '');
|
||||
return str + '\n';
|
||||
}
|
||||
};
|
||||
|
||||
utils.normalizeTimeStamp = function(addressArg) {
|
||||
var addresses = [addressArg];
|
||||
if (Array.isArray(addressArg)) {
|
||||
addresses = addressArg;
|
||||
}
|
||||
return addresses;
|
||||
};
|
||||
|
||||
utils.normalizeTimeStamp = function(value) {
|
||||
if (value > 0xffffffff) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user