added more blocks stuff.
This commit is contained in:
parent
443688face
commit
ec826b4940
@ -330,16 +330,23 @@ Bitcoin.prototype.start = function(callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!self.options.connect) {
|
if (!self.options.connect) {
|
||||||
throw new Error('A "connect" array is required in the bitcoind service configuration.');
|
log.error('A "connect" array is required in the bitcoind service configuration.');
|
||||||
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.nodes = self.options.connect.map(self._connectProcess.bind(self));
|
self.nodes = self.options.connect.map(self._connectProcess.bind(self));
|
||||||
|
|
||||||
if (self.nodes.length === 0) {
|
if (self.nodes.length === 0) {
|
||||||
throw new Error('Could not connect to any servers in connect array.');
|
log.error('Could not connect to any servers in connect array.');
|
||||||
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
self._initChain(function() {
|
async.retry({ interval: 2000, times: 30 }, self._initChain.bind(this), function(err) {
|
||||||
|
|
||||||
|
if(err) {
|
||||||
|
log.error(err.message);
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
log.info('Bitcoin Daemon Ready');
|
log.info('Bitcoin Daemon Ready');
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
@ -18,6 +18,10 @@ function BlockStream(highWaterMark, sync) {
|
|||||||
this.lastEmittedHash = this.dbTip.hash;
|
this.lastEmittedHash = this.dbTip.hash;
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
|
var self = this;
|
||||||
|
self.block.on('reorg', function() {
|
||||||
|
self.push(null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(BlockStream, Readable);
|
inherits(BlockStream, Readable);
|
||||||
@ -112,9 +116,6 @@ BlockHandler.prototype._setupStreams = function() {
|
|||||||
|
|
||||||
processSerial.on('finish', self._onFinish.bind(self));
|
processSerial.on('finish', self._onFinish.bind(self));
|
||||||
|
|
||||||
self.block.once('reorg', function() {
|
|
||||||
blockStream.push(null);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BlockHandler.prototype._onFinish = function() {
|
BlockHandler.prototype._onFinish = function() {
|
||||||
|
|||||||
@ -472,7 +472,7 @@ BlockService.prototype._loadTips = function(callback) {
|
|||||||
|
|
||||||
hash = tipData.slice(0, 32).toString('hex');
|
hash = tipData.slice(0, 32).toString('hex');
|
||||||
|
|
||||||
self.bitcoind.getBlock(hash, function(err, block) {
|
self._getBlock(hash, function(err, block) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
@ -500,9 +500,8 @@ BlockService.prototype._loadTips = function(callback) {
|
|||||||
|
|
||||||
BlockService.prototype._detectReorg = function(blocks, callback) {
|
BlockService.prototype._detectReorg = function(blocks, callback) {
|
||||||
|
|
||||||
var self = this;
|
var tipHash = this.reorgHash || this.tip.hash;
|
||||||
var tipHash = self.reorgHash || self.tip.hash;
|
var tipHeight = this.reorgHeight || this.tip.__height;
|
||||||
var tipHeight = self.reorgHeight || self.tip.__height;
|
|
||||||
var forkedHash;
|
var forkedHash;
|
||||||
|
|
||||||
for(var i = 0; i < blocks.length; i++) {
|
for(var i = 0; i < blocks.length; i++) {
|
||||||
@ -511,24 +510,16 @@ BlockService.prototype._detectReorg = function(blocks, callback) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prevHash = utils.reverseBufferToString(blocks[i].header.prevHash);
|
if (utils.reverseBufferToString(blocks[i].header.prevHash) !== tipHash) {
|
||||||
if (prevHash !== tipHash) {
|
return this._handleReorg(forkedHash, callback.bind(this, tipHash, tipHeight));
|
||||||
forkedHash = blocks[i].hash;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tipHash = blocks[i].hash;
|
tipHash = blocks[i].hash;
|
||||||
tipHeight = blocks[i].__height;
|
tipHeight = blocks[i].__height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forkedHash) {
|
this.reorgHash = tipHash;
|
||||||
return self._handleReorg(forkedHash, function() {
|
this.reorgHeight = tipHeight;
|
||||||
callback(tipHash, tipHeight);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.reorgHash = tipHash;
|
|
||||||
self.reorgHeight = tipHeight;
|
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,8 @@ function Encoding(servicePrefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
||||||
|
|
||||||
|
assert(address, 'address is required');
|
||||||
var buffers = [this.servicePrefix, this.nonP2PKPrefix];
|
var buffers = [this.servicePrefix, this.nonP2PKPrefix];
|
||||||
|
|
||||||
var addressSizeBuffer = new Buffer(1);
|
var addressSizeBuffer = new Buffer(1);
|
||||||
@ -20,17 +22,22 @@ Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
|||||||
buffers.push(addressSizeBuffer);
|
buffers.push(addressSizeBuffer);
|
||||||
buffers.push(addressBuffer);
|
buffers.push(addressBuffer);
|
||||||
|
|
||||||
var txidBuffer = new Buffer(txid || new Array(65).join('0'), 'hex');
|
if (txid) {
|
||||||
buffers.push(txidBuffer);
|
var txidBuffer = new Buffer(txid, 'hex');
|
||||||
|
buffers.push(txidBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
var outputIndexBuffer = new Buffer(4);
|
if (outputIndex >= 0) {
|
||||||
outputIndexBuffer.writeUInt32BE(outputIndex || 0);
|
var outputIndexBuffer = new Buffer(4);
|
||||||
buffers.push(outputIndexBuffer);
|
outputIndexBuffer.writeUInt32BE(outputIndex);
|
||||||
|
buffers.push(outputIndexBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
return Buffer.concat(buffers);
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.decodeUtxoIndexKey = function(buffer) {
|
Encoding.prototype.decodeUtxoIndexKey = function(buffer) {
|
||||||
|
|
||||||
var reader = new BufferReader(buffer);
|
var reader = new BufferReader(buffer);
|
||||||
reader.read(3);
|
reader.read(3);
|
||||||
|
|
||||||
@ -44,17 +51,21 @@ Encoding.prototype.decodeUtxoIndexKey = function(buffer) {
|
|||||||
txid: txid,
|
txid: txid,
|
||||||
outputIndex: outputIndex
|
outputIndex: outputIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.encodeUtxoIndexValue = function(height, satoshis, scriptBuffer) {
|
Encoding.prototype.encodeUtxoIndexValue = function(height, satoshis, scriptBuffer) {
|
||||||
|
|
||||||
var heightBuffer = new Buffer(4);
|
var heightBuffer = new Buffer(4);
|
||||||
heightBuffer.writeUInt32BE(height);
|
heightBuffer.writeUInt32BE(height);
|
||||||
var satoshisBuffer = new Buffer(8);
|
var satoshisBuffer = new Buffer(8);
|
||||||
satoshisBuffer.writeDoubleBE(satoshis);
|
satoshisBuffer.writeDoubleBE(satoshis);
|
||||||
return Buffer.concat([heightBuffer, satoshisBuffer, scriptBuffer]);
|
return Buffer.concat([heightBuffer, satoshisBuffer, scriptBuffer]);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
||||||
|
|
||||||
var height = buffer.readUInt32BE();
|
var height = buffer.readUInt32BE();
|
||||||
var satoshis = buffer.readDoubleBE(4);
|
var satoshis = buffer.readDoubleBE(4);
|
||||||
var scriptBuffer = buffer.slice(12);
|
var scriptBuffer = buffer.slice(12);
|
||||||
@ -63,13 +74,13 @@ Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
|||||||
satoshis: satoshis,
|
satoshis: satoshis,
|
||||||
script: scriptBuffer
|
script: scriptBuffer
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.encodeP2PKUtxoIndexKey = function(txid, outputIndex) {
|
Encoding.prototype.encodeP2PKUtxoIndexKey = function(txid, outputIndex) {
|
||||||
var buffers = [this.servicePrefix, this.P2PKPrefix];
|
|
||||||
|
|
||||||
assert(txid && txid.length === 64, 'Txid required');
|
assert(txid, 'txid is required');
|
||||||
assert(outputIndex >= 0, 'outputIndex required');
|
var buffers = [this.servicePrefix, this.P2PKPrefix];
|
||||||
|
|
||||||
var txidBuffer = new Buffer(txid);
|
var txidBuffer = new Buffer(txid);
|
||||||
buffers.push(txidBuffer);
|
buffers.push(txidBuffer);
|
||||||
|
|||||||
@ -57,30 +57,34 @@ UtxoService.prototype.blockHandler = function(block, connect, callback) {
|
|||||||
operations = self._processOutputs(tx, outputs, block, connect).concat(operations);
|
operations = self._processOutputs(tx, outputs, block, connect).concat(operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, operations);
|
setImmediate(function() {
|
||||||
|
callback(null, operations);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
UtxoService.prototype.getUtxosForAddress = function(address, callback) {
|
UtxoService.prototype.getUtxosForAddress = function(address, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var utxos = [];
|
var utxos = [];
|
||||||
|
|
||||||
var start = self.encoding.encodeUtxoIndexKey(address);
|
var start = self.encoding.encodeUtxoIndexKey(address);
|
||||||
|
|
||||||
var stream = self.db.createReadStream({
|
var stream = self.db.createReadStream({
|
||||||
gte: start.slice(0, -36),
|
gte: start,
|
||||||
lt: Buffer.concat([ start.slice(0, -36), new Buffer('ff', 'hex') ])
|
lt: utils.getTerminalKey(start)
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('data', function(data) {
|
stream.on('data', function(data) {
|
||||||
|
|
||||||
var key = self.encoding.decodeUtxoIndexKey(data.key);
|
var key = self.encoding.decodeUtxoIndexKey(data.key);
|
||||||
var value = self.encoding.decodeUtxoIndexValue(data.value);
|
var value = self.encoding.decodeUtxoIndexValue(data.value);
|
||||||
utxos.push({
|
utxos.push({
|
||||||
txid: key.txid,
|
|
||||||
outputIndex: key.outputIndex,
|
|
||||||
address: address,
|
address: address,
|
||||||
|
txId: key.txid,
|
||||||
|
outputIndex: key.outputIndex,
|
||||||
height: value.height,
|
height: value.height,
|
||||||
satoshis: value.satoshis,
|
satoshis: value.satoshis,
|
||||||
script: value.script
|
script: value.script.toString('hex')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -149,6 +153,7 @@ UtxoService.prototype._processOutputs = function(tx, outputs, block, connect) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
|
//console.log(this.encoding.decodeUtxoIndexKey(key));
|
||||||
var operation = connect ? {
|
var operation = connect ? {
|
||||||
type: 'put',
|
type: 'put',
|
||||||
key: key,
|
key: key,
|
||||||
|
|||||||
26
lib/utils.js
26
lib/utils.js
@ -41,32 +41,6 @@ utils.parseParamsWithJSON = function(paramsArg) {
|
|||||||
return params;
|
return params;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* input: arguments passed into originating function (whoever called us)
|
|
||||||
* output: bool args are valid for encoding a key to the database
|
|
||||||
*/
|
|
||||||
utils.hasRequiredArgsForEncoding = function(args) {
|
|
||||||
function exists(arg) {
|
|
||||||
return !(arg === null || arg === undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exists(args[0])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pastArgMissing;
|
|
||||||
|
|
||||||
for(var i = 1; i < args.length; i++) {
|
|
||||||
var argMissing = exists(args[i]);
|
|
||||||
if (argMissing && pastArgMissing) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pastArgMissing = argMissing;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
utils.getTerminalKey = function(startKey) {
|
utils.getTerminalKey = function(startKey) {
|
||||||
var endKey = Buffer.from(startKey);
|
var endKey = Buffer.from(startKey);
|
||||||
endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1);
|
endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1);
|
||||||
|
|||||||
@ -78,7 +78,7 @@
|
|||||||
"istanbul": "^0.4.3",
|
"istanbul": "^0.4.3",
|
||||||
"jshint": "^2.9.2",
|
"jshint": "^2.9.2",
|
||||||
"jshint-stylish": "^2.1.0",
|
"jshint-stylish": "^2.1.0",
|
||||||
"mocha": "^2.4.5",
|
"mocha": "",
|
||||||
"proxyquire": "^1.3.1",
|
"proxyquire": "^1.3.1",
|
||||||
"rimraf": "^2.4.2",
|
"rimraf": "^2.4.2",
|
||||||
"sinon": "^1.15.4"
|
"sinon": "^1.15.4"
|
||||||
|
|||||||
@ -49,7 +49,7 @@ TestWebService.prototype.setupRoutes = function(app) {
|
|||||||
|
|
||||||
app.get('/utxo/:address', function(req, res) {
|
app.get('/utxo/:address', function(req, res) {
|
||||||
self.node.services.utxo.getUtxosForAddress(req.params.address, function(err, utxos) {
|
self.node.services.utxo.getUtxosForAddress(req.params.address, function(err, utxos) {
|
||||||
res.status(200).jsonp({ address: req.params.address, utxos: utxos });
|
res.status(200).jsonp({ utxos: utxos });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
142
regtest/utils.js
142
regtest/utils.js
@ -12,24 +12,26 @@ var Unit = bitcore.Unit;
|
|||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var PrivateKey = bitcore.PrivateKey;
|
var PrivateKey = bitcore.PrivateKey;
|
||||||
|
|
||||||
var utils = {};
|
var Utils = function(opts) {
|
||||||
|
this.opts = opts;
|
||||||
|
};
|
||||||
|
|
||||||
utils.writeConfigFile = function(fileStr, obj) {
|
Utils.prototype.writeConfigFile = function(fileStr, obj) {
|
||||||
fs.writeFileSync(fileStr, JSON.stringify(obj));
|
fs.writeFileSync(fileStr, JSON.stringify(obj));
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.toArgs = function(opts) {
|
Utils.prototype.toArgs = function(opts) {
|
||||||
return Object.keys(opts).map(function(key) {
|
return Object.keys(opts).map(function(key) {
|
||||||
return '-' + key + '=' + opts[key];
|
return '-' + key + '=' + opts[key];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.waitForService = function(task, callback) {
|
Utils.prototype.waitForService = function(task, callback) {
|
||||||
var retryOpts = { times: 20, interval: 1000 };
|
var retryOpts = { times: 20, interval: 1000 };
|
||||||
async.retry(retryOpts, task, callback);
|
async.retry(retryOpts, task, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.queryBitcoreNode = function(httpOpts, callback) {
|
Utils.prototype.queryBitcoreNode = function(httpOpts, callback) {
|
||||||
var error;
|
var error;
|
||||||
var request = http.request(httpOpts, function(res) {
|
var request = http.request(httpOpts, function(res) {
|
||||||
|
|
||||||
@ -72,25 +74,16 @@ utils.queryBitcoreNode = function(httpOpts, callback) {
|
|||||||
request.end();
|
request.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.waitForBitcoreNode = function(opts, callback) {
|
Utils.prototype.waitForBitcoreNode = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
opts.bitcore.process.stdout.on('data', function(data) {
|
|
||||||
if (opts.debug) {
|
|
||||||
console.log(data.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
opts.bitcore.process.stderr.on('data', function(data) {
|
|
||||||
console.log(data.toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
var errorFilter = function(err, res) {
|
var errorFilter = function(err, res) {
|
||||||
try {
|
try {
|
||||||
var info = JSON.parse(res);
|
var info = JSON.parse(res);
|
||||||
if (info.dbheight === opts.blockHeight &&
|
if (info.dbheight === self.opts.blockHeight &&
|
||||||
info.bitcoindheight === opts.blockHeight) {
|
info.dbheight === info.bitcoindheight &&
|
||||||
|
info.bitcoindhash === info.dbhash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -99,22 +92,22 @@ utils.waitForBitcoreNode = function(opts, callback) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var httpOpts = self.getHttpOpts(opts, { path: '/info', errorFilter: errorFilter });
|
var httpOpts = self.getHttpOpts({ path: '/info', errorFilter: errorFilter });
|
||||||
|
|
||||||
self.waitForService(self.queryBitcoreNode.bind(self, httpOpts), callback);
|
self.waitForService(self.queryBitcoreNode.bind(self, httpOpts), callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.waitForBitcoinReady = function(opts, callback) {
|
Utils.prototype.waitForBitcoinReady = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
self.waitForService(function(callback) {
|
self.waitForService(function(callback) {
|
||||||
|
|
||||||
opts.rpc.generate(opts.initialHeight, function(err, res) {
|
self.opts.rpc.generate(self.opts.initialHeight, function(err, res) {
|
||||||
|
|
||||||
if (err || (res && res.error)) {
|
if (err || (res && res.error)) {
|
||||||
return callback('keep trying');
|
return callback('keep trying');
|
||||||
}
|
}
|
||||||
opts.blockHeight += opts.initialHeight;
|
self.opts.blockHeight += self.opts.initialHeight;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
@ -129,7 +122,7 @@ utils.waitForBitcoinReady = function(opts, callback) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.initializeAndStartService = function(opts, callback) {
|
Utils.prototype.initializeAndStartService = function(opts, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -152,16 +145,36 @@ utils.initializeAndStartService = function(opts, callback) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.startBitcoreNode = function(opts, callback) {
|
Utils.prototype.startBitcoreNode = function(callback) {
|
||||||
this.initializeAndStartService(opts.bitcore, callback);
|
var self = this;
|
||||||
|
this.initializeAndStartService(self.opts.bitcore, function(err) {
|
||||||
|
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.opts.bitcore.process.stdout.on('data', function(data) {
|
||||||
|
if (self.opts.debug) {
|
||||||
|
process.stdout.write(data.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.opts.bitcore.process.stderr.on('data', function(data) {
|
||||||
|
process.stdout.write(data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
callback();
|
||||||
|
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.startBitcoind = function(opts, callback) {
|
Utils.prototype.startBitcoind = function(callback) {
|
||||||
this.initializeAndStartService(opts.bitcoin, callback);
|
this.initializeAndStartService(this.opts.bitcoin, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.unlockWallet = function(opts, callback) {
|
Utils.prototype.unlockWallet = function(callback) {
|
||||||
opts.rpc.walletPassPhrase(opts.walletPassphrase, 3000, function(err) {
|
this.opts.rpc.walletPassPhrase(this.opts.walletPassphrase, 3000, function(err) {
|
||||||
if(err && err.code !== -15) {
|
if(err && err.code !== -15) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@ -169,9 +182,10 @@ utils.unlockWallet = function(opts, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.getPrivateKeysWithABalance = function(opts, callback) {
|
Utils.prototype.getPrivateKeysWithABalance = function(callback) {
|
||||||
|
|
||||||
opts.rpc.listUnspent(function(err, res) {
|
var self = this;
|
||||||
|
self.opts.rpc.listUnspent(function(err, res) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@ -188,7 +202,7 @@ utils.getPrivateKeysWithABalance = function(opts, callback) {
|
|||||||
}
|
}
|
||||||
async.mapLimit(utxos, 8, function(utxo, callback) {
|
async.mapLimit(utxos, 8, function(utxo, callback) {
|
||||||
|
|
||||||
opts.rpc.dumpPrivKey(utxo.address, function(err, res) {
|
self.opts.rpc.dumpPrivKey(utxo.address, function(err, res) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@ -206,8 +220,9 @@ utils.getPrivateKeysWithABalance = function(opts, callback) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.generateSpendingTxs = function(opts, utxos) {
|
Utils.prototype.generateSpendingTxs = function(utxos) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
return utxos.map(function(utxo) {
|
return utxos.map(function(utxo) {
|
||||||
|
|
||||||
var toPrivKey = new PrivateKey('testnet'); //external addresses
|
var toPrivKey = new PrivateKey('testnet'); //external addresses
|
||||||
@ -218,45 +233,46 @@ utils.generateSpendingTxs = function(opts, utxos) {
|
|||||||
|
|
||||||
tx.from(utxo.utxo);
|
tx.from(utxo.utxo);
|
||||||
tx.to(toPrivKey.toAddress().toString(), satsToPrivKey);
|
tx.to(toPrivKey.toAddress().toString(), satsToPrivKey);
|
||||||
tx.fee(opts.fee);
|
tx.fee(self.opts.fee);
|
||||||
tx.change(changePrivKey.toAddress().toString());
|
tx.change(changePrivKey.toAddress().toString());
|
||||||
tx.sign(utxo.privKey);
|
tx.sign(utxo.privKey);
|
||||||
|
|
||||||
opts.walletPrivKeys.push(changePrivKey);
|
self.opts.walletPrivKeys.push(changePrivKey);
|
||||||
opts.satoshisReceived += Unit.fromBTC(utxo.utxo.amount).toSatoshis() - (satsToPrivKey + opts.fee);
|
self.opts.satoshisReceived += Unit.fromBTC(utxo.utxo.amount).toSatoshis() - (satsToPrivKey + self.opts.fee);
|
||||||
return tx;
|
return tx;
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.setupInitialTxs = function(opts, callback) {
|
Utils.prototype.setupInitialTxs = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
self.getPrivateKeysWithABalance(opts, function(err, utxos) {
|
self.getPrivateKeysWithABalance(function(err, utxos) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
opts.initialTxs = self.generateSpendingTxs(opts, utxos);
|
self.opts.initialTxs = self.generateSpendingTxs(utxos);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.sendTxs = function(opts, callback) {
|
Utils.prototype.sendTxs = function(callback) {
|
||||||
async.eachOfSeries(opts.initialTxs, this.sendTx.bind(this, opts), callback);
|
async.eachOfSeries(this.opts.initialTxs, this.sendTx.bind(this), callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.sendTx = function(opts, tx, index, callback) {
|
Utils.prototype.sendTx = function(tx, index, callback) {
|
||||||
|
|
||||||
opts.rpc.sendRawTransaction(tx.serialize(), function(err) {
|
var self = this;
|
||||||
|
self.opts.rpc.sendRawTransaction(tx.serialize(), function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
var mod = index % 2;
|
var mod = index % 2;
|
||||||
if (mod === 1) {
|
if (mod === 1) {
|
||||||
opts.blockHeight++;
|
self.opts.blockHeight++;
|
||||||
opts.rpc.generate(1, callback);
|
self.opts.rpc.generate(1, callback);
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -264,7 +280,7 @@ utils.sendTx = function(opts, tx, index, callback) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.getHttpOpts = function(opts, httpOpts) {
|
Utils.prototype.getHttpOpts = function(httpOpts) {
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
path: httpOpts.path,
|
path: httpOpts.path,
|
||||||
method: httpOpts.method || 'GET',
|
method: httpOpts.method || 'GET',
|
||||||
@ -274,28 +290,28 @@ utils.getHttpOpts = function(opts, httpOpts) {
|
|||||||
'Content-Length': httpOpts.length || 0
|
'Content-Length': httpOpts.length || 0
|
||||||
},
|
},
|
||||||
errorFilter: httpOpts.errorFilter
|
errorFilter: httpOpts.errorFilter
|
||||||
}, opts.bitcore.httpOpts);
|
}, this.opts.bitcore.httpOpts);
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.registerWallet = function(opts, callback) {
|
Utils.prototype.registerWallet = function(callback) {
|
||||||
|
|
||||||
var httpOpts = this.getHttpOpts(opts, { path: '/wallet-api/wallets/' + opts.walletId, method: 'POST' });
|
var httpOpts = this.getHttpOpts(this.opts, { path: '/wallet-api/wallets/' + this.opts.walletId, method: 'POST' });
|
||||||
this.queryBitcoreNode(httpOpts, callback);
|
this.queryBitcoreNode(httpOpts, callback);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.uploadWallet = function(opts, callback) {
|
Utils.prototype.uploadWallet = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var addresses = JSON.stringify(opts.walletPrivKeys.map(function(privKey) {
|
var addresses = JSON.stringify(self.opts.walletPrivKeys.map(function(privKey) {
|
||||||
if (privKey.privKey) {
|
if (privKey.privKey) {
|
||||||
return privKey.pubKey.toString();
|
return privKey.pubKey.toString();
|
||||||
}
|
}
|
||||||
return privKey.toAddress().toString();
|
return privKey.toAddress().toString();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var httpOpts = self.getHttpOpts(opts, {
|
var httpOpts = self.getHttpOpts(self.opts, {
|
||||||
path: '/wallet-api/wallets/' + opts.walletId + '/addresses',
|
path: '/wallet-api/wallets/' + self.opts.walletId + '/addresses',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: addresses,
|
body: addresses,
|
||||||
length: addresses.length
|
length: addresses.length
|
||||||
@ -309,7 +325,7 @@ utils.uploadWallet = function(opts, callback) {
|
|||||||
|
|
||||||
Object.keys(job).should.deep.equal(['jobId']);
|
Object.keys(job).should.deep.equal(['jobId']);
|
||||||
|
|
||||||
var httpOpts = self.getHttpOpts(opts, { path: '/wallet-api/jobs/' + job.jobId });
|
var httpOpts = self.getHttpOpts(self.opts, { path: '/wallet-api/jobs/' + job.jobId });
|
||||||
|
|
||||||
async.retry({ times: 10, interval: 1000 }, function(next) {
|
async.retry({ times: 10, interval: 1000 }, function(next) {
|
||||||
self.queryBitcoreNode(httpOpts, function(err, res) {
|
self.queryBitcoreNode(httpOpts, function(err, res) {
|
||||||
@ -333,12 +349,12 @@ utils.uploadWallet = function(opts, callback) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.getListOfTxs = function(opts, callback) {
|
Utils.prototype.getListOfTxs = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var end = Date.now() + 86400000;
|
var end = Date.now() + 86400000;
|
||||||
var httpOpts = self.getHttpOpts(opts, {
|
var httpOpts = self.getHttpOpts(self.opts, {
|
||||||
path: '/wallet-api/wallets/' + opts.walletId + '/transactions?start=0&end=' + end });
|
path: '/wallet-api/wallets/' + self.opts.walletId + '/transactions?start=0&end=' + end });
|
||||||
|
|
||||||
self.queryBitcoreNode(httpOpts, function(err, res) {
|
self.queryBitcoreNode(httpOpts, function(err, res) {
|
||||||
if(err) {
|
if(err) {
|
||||||
@ -353,7 +369,7 @@ utils.getListOfTxs = function(opts, callback) {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var map = opts.initialTxs.map(function(tx) {
|
var map = self.opts.initialTxs.map(function(tx) {
|
||||||
return tx.serialize();
|
return tx.serialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -363,15 +379,15 @@ utils.getListOfTxs = function(opts, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
map.length.should.equal(0);
|
map.length.should.equal(0);
|
||||||
results.length.should.equal(opts.initialTxs.length);
|
results.length.should.equal(self.opts.initialTxs.length);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.cleanup = function(opts, callback) {
|
Utils.prototype.cleanup = function(callback) {
|
||||||
opts.bitcore.process.kill();
|
this.opts.bitcore.process.kill();
|
||||||
opts.bitcoin.process.kill();
|
this.opts.bitcoin.process.kill();
|
||||||
setTimeout(callback, 2000);
|
setTimeout(callback, 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = utils;
|
module.exports = Utils;
|
||||||
|
|||||||
146
regtest/utxo.js
146
regtest/utxo.js
@ -5,10 +5,16 @@ var expect = chai.expect;
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var BitcoinRPC = require('bitcoind-rpc');
|
var BitcoinRPC = require('bitcoind-rpc');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var utils = require('./utils');
|
var Utils = require('./utils');
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
var bitcore = require('bitcore-lib');
|
||||||
|
var PrivateKey = bitcore.PrivateKey;
|
||||||
|
var Transaction = bitcore.Transaction;
|
||||||
|
var Output = bitcore.Transaction.Output;
|
||||||
|
var Script = bitcore.Script;
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
var debug = true;
|
var debug = false;
|
||||||
var bitcoreDataDir = '/tmp/bitcore';
|
var bitcoreDataDir = '/tmp/bitcore';
|
||||||
var bitcoinDataDir = '/tmp/bitcoin';
|
var bitcoinDataDir = '/tmp/bitcoin';
|
||||||
|
|
||||||
@ -31,7 +37,7 @@ var bitcoin = {
|
|||||||
rpcpassword: rpcConfig.pass,
|
rpcpassword: rpcConfig.pass,
|
||||||
rpcport: rpcConfig.port,
|
rpcport: rpcConfig.port,
|
||||||
zmqpubrawtx: 'tcp://127.0.0.1:38332',
|
zmqpubrawtx: 'tcp://127.0.0.1:38332',
|
||||||
zmqpubhashblock: 'tcp://127.0.0.1:38332'
|
zmqpubrawblock: 'tcp://127.0.0.1:38332'
|
||||||
},
|
},
|
||||||
datadir: bitcoinDataDir,
|
datadir: bitcoinDataDir,
|
||||||
exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind
|
exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind
|
||||||
@ -104,57 +110,161 @@ var opts = {
|
|||||||
initialHeight: 150
|
initialHeight: 150
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var utils = new Utils(opts);
|
||||||
|
|
||||||
describe('Utxo Operations', function() {
|
describe('Utxo Operations', function() {
|
||||||
|
|
||||||
this.timeout(60000);
|
this.timeout(60000);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
|
||||||
after(function(done) {
|
after(function(done) {
|
||||||
utils.cleanup(self.opts, done);
|
utils.cleanup(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
self.opts = Object.assign({}, opts);
|
|
||||||
async.series([
|
async.series([
|
||||||
utils.startBitcoind.bind(utils, self.opts),
|
utils.startBitcoind.bind(utils),
|
||||||
utils.waitForBitcoinReady.bind(utils, self.opts),
|
utils.waitForBitcoinReady.bind(utils),
|
||||||
utils.unlockWallet.bind(utils, self.opts),
|
utils.unlockWallet.bind(utils),
|
||||||
utils.setupInitialTxs.bind(utils, self.opts),
|
utils.setupInitialTxs.bind(utils),
|
||||||
utils.sendTxs.bind(utils, self.opts),
|
utils.sendTxs.bind(utils),
|
||||||
utils.startBitcoreNode.bind(utils, self.opts),
|
utils.startBitcoreNode.bind(utils),
|
||||||
utils.waitForBitcoreNode.bind(utils, self.opts)
|
utils.waitForBitcoreNode.bind(utils)
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should index utxos', function(done) {
|
it('should index utxos', function(done) {
|
||||||
async.mapLimit(opts.walletPrivKeys, 12, function(privKey, next) {
|
async.mapSeries(opts.walletPrivKeys, function(privKey, next) {
|
||||||
|
|
||||||
var address = privKey.toAddress().toString();
|
var address = privKey.toAddress().toString();
|
||||||
utils.queryBitcoreNode(Object.assign({
|
|
||||||
|
var httpOpts = Object.assign({
|
||||||
path: '/test/utxo/' + address
|
path: '/test/utxo/' + address
|
||||||
}, bitcore.httpOpts), function(err, res) {
|
}, bitcore.httpOpts);
|
||||||
|
|
||||||
|
utils.queryBitcoreNode(httpOpts, function(err, res) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = JSON.parse(res);
|
res = JSON.parse(res);
|
||||||
expect(res.address).to.equal(address);
|
|
||||||
expect(res.utxos.length).equal(1);
|
expect(res.utxos.length).equal(1);
|
||||||
expect(Object.keys(res.utxos[0])).to.deep.equal([ 'txid', 'outputIndex', 'address', 'height', 'satoshis', 'script' ]);
|
expect(res.utxos[0].address).to.equal(address);
|
||||||
|
expect(Object.keys(res.utxos[0])).to.deep.equal([
|
||||||
|
'address',
|
||||||
|
'txId',
|
||||||
|
'outputIndex',
|
||||||
|
'height',
|
||||||
|
'satoshis',
|
||||||
|
'script' ]);
|
||||||
next(null, res.utxos);
|
next(null, res.utxos);
|
||||||
|
|
||||||
});
|
});
|
||||||
}, function(err, utxos) {
|
}, function(err, results) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.utxos = _.flatten(results);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should store p2pk and p2pkh utxos', function(done) {
|
||||||
|
|
||||||
|
var pk1 = new PrivateKey('testnet');
|
||||||
|
var pk2 = new PrivateKey('testnet');
|
||||||
|
|
||||||
|
var satoshis = 100000000;
|
||||||
|
var utxo = self.utxos[0];
|
||||||
|
|
||||||
|
var tx = new Transaction();
|
||||||
|
|
||||||
|
tx.from(utxo);
|
||||||
|
|
||||||
|
tx.addOutput(new Output({
|
||||||
|
satoshis: satoshis,
|
||||||
|
script: Script.buildPublicKeyOut(pk1.publicKey)
|
||||||
|
}));
|
||||||
|
|
||||||
|
tx.change(pk2.toAddress().toString());
|
||||||
|
tx.sign(opts.walletPrivKeys[0]);
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
|
||||||
|
function(next) {
|
||||||
|
utils.sendTx(tx, 1, function(err) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
function(next) {
|
||||||
|
|
||||||
|
utils.waitForBitcoreNode(function(err) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
function(next) {
|
||||||
|
|
||||||
|
var address = pk1.publicKey.toString('hex');
|
||||||
|
var httpOpts = Object.assign({
|
||||||
|
path: '/test/utxo/' + address
|
||||||
|
}, bitcore.httpOpts);
|
||||||
|
|
||||||
|
utils.queryBitcoreNode(httpOpts, function(err, res) {
|
||||||
|
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = JSON.parse(res);
|
||||||
|
expect(res.utxos.length).to.equal(1);
|
||||||
|
expect(res.utxos[0].address).to.equal(address);
|
||||||
|
expect(res.utxos[0].satoshis).to.equal(satoshis);
|
||||||
|
expect(Object.keys(res.utxos[0])).to.deep.equal([
|
||||||
|
'address',
|
||||||
|
'txId',
|
||||||
|
'outputIndex',
|
||||||
|
'height',
|
||||||
|
'satoshis',
|
||||||
|
'script' ]);
|
||||||
|
|
||||||
|
next();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
], function(err) {
|
||||||
|
|
||||||
|
if(err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
done();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user