wip on regtest
This commit is contained in:
parent
c7acf254b4
commit
6634b4feae
@ -160,7 +160,6 @@ AddressService.prototype.concurrentBlockHandler = function(block, connectBlock,
|
||||
}
|
||||
|
||||
var address = self.getAddressString(script);
|
||||
|
||||
if(!address) {
|
||||
continue;
|
||||
}
|
||||
@ -1172,7 +1171,7 @@ AddressService.prototype.getAddressTxidsWithHeights = function(address, options,
|
||||
var txids = {};
|
||||
|
||||
var start = self._encoding.encodeAddressIndexKey(address, opts.start || 0); //the start and end must be the same length
|
||||
var end = Buffer.concat([ start.slice(0, -36), new Buffer((opts.end || 0xffffffff), 'hex') ]);
|
||||
var end = Buffer.concat([ start.slice(0, -36), new Buffer((opts.end || 'ffffffff'), 'hex') ]);
|
||||
|
||||
var stream = self.store.createKeyStream({
|
||||
gte: start,
|
||||
@ -1181,9 +1180,6 @@ AddressService.prototype.getAddressTxidsWithHeights = function(address, options,
|
||||
|
||||
var streamErr = null;
|
||||
|
||||
stream.on('close', function(buffer) {
|
||||
});
|
||||
|
||||
stream.on('data', function(buffer) {
|
||||
var key = self._encoding.decodeAddressIndexKey(buffer);
|
||||
txids[key.txid] = key.height;
|
||||
|
||||
@ -316,7 +316,7 @@ ProcessConcurrent.prototype._transform = function(block, enc, callback) {
|
||||
self.blockCount++;
|
||||
self.operations = self.operations.concat(operations);
|
||||
|
||||
if(self.blockCount >= 1) { //self.operations.length >= 100) {
|
||||
if(self.blockCount >= 1) {
|
||||
self.operations.push(self.db.getConcurrentTipOperation(block, true));
|
||||
var obj = {
|
||||
concurrentTip: block,
|
||||
|
||||
@ -29,7 +29,7 @@ function Encoding(servicePrefix) {
|
||||
};
|
||||
}
|
||||
|
||||
Encoding.prototype.encodeWalletTransactionKey = function(walletId, height) {
|
||||
Encoding.prototype.encodeWalletTransactionKey = function(walletId, height, txid) {
|
||||
var buffers = [this.servicePrefix, this.subKeyMap.transaction.buffer];
|
||||
|
||||
var walletIdSizeBuffer = new Buffer(1);
|
||||
@ -43,6 +43,9 @@ Encoding.prototype.encodeWalletTransactionKey = function(walletId, height) {
|
||||
heightBuffer.writeUInt32BE(height || 0);
|
||||
buffers.push(heightBuffer);
|
||||
|
||||
var txidBuffer = new Buffer((txid || new Array(65).join('0')), 'hex');
|
||||
buffers.push(txidBuffer);
|
||||
|
||||
return Buffer.concat(buffers);
|
||||
};
|
||||
|
||||
@ -54,20 +57,15 @@ Encoding.prototype.decodeWalletTransactionKey = function(buffer) {
|
||||
var walletId = reader.read(walletSize).toString('utf8');
|
||||
var height = reader.readUInt32BE();
|
||||
|
||||
var txid = reader.read(32);
|
||||
|
||||
return {
|
||||
walletId: walletId,
|
||||
height: height
|
||||
height: height,
|
||||
txid: txid
|
||||
};
|
||||
};
|
||||
|
||||
Encoding.prototype.encodeWalletTransactionValue = function(txid) {
|
||||
return new Buffer(txid, 'hex');
|
||||
};
|
||||
|
||||
Encoding.prototype.decodeWalletTransactionValue = function(buffer) {
|
||||
return buffer.toString('hex');
|
||||
};
|
||||
|
||||
Encoding.prototype.encodeWalletUtxoKey = function(walletId, txid, outputIndex) {
|
||||
var buffers = [this.servicePrefix, this.subKeyMap.utxo.buffer];
|
||||
|
||||
|
||||
@ -257,11 +257,14 @@ WalletService.prototype.concurrentBlockHandler = function(block, connectBlock, c
|
||||
var operations = [];
|
||||
|
||||
for(var i = 0; i < txs.length; i++) {
|
||||
|
||||
var tx = txs[i];
|
||||
|
||||
var inputs = tx.inputs;
|
||||
var outputs = tx.outputs;
|
||||
|
||||
for (var outputIndex = 0; outputIndex < outputs.length; outputIndex++) {
|
||||
|
||||
var output = outputs[outputIndex];
|
||||
|
||||
var script = output.script;
|
||||
@ -283,8 +286,7 @@ WalletService.prototype.concurrentBlockHandler = function(block, connectBlock, c
|
||||
var walletId = walletIds[j];
|
||||
operations.push({
|
||||
type: action,
|
||||
key: self._encoding.encodeWalletTransactionKey(walletId, block.__height),
|
||||
value: self._encoding.encodeWalletTransactionValue(tx.id)
|
||||
key: self._encoding.encodeWalletTransactionKey(walletId, block.__height, tx.id)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -314,8 +316,7 @@ WalletService.prototype.concurrentBlockHandler = function(block, connectBlock, c
|
||||
var inputWalletId = inputWalletIds[inputIndex];
|
||||
operations.push({
|
||||
type: action,
|
||||
key: self._encoding.encodeWalletTransactionKey(inputWalletId, block.__height),
|
||||
value: self._encoding.encodeWalletTransactionValue(tx.id)
|
||||
key: self._encoding.encodeWalletTransactionKey(inputWalletId, block.__height, tx.id)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -456,6 +457,21 @@ WalletService.prototype._endpointRemoveWallet = function() {
|
||||
};
|
||||
};
|
||||
|
||||
WalletService.prototype._endpointRemoveAllWallets = function() {
|
||||
var self = this;
|
||||
return function(req, res) {
|
||||
|
||||
self._removeAllWallets(function(err, numRecords) {
|
||||
if(err) {
|
||||
return utils.sendError(err, res);
|
||||
}
|
||||
res.status(200).jsonp({
|
||||
numberRemoved: numRecords
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
WalletService.prototype._endpointGetAddresses = function() {
|
||||
var self = this;
|
||||
return function(req, res) {
|
||||
@ -583,7 +599,6 @@ WalletService.prototype._endpointPostAddresses = function() {
|
||||
WalletService.prototype._endpointGetTransactions = function() {
|
||||
var self = this;
|
||||
return function(req, res) {
|
||||
|
||||
var walletId = req.params.walletId;
|
||||
|
||||
self._processStartEndOptions(req, function(err, heights) {
|
||||
@ -770,9 +785,11 @@ WalletService.prototype._getTransactions = function(walletId, options, callback)
|
||||
}
|
||||
|
||||
if (!self._cache.peek(key)) {
|
||||
var stream = self.store.createReadStream({
|
||||
gte: self._encoding.encodeWalletTransactionKey(walletId, opts.start),
|
||||
lt: self._encoding.encodeWalletTransactionKey(walletId, opts.end)
|
||||
var start = self._encoding.encodeWalletTransactionKey(walletId, opts.start);
|
||||
var end = self._encoding.encodeWalletTransactionKey(walletId, opts.end);
|
||||
var stream = self.store.createKeyStream({
|
||||
gte: start,
|
||||
lt: end
|
||||
});
|
||||
|
||||
var streamErr;
|
||||
@ -781,7 +798,7 @@ WalletService.prototype._getTransactions = function(walletId, options, callback)
|
||||
});
|
||||
|
||||
stream.on('data', function(data) {
|
||||
txids.push(self._encoding.decodeWalletTransactionValue(data.value));
|
||||
txids.push(self._encoding.decodeWalletTransactionKey(data).txid);
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
@ -845,6 +862,37 @@ WalletService.prototype._removeWallet = function(walletId, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
WalletService.prototype._removeAllWallets = function(callback) {
|
||||
var self = this;
|
||||
var operations = [];
|
||||
|
||||
var start = self._encoding.servicePrefix;
|
||||
var end = new Buffer.concat([ start, new Buffer('ff', 'hex') ]);
|
||||
|
||||
var stream = self.store.createKeyStream({
|
||||
gte: start,
|
||||
lte: end
|
||||
});
|
||||
|
||||
var streamErr = null;
|
||||
stream.on('error', function(err) {
|
||||
streamErr = err;
|
||||
});
|
||||
|
||||
stream.on('data', function(data) {
|
||||
operations.push({ type: 'del', key: data });
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
self.store.batch(operations, function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, operations.length);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
WalletService.prototype._getAddresses = function(walletId, callback) {
|
||||
var self = this;
|
||||
var key = self._encoding.encodeWalletAddressesKey(walletId);
|
||||
@ -946,6 +994,7 @@ WalletService.prototype._importAddresses = function(walletId, addresses, jobId,
|
||||
job.projectedendtime = now + (now - job.starttime);
|
||||
|
||||
var operations = results[0].concat(results[1]);
|
||||
|
||||
operations.push({
|
||||
type: 'put',
|
||||
key: self._encoding.encodeWalletAddressesKey(walletId),
|
||||
@ -1042,11 +1091,12 @@ WalletService.prototype._getTxidIndexOperations = function(walletId, addresses,
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
//there is a big problem when we have multiple txs for our wallet for the same height!
|
||||
|
||||
var operations = Object.keys(txids).map(function(txid) {
|
||||
return {
|
||||
type: 'put',
|
||||
key: self._encoding.encodeWalletTransactionKey(walletId, txids[txid]),
|
||||
value: self._encoding.encodeWalletTransactionValue(txid)
|
||||
key: self._encoding.encodeWalletTransactionKey(walletId, txids[txid], txid)
|
||||
};
|
||||
});
|
||||
|
||||
@ -1069,7 +1119,7 @@ WalletService.prototype._storeBalance = function(walletId, balance, callback) {
|
||||
WalletService.prototype._processStartEndOptions = function(req, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!(req.query.start && req.query.start < (500 * 1E6))) {
|
||||
if (req.query.start && req.query.end) {
|
||||
|
||||
var heights = [];
|
||||
self.node.services.timestamp.getBlockHeights([
|
||||
@ -1099,9 +1149,11 @@ WalletService.prototype._processStartEndOptions = function(req, callback) {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
||||
setImmediate(function() {
|
||||
callback(null, [req.query.start, req.query.end]);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@ -1183,6 +1235,9 @@ WalletService.prototype.setupRoutes = function(app) {
|
||||
app.delete('/wallets/:walletId',
|
||||
s._endpointRemoveWallet()
|
||||
);
|
||||
app.delete('/wallets/',
|
||||
s._endpointRemoveAllWallets()
|
||||
);
|
||||
app.put('/wallets/:walletId/addresses',
|
||||
s._endpointPutAddresses()
|
||||
);
|
||||
|
||||
467
regtest/v4/bitcoind.js
Normal file
467
regtest/v4/bitcoind.js
Normal file
@ -0,0 +1,467 @@
|
||||
'use strict';
|
||||
|
||||
// To run the tests: $ mocha -R spec regtest/bitcoind.js
|
||||
|
||||
var path = require('path');;
|
||||
var index = require('../..');
|
||||
var log = index.log;
|
||||
|
||||
var chai = require('chai');
|
||||
var bitcore = require('bitcore-lib');
|
||||
var BN = bitcore.crypto.BN;
|
||||
var async = require('async');
|
||||
var rimraf = require('rimraf');
|
||||
var bitcoind;
|
||||
|
||||
/* jshint unused: false */
|
||||
var should = chai.should();
|
||||
var assert = chai.assert;
|
||||
var sinon = require('sinon');
|
||||
var BitcoinRPC = require('bitcoind-rpc');
|
||||
var transactionData = [];
|
||||
var blockHashes = [];
|
||||
var utxos;
|
||||
var client;
|
||||
var coinbasePrivateKey;
|
||||
var privateKey = bitcore.PrivateKey();
|
||||
var destKey = bitcore.PrivateKey();
|
||||
|
||||
describe('Bitcoind Functionality', function() {
|
||||
|
||||
before(function(done) {
|
||||
this.timeout(60000);
|
||||
|
||||
// Add the regtest network
|
||||
bitcore.Networks.enableRegtest();
|
||||
var regtestNetwork = bitcore.Networks.get('regtest');
|
||||
var config = { rpcprotocol: 'http', rpcport: 18332, rpcuser: 'bitcoin', rpcpassword: 'local321', zmqpubrawtx: 'tcp://127.0.0.1:38332' };
|
||||
|
||||
bitcoind = require('../..').services.Bitcoin({
|
||||
connect: [ config ],
|
||||
node: {
|
||||
network: regtestNetwork,
|
||||
getNetworkName: function() {
|
||||
return 'regtest';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bitcoind.on('error', function(err) {
|
||||
log.error('error="%s"', err.message);
|
||||
});
|
||||
|
||||
bitcoind.start(function() {
|
||||
log.info('Bitcoind started');
|
||||
|
||||
client = new BitcoinRPC({
|
||||
protocol: config.rpcprotocol || 'http',
|
||||
host: config.rpchost || '127.0.0.1',
|
||||
port: config.rpcport,
|
||||
user: config.rpcuser,
|
||||
pass: config.rpcpassword,
|
||||
rejectUnauthorized: true
|
||||
});
|
||||
|
||||
setImmediate(function() {
|
||||
client.generate(150, function(err, response) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
blockHashes = response.result;
|
||||
|
||||
log.info('Preparing test data...');
|
||||
|
||||
// Get all of the unspent outputs
|
||||
client.listUnspent(0, 150, function(err, response) {
|
||||
utxos = response.result;
|
||||
|
||||
async.mapSeries(utxos, function(utxo, next) {
|
||||
async.series([
|
||||
function(finished) {
|
||||
// Load all of the transactions for later testing
|
||||
client.getTransaction(utxo.txid, function(err, txresponse) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
// add to the list of transactions for testing later
|
||||
transactionData.push(txresponse.result.hex);
|
||||
finished();
|
||||
});
|
||||
},
|
||||
function(finished) {
|
||||
// Get the private key for each utxo
|
||||
client.dumpPrivKey(utxo.address, function(err, privresponse) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
utxo.privateKeyWIF = privresponse.result;
|
||||
finished();
|
||||
});
|
||||
}
|
||||
], next);
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
this.timeout(60000);
|
||||
bitcoind.node.stopping = true;
|
||||
bitcoind.stop(function(err, result) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('get blocks by hash', function() {
|
||||
|
||||
[0,1,2,3,5,6,7,8,9].forEach(function(i) {
|
||||
it('generated block ' + i, function(done) {
|
||||
bitcoind.getBlock(blockHashes[i], function(err, block) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
should.exist(block);
|
||||
block.hash.should.equal(blockHashes[i]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get blocks as buffers', function() {
|
||||
[0,1,2,3,5,6,7,8,9].forEach(function(i) {
|
||||
it('generated block ' + i, function(done) {
|
||||
bitcoind.getRawBlock(blockHashes[i], function(err, block) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
should.exist(block);
|
||||
(block instanceof Buffer).should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get errors as error instances', function() {
|
||||
it('will wrap an rpc into a javascript error', function(done) {
|
||||
bitcoind.client.getBlock(1000000000, function(err, response) {
|
||||
var error = bitcoind._wrapRPCError(err);
|
||||
(error instanceof Error).should.equal(true);
|
||||
error.message.should.equal(err.message);
|
||||
error.code.should.equal(err.code);
|
||||
should.exist(error.stack);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get blocks by height', function() {
|
||||
|
||||
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
|
||||
it('generated block ' + i, function(done) {
|
||||
// add the genesis block
|
||||
var height = i + 1;
|
||||
bitcoind.getBlock(i + 1, function(err, block) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
should.exist(block);
|
||||
block.hash.should.equal(blockHashes[i]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('will get error with number greater than tip', function(done) {
|
||||
bitcoind.getBlock(1000000000, function(err, response) {
|
||||
should.exist(err);
|
||||
err.code.should.equal(-8);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('get transactions by hash', function() {
|
||||
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
|
||||
it('for tx ' + i, function(done) {
|
||||
var txhex = transactionData[i];
|
||||
var tx = new bitcore.Transaction();
|
||||
tx.fromString(txhex);
|
||||
bitcoind.getTransaction(tx.hash, function(err, response) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
assert(response.toString('hex') === txhex, 'incorrect tx data result');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('will return error if the transaction does not exist', function(done) {
|
||||
var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618';
|
||||
bitcoind.getTransaction(txid, function(err, response) {
|
||||
should.exist(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get transactions as buffers', function() {
|
||||
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
|
||||
it('for tx ' + i, function(done) {
|
||||
var txhex = transactionData[i];
|
||||
var tx = new bitcore.Transaction();
|
||||
tx.fromString(txhex);
|
||||
bitcoind.getRawTransaction(tx.hash, function(err, response) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
response.should.be.instanceOf(Buffer);
|
||||
assert(response.toString('hex') === txhex, 'incorrect tx data result');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('will return error if the transaction does not exist', function(done) {
|
||||
var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618';
|
||||
bitcoind.getRawTransaction(txid, function(err, response) {
|
||||
should.exist(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get block header', function() {
|
||||
var expectedWork = new BN(6);
|
||||
[1,2,3,4,5,6,7,8,9].forEach(function(i) {
|
||||
it('generate block ' + i, function(done) {
|
||||
bitcoind.getBlockHeader(blockHashes[i], function(err, blockIndex) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
should.exist(blockIndex);
|
||||
should.exist(blockIndex.chainWork);
|
||||
var work = new BN(blockIndex.chainWork, 'hex');
|
||||
work.toString(16).should.equal(expectedWork.toString(16));
|
||||
expectedWork = expectedWork.add(new BN(2));
|
||||
should.exist(blockIndex.prevHash);
|
||||
blockIndex.hash.should.equal(blockHashes[i]);
|
||||
blockIndex.prevHash.should.equal(blockHashes[i - 1]);
|
||||
blockIndex.height.should.equal(i + 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('will get null prevHash for the genesis block', function(done) {
|
||||
bitcoind.getBlockHeader(0, function(err, header) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
should.exist(header);
|
||||
should.equal(header.prevHash, undefined);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('will get error for block not found', function(done) {
|
||||
bitcoind.getBlockHeader('notahash', function(err, header) {
|
||||
should.exist(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get block index by height', function() {
|
||||
var expectedWork = new BN(6);
|
||||
[2,3,4,5,6,7,8,9].forEach(function(i) {
|
||||
it('generate block ' + i, function() {
|
||||
bitcoind.getBlockHeader(i, function(err, header) {
|
||||
should.exist(header);
|
||||
should.exist(header.chainWork);
|
||||
var work = new BN(header.chainWork, 'hex');
|
||||
work.toString(16).should.equal(expectedWork.toString(16));
|
||||
expectedWork = expectedWork.add(new BN(2));
|
||||
should.exist(header.prevHash);
|
||||
header.hash.should.equal(blockHashes[i - 1]);
|
||||
header.prevHash.should.equal(blockHashes[i - 2]);
|
||||
header.height.should.equal(i);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('will get error with number greater than tip', function(done) {
|
||||
bitcoind.getBlockHeader(100000, function(err, header) {
|
||||
should.exist(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('send transaction functionality', function() {
|
||||
|
||||
it('will not error and return the transaction hash', function(done) {
|
||||
|
||||
// create and sign the transaction
|
||||
var tx = bitcore.Transaction();
|
||||
tx.from(utxos[0]);
|
||||
tx.change(privateKey.toAddress());
|
||||
tx.to(destKey.toAddress(), utxos[0].amount * 1e8 - 1000);
|
||||
tx.sign(bitcore.PrivateKey.fromWIF(utxos[0].privateKeyWIF));
|
||||
|
||||
// test sending the transaction
|
||||
bitcoind.sendTransaction(tx.serialize(), function(err, hash) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
hash.should.equal(tx.hash);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('will throw an error if an unsigned transaction is sent', function(done) {
|
||||
var tx = bitcore.Transaction();
|
||||
tx.from(utxos[1]);
|
||||
tx.change(privateKey.toAddress());
|
||||
tx.to(destKey.toAddress(), utxos[1].amount * 1e8 - 1000);
|
||||
bitcoind.sendTransaction(tx.uncheckedSerialize(), function(err, hash) {
|
||||
should.exist(err);
|
||||
(err instanceof Error).should.equal(true);
|
||||
should.not.exist(hash);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('will throw an error for unexpected types (tx decode failed)', function(done) {
|
||||
var garbage = new Buffer('abcdef', 'hex');
|
||||
bitcoind.sendTransaction(garbage, function(err, hash) {
|
||||
should.exist(err);
|
||||
should.not.exist(hash);
|
||||
var num = 23;
|
||||
bitcoind.sendTransaction(num, function(err, hash) {
|
||||
should.exist(err);
|
||||
(err instanceof Error).should.equal(true);
|
||||
should.not.exist(hash);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('will emit "tx" events', function(done) {
|
||||
var tx = bitcore.Transaction();
|
||||
tx.from(utxos[2]);
|
||||
tx.change(privateKey.toAddress());
|
||||
tx.to(destKey.toAddress(), utxos[2].amount * 1e8 - 1000);
|
||||
tx.sign(bitcore.PrivateKey.fromWIF(utxos[2].privateKeyWIF));
|
||||
|
||||
var serialized = tx.serialize();
|
||||
|
||||
bitcoind.once('tx', function(buffer) {
|
||||
buffer.toString('hex').should.equal(serialized);
|
||||
done();
|
||||
});
|
||||
bitcoind.sendTransaction(serialized, function(err, hash) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
should.exist(hash);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('fee estimation', function() {
|
||||
it('will estimate fees', function(done) {
|
||||
bitcoind.estimateFee(1, function(err, fees) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
fees.should.equal(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('tip updates', function() {
|
||||
it('will get an event when the tip is new', function(done) {
|
||||
this.timeout(4000);
|
||||
bitcoind.on('tip', function(height) {
|
||||
if (height === 151) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
client.generate(1, function(err, response) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get detailed transaction', function() {
|
||||
it('should include details for coinbase tx', function(done) {
|
||||
bitcoind.getDetailedTransaction(utxos[0].txid, function(err, tx) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
should.exist(tx.height);
|
||||
tx.height.should.be.a('number');
|
||||
should.exist(tx.blockTimestamp);
|
||||
should.exist(tx.blockHash);
|
||||
tx.coinbase.should.equal(true);
|
||||
tx.version.should.equal(1);
|
||||
tx.hex.should.be.a('string');
|
||||
tx.locktime.should.equal(0);
|
||||
tx.feeSatoshis.should.equal(0);
|
||||
tx.outputSatoshis.should.equal(50 * 1e8);
|
||||
tx.inputSatoshis.should.equal(0);
|
||||
tx.inputs.length.should.equal(1);
|
||||
tx.outputs.length.should.equal(1);
|
||||
should.equal(tx.inputs[0].prevTxId, null);
|
||||
should.equal(tx.inputs[0].outputIndex, null);
|
||||
tx.inputs[0].script.should.be.a('string');
|
||||
should.equal(tx.inputs[0].scriptAsm, null);
|
||||
should.equal(tx.inputs[0].address, null);
|
||||
should.equal(tx.inputs[0].satoshis, null);
|
||||
tx.outputs[0].satoshis.should.equal(50 * 1e8);
|
||||
tx.outputs[0].script.should.be.a('string');
|
||||
tx.outputs[0].scriptAsm.should.be.a('string');
|
||||
tx.outputs[0].spentTxId.should.be.a('string');
|
||||
tx.outputs[0].spentIndex.should.equal(0);
|
||||
tx.outputs[0].spentHeight.should.be.a('number');
|
||||
tx.outputs[0].address.should.be.a('string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getInfo', function() {
|
||||
it('will get information', function(done) {
|
||||
bitcoind.getInfo(function(err, info) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
info.network.should.equal('regtest');
|
||||
should.exist(info);
|
||||
should.exist(info.version);
|
||||
should.exist(info.blocks);
|
||||
should.exist(info.timeOffset);
|
||||
should.exist(info.connections);
|
||||
should.exist(info.difficulty);
|
||||
should.exist(info.testnet);
|
||||
should.exist(info.relayFee);
|
||||
should.exist(info.errors);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -15,6 +15,7 @@ var BitcoinRPC = require('bitcoind-rpc');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var http = require('http');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var bitcoreDataDir = '/tmp/bitcore';
|
||||
var bitcoinDataDir = '/tmp/bitcoin';
|
||||
@ -81,8 +82,6 @@ var bitcore = {
|
||||
protocol: 'http:',
|
||||
hostname: 'localhost',
|
||||
port: 53001,
|
||||
method: 'GET',
|
||||
body: ''
|
||||
},
|
||||
opts: { cwd: bitcoreDataDir },
|
||||
datadir: bitcoreDataDir,
|
||||
@ -100,18 +99,19 @@ var numberOfStartingTxs = 50;
|
||||
var walletPrivKeys = [];
|
||||
var initialTxs = [];
|
||||
var fee = 100000;
|
||||
var walletId = crypto.createHash('sha256').update('test').digest('hex');
|
||||
|
||||
describe('Wallet Operations', function() {
|
||||
|
||||
this.timeout(60000);
|
||||
|
||||
afterEach(function(done) {
|
||||
after(function(done) {
|
||||
bitcore.process.kill();
|
||||
bitcoin.process.kill();
|
||||
setTimeout(done, 2000);
|
||||
});
|
||||
|
||||
beforeEach(function(done) {
|
||||
before(function(done) {
|
||||
async.series([
|
||||
startBitcoind,
|
||||
waitForBitcoinReady,
|
||||
@ -122,11 +122,93 @@ describe('Wallet Operations', function() {
|
||||
], done);
|
||||
});
|
||||
|
||||
it('should generate txs', function(done) {
|
||||
console.log(bitcore);
|
||||
done();
|
||||
it('should register wallet', function(done) {
|
||||
|
||||
var httpOpts = Object.assign({
|
||||
path: '/wallet-api/wallets/' + walletId,
|
||||
method: 'POST'
|
||||
}, bitcore.httpOpts);
|
||||
|
||||
queryBitcoreNode(httpOpts, function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.should.deep.equal(JSON.stringify({
|
||||
walletId: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
|
||||
}));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should upload a wallet', function(done) {
|
||||
var addresses = JSON.stringify(walletPrivKeys.map(function(privKey) {
|
||||
return privKey.toAddress().toString();
|
||||
}));
|
||||
var httpOpts = Object.assign({
|
||||
path: '/wallet-api/wallets/' + walletId + '/addresses',
|
||||
method: 'POST',
|
||||
body: addresses,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': addresses.length
|
||||
}
|
||||
}, bitcore.httpOpts);
|
||||
async.waterfall([ queryBitcoreNode.bind(this, httpOpts) ], function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var job = JSON.parse(res);
|
||||
|
||||
Object.keys(job).should.deep.equal(['jobId']);
|
||||
|
||||
var httpOpts = Object.assign({
|
||||
path: '/wallet-api/jobs/' + job.jobId,
|
||||
method: 'GET'
|
||||
}, bitcore.httpOpts);
|
||||
|
||||
async.retry({ times: 10, interval: 1000 }, function(next) {
|
||||
queryBitcoreNode(httpOpts, function(err, res) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var result = JSON.parse(res);
|
||||
if (result.status === 'complete') {
|
||||
return next();
|
||||
}
|
||||
next(res);
|
||||
});
|
||||
|
||||
}, function(err) {
|
||||
if(err) {
|
||||
return done(err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should get a list of transactions', function(done) {
|
||||
var httpOpts = Object.assign({
|
||||
path: '/wallet-api/wallets/' + walletId + '/transactions',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}, bitcore.httpOpts);
|
||||
queryBitcoreNode(httpOpts, function(err, res) {
|
||||
if(err) {
|
||||
return done(err);
|
||||
}
|
||||
var results = res.split('\n').slice(0, -1);
|
||||
results.length.should.equal(numberOfStartingTxs);
|
||||
for(var i = 0; i < results.length; i++) {
|
||||
var result = results[i];
|
||||
var tx = new Transaction(JSON.parse(result));
|
||||
tx.uncheckedSerialize().should.equal(initialTxs[i].serialize());
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function writeConfigFile(fileStr, obj) {
|
||||
@ -145,12 +227,10 @@ function waitForService(task, next) {
|
||||
}
|
||||
|
||||
function queryBitcoreNode(httpOpts, next) {
|
||||
console.log('request', httpOpts);
|
||||
var error;
|
||||
var request = http.request(httpOpts, function(res) {
|
||||
|
||||
if (res.statusCode !== 200) {
|
||||
console.log('status code: ', error, res.statusCode);
|
||||
if (res.statusCode !== 200 && res.statusCode !== 201) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
@ -169,17 +249,13 @@ console.log('status code: ', error, res.statusCode);
|
||||
});
|
||||
|
||||
res.on('end', function() {
|
||||
console.log('end: ', error);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
if (httpOpts.errorFilter) {
|
||||
return next(httpOpts.errorFilter(resError, resData));
|
||||
}
|
||||
if (resError) {
|
||||
return next(resError);
|
||||
}
|
||||
next();
|
||||
next(resError, resData);
|
||||
});
|
||||
|
||||
});
|
||||
@ -189,7 +265,7 @@ console.log('end: ', error);
|
||||
next(error);
|
||||
});
|
||||
|
||||
request.write('');
|
||||
request.write(httpOpts.body || '');
|
||||
request.end();
|
||||
}
|
||||
|
||||
@ -208,15 +284,16 @@ function waitForBitcoreNode(next) {
|
||||
|
||||
var httpOpts = Object.assign({
|
||||
path: '/wallet-api/issynced',
|
||||
errorFilter: errorFilter
|
||||
errorFilter: errorFilter,
|
||||
method: 'GET'
|
||||
}, bitcore.httpOpts);
|
||||
|
||||
waitForService(queryBitcoreNode.bind(this, httpOpts), next);
|
||||
}
|
||||
|
||||
function waitForBitcoinReady(next) {
|
||||
async.retry({ times: 10, interval: 1000 }, function(next) {
|
||||
rpc.generate(150, function(err, res) {
|
||||
waitForService(function(next) {
|
||||
rpc.generate(101, function(err, res) {
|
||||
if (err || (res && res.error)) {
|
||||
return next('keep trying');
|
||||
}
|
||||
@ -227,7 +304,7 @@ function waitForBitcoinReady(next) {
|
||||
return next(err);
|
||||
}
|
||||
next();
|
||||
});
|
||||
}, next);
|
||||
}
|
||||
|
||||
function initializeAndStartService(opts, next) {
|
||||
@ -335,7 +412,7 @@ function sendTx(tx, next) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
rpc.generate(6, function(err) {
|
||||
rpc.generate(1, function(err) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
@ -23,12 +23,24 @@ describe('Wallet-Api service encoding', function() {
|
||||
satsBuf.writeDoubleBE(sats);
|
||||
|
||||
it('should encode wallet transaction key' , function() {
|
||||
encoding.encodeWalletTransactionKey(walletId, height).should.deep.equal(Buffer.concat([
|
||||
encoding.encodeWalletTransactionKey(walletId, height, txid).should.deep.equal(Buffer.concat([
|
||||
servicePrefix,
|
||||
encoding.subKeyMap.transaction.buffer,
|
||||
new Buffer('0c', 'hex'),
|
||||
new Buffer(walletId),
|
||||
new Buffer('00000001', 'hex')
|
||||
new Buffer('00000001', 'hex'),
|
||||
new Buffer(txid, 'hex')
|
||||
]));
|
||||
});
|
||||
|
||||
it('should encode wallet transaction key without a txid (all zeroes) or height (0)' , function() {
|
||||
encoding.encodeWalletTransactionKey(walletId).should.deep.equal(Buffer.concat([
|
||||
servicePrefix,
|
||||
encoding.subKeyMap.transaction.buffer,
|
||||
new Buffer('0c', 'hex'),
|
||||
new Buffer(walletId),
|
||||
new Buffer('00000000', 'hex'),
|
||||
new Buffer(new Array(65).join('0'), 'hex')
|
||||
]));
|
||||
});
|
||||
|
||||
@ -44,14 +56,6 @@ describe('Wallet-Api service encoding', function() {
|
||||
walletTransactionKey.height.should.equal(height);
|
||||
});
|
||||
|
||||
it('should encode wallet transaction value', function() {
|
||||
encoding.encodeWalletTransactionValue(txid).should.deep.equal(new Buffer(txid, 'hex'));
|
||||
});
|
||||
|
||||
it('should decode wallet transaction value', function() {
|
||||
encoding.decodeWalletTransactionValue(new Buffer(txid, 'hex')).should.equal(txid);
|
||||
});
|
||||
|
||||
it('should encode wallet utxo key', function() {
|
||||
encoding.encodeWalletUtxoKey(walletId, txid, outputIndex).should.deep.equal(Buffer.concat([
|
||||
servicePrefix,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user