wip
This commit is contained in:
parent
5de265e094
commit
5e0b2b1f11
@ -50,13 +50,13 @@ AddressService.prototype.stop = function(callback) {
|
|||||||
|
|
||||||
AddressService.prototype.getAPIMethods = function() {
|
AddressService.prototype.getAPIMethods = function() {
|
||||||
return [
|
return [
|
||||||
['getBalance', this, this.getBalance, 2],
|
//['getBalance', this, this.getBalance, 2],
|
||||||
['getOutputs', this, this.getOutputs, 2],
|
//['getOutputs', this, this.getOutputs, 2],
|
||||||
['getUtxos', this, this.getUtxos, 2],
|
//['getUtxos', this, this.getUtxos, 2],
|
||||||
['getInputForOutput', this, this.getInputForOutput, 2],
|
//['getInputForOutput', this, this.getInputForOutput, 2],
|
||||||
['isSpent', this, this.isSpent, 2],
|
//['getAddressHistory', this, this.getAddressHistory, 2],
|
||||||
['getAddressHistory', this, this.getAddressHistory, 2],
|
//['getAddressSummary', this, this.getAddressSummary, 1],
|
||||||
['getAddressSummary', this, this.getAddressSummary, 1]
|
['getAddressUnspentOutputs', this, this.getAddressUnspentOutputs, 1]
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,117 +87,104 @@ AddressService.prototype._getAddress = function(opts, item) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._processInput = function(opts, input) {
|
||||||
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;
|
|
||||||
|
|
||||||
var address = this._getAddress(opts, input);
|
var address = this._getAddress(opts, input);
|
||||||
|
|
||||||
if (!address) {
|
if (!address) {
|
||||||
return setImemdiate(cb);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var action = self._getAddress(opts.connect);
|
|
||||||
|
|
||||||
var operations = [];
|
|
||||||
|
|
||||||
// address index
|
// 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({
|
var operations = [{
|
||||||
type: action.action,
|
type: opts.action,
|
||||||
key: addressKey
|
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) {
|
operations.push(rec);
|
||||||
log.debug('Error saving tx inputs.');
|
|
||||||
return self._emit('error', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var utxo = tx.outputs[input.outputIndex];
|
return operations;
|
||||||
|
|
||||||
// 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;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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) {
|
if(!address) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var addressKey = self._encoding.encodeAddressIndexKey(address, block.height, txid);
|
var txid = tx.id;
|
||||||
var utxoKey = self._encoding.encodeUtxoIndexKey(address, txid, outputIndex);
|
var addressKey = this._encoding.encodeAddressIndexKey(address, opts.block.height, txid);
|
||||||
var utxoValue = self._encoding.encodeUtxoIndexValue(block.height, output.satoshis, output._scriptBuffer);
|
var utxoKey = this._encoding.encodeUtxoIndexKey(address, txid, index);
|
||||||
|
var utxoValue = this._encoding.encodeUtxoIndexValue(opts.block.height, output.satoshis, output._scriptBuffer);
|
||||||
|
|
||||||
operations.push({
|
var operations = [{
|
||||||
type: action,
|
type: opts.action,
|
||||||
key: addressKey
|
key: addressKey
|
||||||
});
|
}];
|
||||||
|
|
||||||
operations.push({
|
operations.push({
|
||||||
type: action,
|
type: opts.action,
|
||||||
key: utxoKey,
|
key: utxoKey,
|
||||||
value: utxoValue
|
value: utxoValue
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AddressService.prototype._processTransactions = function(opts, tx) {
|
AddressService.prototype._processTransaction = function(opts, tx) {
|
||||||
|
|
||||||
var self = this;
|
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) {
|
if (!opts.connect) {
|
||||||
return self._processOutput(tx, opts);
|
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(input) {
|
||||||
var inputOperations = tx.inputs.map(function(tx) {
|
self._processInput(tx, input, _opts);
|
||||||
self._processInput(tx, opts, self._onOperations.bind(self));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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) {
|
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) {
|
AddressService.prototype.getAddressString = function(script, output) {
|
||||||
var address = script.toAddress(this.node.network.name);
|
var address = script.toAddress(this.node.network.name);
|
||||||
if(address) {
|
if(address) {
|
||||||
@ -1014,7 +992,7 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
|||||||
|
|
||||||
var txids = [];
|
var txids = [];
|
||||||
|
|
||||||
async.eachLimit(addresses, self.concurrency, function(address, next) {
|
async.eachLimit(addresses, 4, function(address, next) {
|
||||||
self.getAddressTxids(address, options, function(err, tmpTxids) {
|
self.getAddressTxids(address, options, function(err, tmpTxids) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
@ -1024,7 +1002,7 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
|||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
}, function() {
|
}, 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) {
|
self.node.services.transaction.getTransaction(txid.toString('hex'), options, function(err, tx) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
@ -1104,22 +1082,94 @@ AddressService.prototype.getAddressTxidsWithHeights = function(address, options,
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) {
|
||||||
* This will give an object with:
|
|
||||||
* balance - confirmed balance
|
var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool;
|
||||||
* unconfirmedBalance - unconfirmed balance
|
var addresses = utils._normalizeAddressArg(address);
|
||||||
* totalReceived - satoshis received
|
var cacheKey = addresses.join('');
|
||||||
* totalSpent - satoshis spent
|
var utxos = this.utxosCache.get(cacheKey);
|
||||||
* appearances - number of transactions
|
|
||||||
* unconfirmedAppearances - number of unconfirmed transactions
|
function transformUnspentOutput(delta) {
|
||||||
* txids - list of txids (unless noTxList is set)
|
var script = bitcore.Script.fromAddress(delta.address);
|
||||||
*
|
return {
|
||||||
* @param {String} address
|
address: delta.address,
|
||||||
* @param {Object} options
|
txid: delta.txid,
|
||||||
* @param {Boolean} [options.noTxList] - if set, txid array will not be included
|
outputIndex: delta.index,
|
||||||
* @param {Function} callback
|
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) {
|
AddressService.prototype.getAddressSummary = function(addressArg, options, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var startTime = new Date();
|
var startTime = new Date();
|
||||||
|
|||||||
12
lib/utils.js
12
lib/utils.js
@ -3,6 +3,8 @@
|
|||||||
var bitcore = require('bitcore-lib');
|
var bitcore = require('bitcore-lib');
|
||||||
var BufferUtil = bitcore.util.buffer;
|
var BufferUtil = bitcore.util.buffer;
|
||||||
var MAX_SAFE_INTEGER = 0x1fffffffffffff; // 2 ^ 53 - 1
|
var MAX_SAFE_INTEGER = 0x1fffffffffffff; // 2 ^ 53 - 1
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
var utils = {};
|
var utils = {};
|
||||||
utils.isHash = function(value) {
|
utils.isHash = function(value) {
|
||||||
@ -93,7 +95,15 @@ utils.toJSONL = function(obj) {
|
|||||||
var str = JSON.stringify(obj);
|
var str = JSON.stringify(obj);
|
||||||
str = str.replace(/\n/g, '');
|
str = str.replace(/\n/g, '');
|
||||||
return str + '\n';
|
return str + '\n';
|
||||||
}
|
};
|
||||||
|
|
||||||
|
utils.normalizeTimeStamp = function(addressArg) {
|
||||||
|
var addresses = [addressArg];
|
||||||
|
if (Array.isArray(addressArg)) {
|
||||||
|
addresses = addressArg;
|
||||||
|
}
|
||||||
|
return addresses;
|
||||||
|
};
|
||||||
|
|
||||||
utils.normalizeTimeStamp = function(value) {
|
utils.normalizeTimeStamp = function(value) {
|
||||||
if (value > 0xffffffff) {
|
if (value > 0xffffffff) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user