Convert db into a db module.
This commit is contained in:
parent
96f6604222
commit
df9b62acca
2
index.js
2
index.js
|
@ -3,7 +3,6 @@
|
||||||
module.exports = require('./lib');
|
module.exports = require('./lib');
|
||||||
module.exports.Node = require('./lib/node');
|
module.exports.Node = require('./lib/node');
|
||||||
module.exports.Chain = require('./lib/chain');
|
module.exports.Chain = require('./lib/chain');
|
||||||
module.exports.DB = require('./lib/db');
|
|
||||||
module.exports.Transaction = require('./lib/transaction');
|
module.exports.Transaction = require('./lib/transaction');
|
||||||
module.exports.Module = require('./lib/module');
|
module.exports.Module = require('./lib/module');
|
||||||
module.exports.errors = require('./lib/errors');
|
module.exports.errors = require('./lib/errors');
|
||||||
|
@ -11,6 +10,7 @@ module.exports.errors = require('./lib/errors');
|
||||||
module.exports.modules = {};
|
module.exports.modules = {};
|
||||||
module.exports.modules.AddressModule = require('./lib/modules/address');
|
module.exports.modules.AddressModule = require('./lib/modules/address');
|
||||||
module.exports.modules.BitcoinModule = require('./lib/modules/bitcoind');
|
module.exports.modules.BitcoinModule = require('./lib/modules/bitcoind');
|
||||||
|
module.exports.modules.DBModule = require('./lib/modules/db');
|
||||||
|
|
||||||
module.exports.scaffold = {};
|
module.exports.scaffold = {};
|
||||||
module.exports.scaffold.create = require('./lib/scaffold/create');
|
module.exports.scaffold.create = require('./lib/scaffold/create');
|
||||||
|
|
|
@ -27,6 +27,7 @@ var index = require('..');
|
||||||
var BitcoreNode = index.Node;
|
var BitcoreNode = index.Node;
|
||||||
var AddressModule = index.modules.AddressModule;
|
var AddressModule = index.modules.AddressModule;
|
||||||
var BitcoinModule = index.modules.BitcoinModule;
|
var BitcoinModule = index.modules.BitcoinModule;
|
||||||
|
var DBModule = index.modules.DBModule;
|
||||||
var testWIF = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG';
|
var testWIF = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG';
|
||||||
var testKey;
|
var testKey;
|
||||||
var client;
|
var client;
|
||||||
|
@ -65,6 +66,11 @@ describe('Node Functionality', function() {
|
||||||
datadir: datadir,
|
datadir: datadir,
|
||||||
network: 'regtest',
|
network: 'regtest',
|
||||||
modules: [
|
modules: [
|
||||||
|
{
|
||||||
|
name: 'db',
|
||||||
|
module: DBModule,
|
||||||
|
dependencies: DBModule.dependencies
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'bitcoind',
|
name: 'bitcoind',
|
||||||
module: BitcoinModule,
|
module: BitcoinModule,
|
||||||
|
|
15
lib/chain.js
15
lib/chain.js
|
@ -99,18 +99,14 @@ Chain.prototype.initialize = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Does our database already have a tip?
|
// Does our database already have a tip?
|
||||||
self.node.db.getMetadata(function getMetadataCallback(err, metadata) {
|
self.node.modules.db.getMetadata(function getMetadataCallback(err, metadata) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return self.emit('error', err);
|
return self.emit('error', err);
|
||||||
} else if(!metadata || !metadata.tip) {
|
} else if(!metadata || !metadata.tip) {
|
||||||
self.tip = self.genesis;
|
self.tip = self.genesis;
|
||||||
self.tip.__height = 0;
|
self.tip.__height = 0;
|
||||||
self.tip.__weight = self.genesisWeight;
|
self.tip.__weight = self.genesisWeight;
|
||||||
self.node.db.putBlock(self.genesis, function putBlockCallback(err) {
|
self.node.modules.db.connectBlock(self.genesis, function(err) {
|
||||||
if(err) {
|
|
||||||
return self.emit('error', err);
|
|
||||||
}
|
|
||||||
self.node.db._onChainAddBlock(self.genesis, function(err) {
|
|
||||||
if(err) {
|
if(err) {
|
||||||
return self.emit('error', err);
|
return self.emit('error', err);
|
||||||
}
|
}
|
||||||
|
@ -119,10 +115,9 @@ Chain.prototype.initialize = function() {
|
||||||
self.saveMetadata();
|
self.saveMetadata();
|
||||||
self.emit('initialized');
|
self.emit('initialized');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
metadata.tip = metadata.tip;
|
metadata.tip = metadata.tip;
|
||||||
self.node.db.getBlock(metadata.tip, function getBlockCallback(err, tip) {
|
self.node.modules.db.getBlock(metadata.tip, function getBlockCallback(err, tip) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return self.emit('error', err);
|
return self.emit('error', err);
|
||||||
}
|
}
|
||||||
|
@ -215,7 +210,7 @@ Chain.prototype.getHashes = function getHashes(tipHash, callback) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// do a db call if we don't have it
|
// do a db call if we don't have it
|
||||||
self.node.db.getPrevHash(hash, function(err, prevHash) {
|
self.node.modules.db.getPrevHash(hash, function(err, prevHash) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
@ -246,7 +241,7 @@ Chain.prototype.saveMetadata = function saveMetadata(callback) {
|
||||||
|
|
||||||
self.lastSavedMetadata = new Date();
|
self.lastSavedMetadata = new Date();
|
||||||
|
|
||||||
self.node.db.putMetadata(metadata, callback);
|
self.node.modules.db.putMetadata(metadata, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Chain;
|
module.exports = Chain;
|
||||||
|
|
|
@ -82,10 +82,10 @@ AddressModule.prototype.transactionOutputHandler = function(messages, tx, output
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the address for the output
|
// Find the address for the output
|
||||||
var address = script.toAddress(this.node.db.network);
|
var address = script.toAddress(this.node.network);
|
||||||
if (!address && script.isPublicKeyOut()) {
|
if (!address && script.isPublicKeyOut()) {
|
||||||
var pubkey = script.chunks[0].buf;
|
var pubkey = script.chunks[0].buf;
|
||||||
address = Address.fromPublicKey(new PublicKey(pubkey), this.node.db.network);
|
address = Address.fromPublicKey(new PublicKey(pubkey), this.node.network);
|
||||||
} else if (!address){
|
} else if (!address){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -162,10 +162,10 @@ AddressModule.prototype.blockHandler = function(block, addOutput, callback) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var address = script.toAddress(this.node.db.network);
|
var address = script.toAddress(this.node.network);
|
||||||
if (!address && script.isPublicKeyOut()) {
|
if (!address && script.isPublicKeyOut()) {
|
||||||
var pubkey = script.chunks[0].buf;
|
var pubkey = script.chunks[0].buf;
|
||||||
address = Address.fromPublicKey(new PublicKey(pubkey), this.node.db.network);
|
address = Address.fromPublicKey(new PublicKey(pubkey), this.node.network);
|
||||||
} else if (!address){
|
} else if (!address){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ AddressModule.prototype.getOutputs = function(addressStr, queryMempool, callback
|
||||||
var outputs = [];
|
var outputs = [];
|
||||||
var key = [AddressModule.PREFIXES.OUTPUTS, addressStr].join('-');
|
var key = [AddressModule.PREFIXES.OUTPUTS, addressStr].join('-');
|
||||||
|
|
||||||
var stream = this.node.db.store.createReadStream({
|
var stream = this.node.modules.db.store.createReadStream({
|
||||||
start: key,
|
start: key,
|
||||||
end: key + '~'
|
end: key + '~'
|
||||||
});
|
});
|
||||||
|
@ -443,7 +443,7 @@ AddressModule.prototype.getSpendInfoForOutput = function(txid, outputIndex, call
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var key = [AddressModule.PREFIXES.SPENTS, txid, outputIndex].join('-');
|
var key = [AddressModule.PREFIXES.SPENTS, txid, outputIndex].join('-');
|
||||||
this.node.db.store.get(key, function(err, value) {
|
this.node.modules.db.store.get(key, function(err, value) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
@ -492,12 +492,12 @@ AddressModule.prototype.getAddressHistoryForAddress = function(address, queryMem
|
||||||
return callback(null, txinfos[txid]);
|
return callback(null, txinfos[txid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.node.db.getTransactionWithBlockInfo(txid, queryMempool, function(err, transaction) {
|
self.node.modules.db.getTransactionWithBlockInfo(txid, queryMempool, function(err, transaction) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.populateInputs(self.node.db, [], function(err) {
|
transaction.populateInputs(self.node.modules.db, [], function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var Module = require('../module');
|
|
||||||
var bindings = require('bindings')('bitcoind.node');
|
var bindings = require('bindings')('bitcoind.node');
|
||||||
var mkdirp = require('mkdirp');
|
var mkdirp = require('mkdirp');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var index = require('../');
|
|
||||||
var log = index.log;
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
|
var index = require('../');
|
||||||
|
var log = index.log;
|
||||||
|
var Module = require('../module');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an interface to native bindings to Bitcoin Core
|
* Provides an interface to native bindings to Bitcoin Core
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var fs = require('fs');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var levelup = require('levelup');
|
var levelup = require('levelup');
|
||||||
var leveldown = require('leveldown');
|
var leveldown = require('leveldown');
|
||||||
|
var mkdirp = require('mkdirp');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
var Networks = bitcore.Networks;
|
||||||
var Block = bitcore.Block;
|
var Block = bitcore.Block;
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
var index = require('./');
|
var index = require('../');
|
||||||
var errors = index.errors;
|
var errors = index.errors;
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var Transaction = require('./transaction');
|
var Transaction = require('../transaction');
|
||||||
|
var Module = require('../module');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the current state of the bitcoin blockchain transaction data. Other modules
|
||||||
|
* can extend the data that is indexed by implementing a `blockHandler` method.
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {String} options.datadir - The bitcoin data directory
|
||||||
|
* @param {Node} options.node - A reference to the node
|
||||||
|
*/
|
||||||
function DB(options) {
|
function DB(options) {
|
||||||
/* jshint maxstatements: 30 */
|
|
||||||
/* jshint maxcomplexity: 20 */
|
|
||||||
|
|
||||||
if (!(this instanceof DB)) {
|
if (!(this instanceof DB)) {
|
||||||
return new DB(options);
|
return new DB(options);
|
||||||
}
|
}
|
||||||
|
@ -24,55 +32,50 @@ function DB(options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.coinbaseAmount = options.coinbaseAmount || 50 * 1e8;
|
Module.call(this, options);
|
||||||
|
|
||||||
var levelupStore = leveldown;
|
$.checkState(this.node.network, 'Node is expected to have a "network" property');
|
||||||
|
this.network = this.node.network;
|
||||||
|
|
||||||
|
this._setDataPath();
|
||||||
|
|
||||||
|
this.levelupStore = leveldown;
|
||||||
if (options.store) {
|
if (options.store) {
|
||||||
levelupStore = options.store;
|
this.levelupStore = options.store;
|
||||||
} else if(!options.path) {
|
|
||||||
throw new Error('Please include database path in options');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store = levelup(options.path, { db: levelupStore });
|
|
||||||
this.txPrefix = options.txPrefix || DB.PREFIXES.TX;
|
|
||||||
this.prevHashPrefix = options.prevHashPrefix || DB.PREFIXES.PREV_HASH;
|
|
||||||
this.blockPrefix = options.blockPrefix || DB.PREFIXES.BLOCK;
|
|
||||||
this.dataPrefix = options.dataPrefix || DB.PREFIXES.DATA;
|
|
||||||
this.weightPrefix = options.weightPrefix || DB.PREFIXES.WEIGHT;
|
|
||||||
this.Transaction = Transaction;
|
|
||||||
|
|
||||||
this.coinbaseAddress = options.coinbaseAddress;
|
|
||||||
this.coinbaseAmount = options.coinbaseAmount || 50 * 1e8;
|
|
||||||
this.Transaction = Transaction;
|
|
||||||
|
|
||||||
this.network = bitcore.Networks.get(options.network) || bitcore.Networks.testnet;
|
|
||||||
|
|
||||||
this.node = options.node;
|
|
||||||
|
|
||||||
this.subscriptions = {
|
this.subscriptions = {
|
||||||
transaction: [],
|
transaction: [],
|
||||||
block: []
|
block: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
DB.PREFIXES = {
|
util.inherits(DB, Module);
|
||||||
TX: 'tx',
|
|
||||||
PREV_HASH: 'ph',
|
|
||||||
BLOCK: 'blk',
|
|
||||||
DATA: 'data',
|
|
||||||
WEIGHT: 'wt'
|
|
||||||
};
|
|
||||||
|
|
||||||
util.inherits(DB, EventEmitter);
|
DB.dependencies = ['bitcoind'];
|
||||||
|
|
||||||
DB.prototype.initialize = function() {
|
DB.prototype._setDataPath = function() {
|
||||||
this.emit('ready');
|
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
|
||||||
|
var regtest = Networks.get('regtest');
|
||||||
|
if (this.node.network === Networks.livenet) {
|
||||||
|
this.dataPath = this.node.datadir + '/bitcore-node.db';
|
||||||
|
} else if (this.node.network === Networks.testnet) {
|
||||||
|
this.dataPath = this.node.datadir + '/testnet3/bitcore-node.db';
|
||||||
|
} else if (this.node.network === regtest) {
|
||||||
|
this.dataPath = this.node.datadir + '/regtest/bitcore-node.db';
|
||||||
|
} else {
|
||||||
|
throw new Error('Unknown network: ' + this.network);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.start = function(callback) {
|
DB.prototype.start = function(callback) {
|
||||||
|
if (!fs.existsSync(this.dataPath)) {
|
||||||
|
mkdirp.sync(this.dataPath);
|
||||||
|
}
|
||||||
|
this.store = levelup(this.dataPath, { db: this.levelupStore });
|
||||||
this.node.modules.bitcoind.on('tx', this.transactionHandler.bind(this));
|
this.node.modules.bitcoind.on('tx', this.transactionHandler.bind(this));
|
||||||
this.emit('ready');
|
this.emit('ready');
|
||||||
|
log.info('Bitcoin Database Ready');
|
||||||
setImmediate(callback);
|
setImmediate(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,10 +92,36 @@ DB.prototype.getInfo = function(callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.getBlock = function(hash, callback) {
|
DB.prototype.transactionHandler = function(txInfo) {
|
||||||
var self = this;
|
var tx = Transaction().fromBuffer(txInfo.buffer);
|
||||||
|
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||||
|
this.subscriptions.transaction[i].emit('transaction', {
|
||||||
|
rejected: !txInfo.mempool,
|
||||||
|
tx: tx
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// get block from bitcoind
|
/**
|
||||||
|
* Closes the underlying store database
|
||||||
|
* @param {Function} callback - A function that accepts: Error
|
||||||
|
*/
|
||||||
|
DB.prototype.close = function(callback) {
|
||||||
|
this.store.close(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
DB.prototype.getAPIMethods = function() {
|
||||||
|
var methods = [
|
||||||
|
['getBlock', this, this.getBlock, 1],
|
||||||
|
['getTransaction', this, this.getTransaction, 2],
|
||||||
|
['getTransactionWithBlockInfo', this, this.getTransactionWithBlockInfo, 2],
|
||||||
|
['sendTransaction', this, this.sendTransaction, 1],
|
||||||
|
['estimateFee', this, this.estimateFee, 1]
|
||||||
|
];
|
||||||
|
return methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
DB.prototype.getBlock = function(hash, callback) {
|
||||||
this.node.modules.bitcoind.getBlock(hash, function(err, blockData) {
|
this.node.modules.bitcoind.getBlock(hash, function(err, blockData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
@ -101,22 +130,6 @@ DB.prototype.getBlock = function(hash, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.getPrevHash = function(blockHash, callback) {
|
|
||||||
var blockIndex = this.node.modules.bitcoind.getBlockIndex(blockHash);
|
|
||||||
setImmediate(function() {
|
|
||||||
if (blockIndex) {
|
|
||||||
callback(null, blockIndex.prevHash);
|
|
||||||
} else {
|
|
||||||
callback(new Error('Could not get prevHash, block not found'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.putBlock = function(block, callback) {
|
|
||||||
// block is already stored in bitcoind
|
|
||||||
setImmediate(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.getTransaction = function(txid, queryMempool, callback) {
|
DB.prototype.getTransaction = function(txid, queryMempool, callback) {
|
||||||
this.node.modules.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) {
|
this.node.modules.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -145,7 +158,7 @@ DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.sendTransaction = function(tx, callback) {
|
DB.prototype.sendTransaction = function(tx, callback) {
|
||||||
if(tx instanceof this.Transaction) {
|
if (tx instanceof Transaction) {
|
||||||
tx = tx.toString();
|
tx = tx.toString();
|
||||||
}
|
}
|
||||||
$.checkArgument(typeof tx === 'string', 'Argument must be a hex string or Transaction');
|
$.checkArgument(typeof tx === 'string', 'Argument must be a hex string or Transaction');
|
||||||
|
@ -160,25 +173,53 @@ DB.prototype.sendTransaction = function(tx, callback) {
|
||||||
|
|
||||||
DB.prototype.estimateFee = function(blocks, callback) {
|
DB.prototype.estimateFee = function(blocks, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
setImmediate(function() {
|
setImmediate(function() {
|
||||||
callback(null, self.node.modules.bitcoind.estimateFee(blocks));
|
callback(null, self.node.modules.bitcoind.estimateFee(blocks));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.validateBlockData = function(block, callback) {
|
DB.prototype.getPublishEvents = function() {
|
||||||
// bitcoind does the validation
|
return [
|
||||||
setImmediate(callback);
|
{
|
||||||
|
name: 'transaction',
|
||||||
|
scope: this,
|
||||||
|
subscribe: this.subscribe.bind(this, 'transaction'),
|
||||||
|
unsubscribe: this.unsubscribe.bind(this, 'transaction')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'block',
|
||||||
|
scope: this,
|
||||||
|
subscribe: this.subscribe.bind(this, 'block'),
|
||||||
|
unsubscribe: this.unsubscribe.bind(this, 'block')
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype._updatePrevHashIndex = function(block, callback) {
|
DB.prototype.subscribe = function(name, emitter) {
|
||||||
// bitcoind has the previous hash for each block
|
this.subscriptions[name].push(emitter);
|
||||||
setImmediate(callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype._updateWeight = function(hash, weight, callback) {
|
DB.prototype.unsubscribe = function(name, emitter) {
|
||||||
// bitcoind has all work for each block
|
var index = this.subscriptions[name].indexOf(emitter);
|
||||||
setImmediate(callback);
|
if (index > -1) {
|
||||||
|
this.subscriptions[name].splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will give the previous hash for a block.
|
||||||
|
* @param {String} blockHash
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
DB.prototype.getPrevHash = function(blockHash, callback) {
|
||||||
|
var blockIndex = this.node.modules.bitcoind.getBlockIndex(blockHash);
|
||||||
|
setImmediate(function() {
|
||||||
|
if (blockIndex) {
|
||||||
|
callback(null, blockIndex.prevHash);
|
||||||
|
} else {
|
||||||
|
callback(new Error('Could not get prevHash, block not found'));
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,53 +257,33 @@ DB.prototype.getMetadata = function(callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the underlying store database
|
* Connects a block to the database and add indexes
|
||||||
* @param {Function} callback - A function that accepts: Error
|
* @param {Block} block - The bitcore block
|
||||||
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
DB.prototype.close = function(callback) {
|
DB.prototype.connectBlock = function(block, callback) {
|
||||||
this.store.close(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.getOutputTotal = function(transactions, excludeCoinbase) {
|
|
||||||
var totals = transactions.map(function(tx) {
|
|
||||||
if(tx.isCoinbase() && excludeCoinbase) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return tx._getOutputAmount();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var grandTotal = totals.reduce(function(previousValue, currentValue) {
|
|
||||||
return previousValue + currentValue;
|
|
||||||
});
|
|
||||||
return grandTotal;
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.getInputTotal = function(transactions) {
|
|
||||||
var totals = transactions.map(function(tx) {
|
|
||||||
if(tx.isCoinbase()) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return tx._getInputAmount();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var grandTotal = totals.reduce(function(previousValue, currentValue) {
|
|
||||||
return previousValue + currentValue;
|
|
||||||
});
|
|
||||||
return grandTotal;
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype._onChainAddBlock = function(block, callback) {
|
|
||||||
log.debug('DB handling new chain block');
|
log.debug('DB handling new chain block');
|
||||||
|
this.runAllBlockHandlers(block, true, callback);
|
||||||
this.blockHandler(block, true, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype._onChainRemoveBlock = function(block, callback) {
|
/**
|
||||||
|
* Disconnects a block from the database and removes indexes
|
||||||
|
* @param {Block} block - The bitcore block
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
DB.prototype.disconnectBlock = function(block, callback) {
|
||||||
log.debug('DB removing chain block');
|
log.debug('DB removing chain block');
|
||||||
this.blockHandler(block, false, callback);
|
this.runAllBlockHandlers(block, false, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.blockHandler = function(block, add, callback) {
|
/**
|
||||||
|
* Will collect all database operations for a block from other modules
|
||||||
|
* and save to the database.
|
||||||
|
* @param {Block} block - The bitcore block
|
||||||
|
* @param {Boolean} add - If the block is being added/connected or removed/disconnected
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
DB.prototype.runAllBlockHandlers = function(block, add, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var operations = [];
|
var operations = [];
|
||||||
|
|
||||||
|
@ -273,8 +294,8 @@ DB.prototype.blockHandler = function(block, add, callback) {
|
||||||
|
|
||||||
async.eachSeries(
|
async.eachSeries(
|
||||||
this.node.modules,
|
this.node.modules,
|
||||||
function(bitcoreNodeModule, next) {
|
function(mod, next) {
|
||||||
bitcoreNodeModule.blockHandler.call(bitcoreNodeModule, block, add, function(err, ops) {
|
mod.blockHandler.call(mod, block, add, function(err, ops) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -296,53 +317,4 @@ DB.prototype.blockHandler = function(block, add, callback) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.getAPIMethods = function() {
|
|
||||||
var methods = [
|
|
||||||
['getInfo', this, this.getInfo, 0],
|
|
||||||
['getBlock', this, this.getBlock, 1],
|
|
||||||
['getTransaction', this, this.getTransaction, 2],
|
|
||||||
['sendTransaction', this, this.sendTransaction, 1],
|
|
||||||
['estimateFee', this, this.estimateFee, 1]
|
|
||||||
];
|
|
||||||
return methods;
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.getPublishEvents = function() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'transaction',
|
|
||||||
scope: this,
|
|
||||||
subscribe: this.subscribe.bind(this, 'transaction'),
|
|
||||||
unsubscribe: this.unsubscribe.bind(this, 'transaction')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'block',
|
|
||||||
scope: this,
|
|
||||||
subscribe: this.subscribe.bind(this, 'block'),
|
|
||||||
unsubscribe: this.unsubscribe.bind(this, 'block')
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.subscribe = function(name, emitter) {
|
|
||||||
this.subscriptions[name].push(emitter);
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.unsubscribe = function(name, emitter) {
|
|
||||||
var index = this.subscriptions[name].indexOf(emitter);
|
|
||||||
if(index > -1) {
|
|
||||||
this.subscriptions[name].splice(index, 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DB.prototype.transactionHandler = function(txInfo) {
|
|
||||||
var tx = bitcore.Transaction().fromBuffer(txInfo.buffer);
|
|
||||||
for(var i = 0; i < this.subscriptions.transaction.length; i++) {
|
|
||||||
this.subscriptions.transaction[i].emit('transaction', {
|
|
||||||
rejected: !txInfo.mempool,
|
|
||||||
tx: tx
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = DB;
|
module.exports = DB;
|
78
lib/node.js
78
lib/node.js
|
@ -1,10 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var mkdirp = require('mkdirp');
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var BufferUtil = bitcore.util.buffer;
|
var BufferUtil = bitcore.util.buffer;
|
||||||
var Networks = bitcore.Networks;
|
var Networks = bitcore.Networks;
|
||||||
|
@ -12,7 +10,6 @@ var _ = bitcore.deps._;
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
var Block = bitcore.Block;
|
var Block = bitcore.Block;
|
||||||
var Chain = require('./chain');
|
var Chain = require('./chain');
|
||||||
var DB = require('./db');
|
|
||||||
var index = require('./');
|
var index = require('./');
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var Bus = require('./bus');
|
var Bus = require('./bus');
|
||||||
|
@ -23,7 +20,6 @@ function Node(config) {
|
||||||
return new Node(config);
|
return new Node(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.db = null;
|
|
||||||
this.chain = null;
|
this.chain = null;
|
||||||
this.network = null;
|
this.network = null;
|
||||||
|
|
||||||
|
@ -81,7 +77,7 @@ Node.prototype.addModule = function(service) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.getAllAPIMethods = function() {
|
Node.prototype.getAllAPIMethods = function() {
|
||||||
var methods = this.db.getAPIMethods();
|
var methods = [];
|
||||||
for(var i in this.modules) {
|
for(var i in this.modules) {
|
||||||
var mod = this.modules[i];
|
var mod = this.modules[i];
|
||||||
methods = methods.concat(mod.getAPIMethods());
|
methods = methods.concat(mod.getAPIMethods());
|
||||||
|
@ -90,7 +86,7 @@ Node.prototype.getAllAPIMethods = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.getAllPublishEvents = function() {
|
Node.prototype.getAllPublishEvents = function() {
|
||||||
var events = this.db.getPublishEvents();
|
var events = [];
|
||||||
for (var i in this.modules) {
|
for (var i in this.modules) {
|
||||||
var mod = this.modules[i];
|
var mod = this.modules[i];
|
||||||
events = events.concat(mod.getPublishEvents());
|
events = events.concat(mod.getPublishEvents());
|
||||||
|
@ -100,8 +96,6 @@ Node.prototype.getAllPublishEvents = function() {
|
||||||
|
|
||||||
Node.prototype._loadConfiguration = function(config) {
|
Node.prototype._loadConfiguration = function(config) {
|
||||||
this._loadNetwork(config);
|
this._loadNetwork(config);
|
||||||
this._loadDB(config);
|
|
||||||
this._loadAPI();
|
|
||||||
this._loadConsensus(config);
|
this._loadConsensus(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -185,7 +179,7 @@ Node.prototype._syncBitcoindRewind = function(block, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Undo the related indexes for this block
|
// Undo the related indexes for this block
|
||||||
self.db._onChainRemoveBlock(tip, function(err) {
|
self.modules.db.disconnectBlock(tip, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return removeDone(err);
|
return removeDone(err);
|
||||||
}
|
}
|
||||||
|
@ -258,7 +252,7 @@ Node.prototype._syncBitcoind = function() {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
// Create indexes
|
// Create indexes
|
||||||
self.db._onChainAddBlock(block, function(err) {
|
self.modules.db.connectBlock(block, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
@ -325,39 +319,6 @@ Node.prototype._loadNetwork = function(config) {
|
||||||
$.checkState(this.network, 'Unrecognized network');
|
$.checkState(this.network, 'Unrecognized network');
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype._loadDB = function(config) {
|
|
||||||
var options = _.clone(config.db || {});
|
|
||||||
|
|
||||||
if (config.DB) {
|
|
||||||
// Other modules can inherit from our DB and replace it with their own
|
|
||||||
DB = config.DB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the additional indexes in a new directory
|
|
||||||
// based on the network configuration and the datadir
|
|
||||||
$.checkArgument(config.datadir, 'Please specify "datadir" in configuration options');
|
|
||||||
$.checkState(this.network, 'Network property not defined');
|
|
||||||
var regtest = Networks.get('regtest');
|
|
||||||
if (this.network === Networks.livenet) {
|
|
||||||
options.path = config.datadir + '/bitcore-node.db';
|
|
||||||
} else if (this.network === Networks.testnet) {
|
|
||||||
options.path = config.datadir + '/testnet3/bitcore-node.db';
|
|
||||||
} else if (this.network === regtest) {
|
|
||||||
options.path = config.datadir + '/regtest/bitcore-node.db';
|
|
||||||
} else {
|
|
||||||
throw new Error('Unknown network: ' + this.network);
|
|
||||||
}
|
|
||||||
options.network = this.network;
|
|
||||||
|
|
||||||
if (!fs.existsSync(options.path)) {
|
|
||||||
mkdirp.sync(options.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.node = this;
|
|
||||||
|
|
||||||
this.db = new DB(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
Node.prototype._loadConsensus = function(config) {
|
Node.prototype._loadConsensus = function(config) {
|
||||||
var options;
|
var options;
|
||||||
if (!config) {
|
if (!config) {
|
||||||
|
@ -369,24 +330,9 @@ Node.prototype._loadConsensus = function(config) {
|
||||||
this.chain = new Chain(options);
|
this.chain = new Chain(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype._loadAPI = function() {
|
|
||||||
var self = this;
|
|
||||||
var methodData = self.db.getAPIMethods();
|
|
||||||
methodData.forEach(function(data) {
|
|
||||||
var name = data[0];
|
|
||||||
var instance = data[1];
|
|
||||||
var method = data[2];
|
|
||||||
|
|
||||||
self[name] = function() {
|
|
||||||
return method.apply(instance, arguments);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Node.prototype._initialize = function() {
|
Node.prototype._initialize = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this._initializeDatabase();
|
|
||||||
this._initializeChain();
|
this._initializeChain();
|
||||||
|
|
||||||
this.start(function(err) {
|
this.start(function(err) {
|
||||||
|
@ -397,18 +343,6 @@ Node.prototype._initialize = function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype._initializeDatabase = function() {
|
|
||||||
var self = this;
|
|
||||||
this.db.on('ready', function() {
|
|
||||||
log.info('Bitcoin Database Ready');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.db.on('error', function(err) {
|
|
||||||
Error.captureStackTrace(err);
|
|
||||||
self.emit('error', err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Node.prototype._initializeChain = function() {
|
Node.prototype._initializeChain = function() {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -433,10 +367,6 @@ Node.prototype._initializeChain = function() {
|
||||||
|
|
||||||
Node.prototype.getServices = function() {
|
Node.prototype.getServices = function() {
|
||||||
var services = [
|
var services = [
|
||||||
{
|
|
||||||
name: 'db',
|
|
||||||
dependencies: ['bitcoind'],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'chain',
|
name: 'chain',
|
||||||
dependencies: ['db']
|
dependencies: ['db']
|
||||||
|
|
|
@ -13,7 +13,7 @@ function getDefaultConfig() {
|
||||||
datadir: process.env.BITCORENODE_DIR || path.resolve(process.env.HOME, '.bitcoin'),
|
datadir: process.env.BITCORENODE_DIR || path.resolve(process.env.HOME, '.bitcoin'),
|
||||||
network: process.env.BITCORENODE_NETWORK || 'livenet',
|
network: process.env.BITCORENODE_NETWORK || 'livenet',
|
||||||
port: process.env.BITCORENODE_PORT || 3001,
|
port: process.env.BITCORENODE_PORT || 3001,
|
||||||
modules: ['bitcoind', 'address']
|
modules: ['bitcoind', 'db', 'address']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,21 +46,21 @@ describe('Bitcoin Chain', function() {
|
||||||
it('should initialize the chain with the genesis block if no metadata is found in the db', function(done) {
|
it('should initialize the chain with the genesis block if no metadata is found in the db', function(done) {
|
||||||
var db = {};
|
var db = {};
|
||||||
db.getMetadata = sinon.stub().callsArgWith(0, null, {});
|
db.getMetadata = sinon.stub().callsArgWith(0, null, {});
|
||||||
db.putBlock = sinon.stub().callsArg(1);
|
|
||||||
db.putMetadata = sinon.stub().callsArg(1);
|
db.putMetadata = sinon.stub().callsArg(1);
|
||||||
db.getTransactionsFromBlock = sinon.stub();
|
db.getTransactionsFromBlock = sinon.stub();
|
||||||
db._onChainAddBlock = sinon.stub().callsArg(1);
|
db.connectBlock = sinon.stub().callsArg(1);
|
||||||
db.mempool = {
|
db.mempool = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
var node = {
|
var node = {
|
||||||
|
modules: {
|
||||||
db: db
|
db: db
|
||||||
|
}
|
||||||
};
|
};
|
||||||
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
||||||
|
|
||||||
chain.on('ready', function() {
|
chain.on('ready', function() {
|
||||||
should.exist(chain.tip);
|
should.exist(chain.tip);
|
||||||
db.putBlock.callCount.should.equal(1);
|
|
||||||
chain.tip.hash.should.equal('genesis');
|
chain.tip.hash.should.equal('genesis');
|
||||||
Number(chain.tip.__weight.toString(10)).should.equal(0);
|
Number(chain.tip.__weight.toString(10)).should.equal(0);
|
||||||
done();
|
done();
|
||||||
|
@ -76,7 +76,6 @@ describe('Bitcoin Chain', function() {
|
||||||
it('should initialize the chain with the metadata from the database if it exists', function(done) {
|
it('should initialize the chain with the metadata from the database if it exists', function(done) {
|
||||||
var db = {};
|
var db = {};
|
||||||
db.getMetadata = sinon.stub().callsArgWith(0, null, {tip: 'block2', tipWeight: 2});
|
db.getMetadata = sinon.stub().callsArgWith(0, null, {tip: 'block2', tipWeight: 2});
|
||||||
db.putBlock = sinon.stub().callsArg(1);
|
|
||||||
db.putMetadata = sinon.stub().callsArg(1);
|
db.putMetadata = sinon.stub().callsArg(1);
|
||||||
db.getBlock = sinon.stub().callsArgWith(1, null, {hash: 'block2', prevHash: 'block1'});
|
db.getBlock = sinon.stub().callsArgWith(1, null, {hash: 'block2', prevHash: 'block1'});
|
||||||
db.getTransactionsFromBlock = sinon.stub();
|
db.getTransactionsFromBlock = sinon.stub();
|
||||||
|
@ -84,14 +83,15 @@ describe('Bitcoin Chain', function() {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
var node = {
|
var node = {
|
||||||
|
modules: {
|
||||||
db: db
|
db: db
|
||||||
|
}
|
||||||
};
|
};
|
||||||
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
||||||
chain.getHeightForBlock = sinon.stub().callsArgWith(1, null, 10);
|
chain.getHeightForBlock = sinon.stub().callsArgWith(1, null, 10);
|
||||||
chain.getWeight = sinon.stub().callsArgWith(1, null, new BN(50));
|
chain.getWeight = sinon.stub().callsArgWith(1, null, new BN(50));
|
||||||
chain.on('ready', function() {
|
chain.on('ready', function() {
|
||||||
should.exist(chain.tip);
|
should.exist(chain.tip);
|
||||||
db.putBlock.callCount.should.equal(0);
|
|
||||||
chain.tip.hash.should.equal('block2');
|
chain.tip.hash.should.equal('block2');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -113,7 +113,9 @@ describe('Bitcoin Chain', function() {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
var node = {
|
var node = {
|
||||||
|
modules: {
|
||||||
db: db
|
db: db
|
||||||
|
}
|
||||||
};
|
};
|
||||||
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
||||||
chain.on('error', function(error) {
|
chain.on('error', function(error) {
|
||||||
|
@ -124,31 +126,6 @@ describe('Bitcoin Chain', function() {
|
||||||
chain.initialize();
|
chain.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emit error from putBlock', function(done) {
|
|
||||||
var db = {
|
|
||||||
getMetadata: function(cb) {
|
|
||||||
cb(null, null);
|
|
||||||
},
|
|
||||||
putBlock: function(block, cb) {
|
|
||||||
cb(new Error('putBlockError'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
db.getTransactionsFromBlock = sinon.stub();
|
|
||||||
db.mempool = {
|
|
||||||
on: sinon.spy()
|
|
||||||
};
|
|
||||||
var node = {
|
|
||||||
db: db
|
|
||||||
};
|
|
||||||
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
|
||||||
chain.on('error', function(error) {
|
|
||||||
should.exist(error);
|
|
||||||
error.message.should.equal('putBlockError');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
chain.initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('emit error from getBlock', function(done) {
|
it('emit error from getBlock', function(done) {
|
||||||
var db = {
|
var db = {
|
||||||
getMetadata: function(cb) {
|
getMetadata: function(cb) {
|
||||||
|
@ -163,7 +140,9 @@ describe('Bitcoin Chain', function() {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
var node = {
|
var node = {
|
||||||
|
modules: {
|
||||||
db: db
|
db: db
|
||||||
|
}
|
||||||
};
|
};
|
||||||
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
var chain = new Chain({node: node, genesis: {hash: 'genesis'}});
|
||||||
chain.on('error', function(error) {
|
chain.on('error', function(error) {
|
||||||
|
@ -196,8 +175,8 @@ describe('Bitcoin Chain', function() {
|
||||||
var work = '000000000000000000000000000000000000000000005a7b3c42ea8b844374e9';
|
var work = '000000000000000000000000000000000000000000005a7b3c42ea8b844374e9';
|
||||||
var chain = new Chain();
|
var chain = new Chain();
|
||||||
chain.node = {};
|
chain.node = {};
|
||||||
chain.node.db = {};
|
|
||||||
chain.node.modules = {};
|
chain.node.modules = {};
|
||||||
|
chain.node.modules.db = {};
|
||||||
chain.node.modules.bitcoind = {
|
chain.node.modules.bitcoind = {
|
||||||
getBlockIndex: sinon.stub().returns({
|
getBlockIndex: sinon.stub().returns({
|
||||||
chainWork: work
|
chainWork: work
|
||||||
|
@ -233,7 +212,7 @@ describe('Bitcoin Chain', function() {
|
||||||
blocks[block1.hash] = block1;
|
blocks[block1.hash] = block1;
|
||||||
blocks[block2.hash] = block2;
|
blocks[block2.hash] = block2;
|
||||||
|
|
||||||
var db = new DB({store: memdown});
|
var db = {};
|
||||||
db.getPrevHash = function(blockHash, cb) {
|
db.getPrevHash = function(blockHash, cb) {
|
||||||
// TODO: expose prevHash as a string from bitcore
|
// TODO: expose prevHash as a string from bitcore
|
||||||
var prevHash = BufferUtil.reverse(blocks[blockHash].header.prevHash).toString('hex');
|
var prevHash = BufferUtil.reverse(blocks[blockHash].header.prevHash).toString('hex');
|
||||||
|
@ -241,7 +220,9 @@ describe('Bitcoin Chain', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
var node = {
|
var node = {
|
||||||
|
modules: {
|
||||||
db: db
|
db: db
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var chain = new Chain({
|
var chain = new Chain({
|
||||||
|
|
|
@ -6,6 +6,7 @@ var bitcorenode = require('../../');
|
||||||
var AddressModule = bitcorenode.modules.AddressModule;
|
var AddressModule = bitcorenode.modules.AddressModule;
|
||||||
var blockData = require('../data/livenet-345003.json');
|
var blockData = require('../data/livenet-345003.json');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
var Networks = bitcore.Networks;
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var errors = bitcorenode.errors;
|
var errors = bitcorenode.errors;
|
||||||
var levelup = require('levelup');
|
var levelup = require('levelup');
|
||||||
|
@ -71,6 +72,7 @@ describe('AddressModule', function() {
|
||||||
var txBuf = new Buffer('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000', 'hex');
|
var txBuf = new Buffer('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000', 'hex');
|
||||||
var tx = bitcore.Transaction().fromBuffer(txBuf);
|
var tx = bitcore.Transaction().fromBuffer(txBuf);
|
||||||
var am = new AddressModule({node: mocknode});
|
var am = new AddressModule({node: mocknode});
|
||||||
|
am.node.network = Networks.livenet;
|
||||||
var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX';
|
var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX';
|
||||||
var messages = {};
|
var messages = {};
|
||||||
am.transactionOutputHandler(messages, tx, 0, true);
|
am.transactionOutputHandler(messages, tx, 0, true);
|
||||||
|
@ -151,7 +153,8 @@ describe('AddressModule', function() {
|
||||||
var value64 = data[2].value;
|
var value64 = data[2].value;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
am = new AddressModule({node: mocknode, network: 'livenet'});
|
am = new AddressModule({node: mocknode});
|
||||||
|
am.node.network = Networks.livenet;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the correct operations when updating/adding outputs', function(done) {
|
it('should create the correct operations when updating/adding outputs', function(done) {
|
||||||
|
@ -428,8 +431,8 @@ describe('AddressModule', function() {
|
||||||
__height: 1
|
__height: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
db: db,
|
|
||||||
modules: {
|
modules: {
|
||||||
|
db: db,
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
on: sinon.stub()
|
on: sinon.stub()
|
||||||
}
|
}
|
||||||
|
@ -442,7 +445,7 @@ describe('AddressModule', function() {
|
||||||
|
|
||||||
it('should get outputs for an address', function(done) {
|
it('should get outputs for an address', function(done) {
|
||||||
var readStream1 = new EventEmitter();
|
var readStream1 = new EventEmitter();
|
||||||
am.node.db.store = {
|
am.node.modules.db.store = {
|
||||||
createReadStream: sinon.stub().returns(readStream1)
|
createReadStream: sinon.stub().returns(readStream1)
|
||||||
};
|
};
|
||||||
var mempoolOutputs = [
|
var mempoolOutputs = [
|
||||||
|
@ -497,7 +500,7 @@ describe('AddressModule', function() {
|
||||||
|
|
||||||
it('should give an error if the readstream has an error', function(done) {
|
it('should give an error if the readstream has an error', function(done) {
|
||||||
var readStream2 = new EventEmitter();
|
var readStream2 = new EventEmitter();
|
||||||
am.node.db.store = {
|
am.node.modules.db.store = {
|
||||||
createReadStream: sinon.stub().returns(readStream2)
|
createReadStream: sinon.stub().returns(readStream2)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -524,8 +527,8 @@ describe('AddressModule', function() {
|
||||||
|
|
||||||
var db = {};
|
var db = {};
|
||||||
var testnode = {
|
var testnode = {
|
||||||
db: db,
|
|
||||||
modules: {
|
modules: {
|
||||||
|
db: db,
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
on: sinon.stub()
|
on: sinon.stub()
|
||||||
}
|
}
|
||||||
|
@ -554,13 +557,7 @@ describe('AddressModule', function() {
|
||||||
'addr3': ['utxo3']
|
'addr3': ['utxo3']
|
||||||
};
|
};
|
||||||
|
|
||||||
var db = {
|
var db = {};
|
||||||
modules: {
|
|
||||||
bitcoind: {
|
|
||||||
on: sinon.spy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var testnode = {
|
var testnode = {
|
||||||
db: db,
|
db: db,
|
||||||
modules: {
|
modules: {
|
||||||
|
@ -741,8 +738,8 @@ describe('AddressModule', function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var testnode = {
|
var testnode = {
|
||||||
db: db,
|
|
||||||
modules: {
|
modules: {
|
||||||
|
db: db,
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
on: sinon.stub()
|
on: sinon.stub()
|
||||||
}
|
}
|
||||||
|
@ -861,8 +858,8 @@ describe('AddressModule', function() {
|
||||||
__height: 1
|
__height: 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
db: db,
|
|
||||||
modules: {
|
modules: {
|
||||||
|
db: db,
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
on: sinon.stub()
|
on: sinon.stub()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,113 @@
|
||||||
|
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var index = require('../');
|
var index = require('../../');
|
||||||
var DB = index.DB;
|
var DB = index.modules.DBModule;
|
||||||
var blockData = require('./data/livenet-345003.json');
|
var blockData = require('../data/livenet-345003.json');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
var Networks = bitcore.Networks;
|
||||||
var Block = bitcore.Block;
|
var Block = bitcore.Block;
|
||||||
var transactionData = require('./data/bitcoin-transactions.json');
|
var transactionData = require('../data/bitcoin-transactions.json');
|
||||||
var errors = index.errors;
|
var errors = index.errors;
|
||||||
var memdown = require('memdown');
|
var memdown = require('memdown');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
|
|
||||||
describe('Bitcoin DB', function() {
|
describe('DB Module', function() {
|
||||||
|
|
||||||
|
var baseConfig = {
|
||||||
|
node: {
|
||||||
|
network: Networks.testnet,
|
||||||
|
datadir: 'testdir'
|
||||||
|
},
|
||||||
|
store: memdown
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('#_setDataPath', function() {
|
||||||
|
it('should set the database path', function() {
|
||||||
|
var config = {
|
||||||
|
node: {
|
||||||
|
network: Networks.livenet,
|
||||||
|
datadir: process.env.HOME + '/.bitcoin'
|
||||||
|
},
|
||||||
|
store: memdown
|
||||||
|
};
|
||||||
|
var db = new DB(config);
|
||||||
|
db.dataPath.should.equal(process.env.HOME + '/.bitcoin/bitcore-node.db');
|
||||||
|
});
|
||||||
|
it('should load the db for testnet', function() {
|
||||||
|
var config = {
|
||||||
|
node: {
|
||||||
|
network: Networks.testnet,
|
||||||
|
datadir: process.env.HOME + '/.bitcoin'
|
||||||
|
},
|
||||||
|
store: memdown
|
||||||
|
};
|
||||||
|
var db = new DB(config);
|
||||||
|
db.dataPath.should.equal(process.env.HOME + '/.bitcoin/testnet3/bitcore-node.db');
|
||||||
|
});
|
||||||
|
it('error with unknown network', function() {
|
||||||
|
var config = {
|
||||||
|
node: {
|
||||||
|
network: 'unknown',
|
||||||
|
datadir: process.env.HOME + '/.bitcoin'
|
||||||
|
},
|
||||||
|
store: memdown
|
||||||
|
};
|
||||||
|
(function() {
|
||||||
|
var db = new DB(config);
|
||||||
|
}).should.throw('Unknown network');
|
||||||
|
});
|
||||||
|
it('should load the db with regtest', function() {
|
||||||
|
// Switch to use regtest
|
||||||
|
Networks.remove(Networks.testnet);
|
||||||
|
Networks.add({
|
||||||
|
name: 'regtest',
|
||||||
|
alias: 'regtest',
|
||||||
|
pubkeyhash: 0x6f,
|
||||||
|
privatekey: 0xef,
|
||||||
|
scripthash: 0xc4,
|
||||||
|
xpubkey: 0x043587cf,
|
||||||
|
xprivkey: 0x04358394,
|
||||||
|
networkMagic: 0xfabfb5da,
|
||||||
|
port: 18444,
|
||||||
|
dnsSeeds: [ ]
|
||||||
|
});
|
||||||
|
var regtest = Networks.get('regtest');
|
||||||
|
var config = {
|
||||||
|
node: {
|
||||||
|
network: regtest,
|
||||||
|
datadir: process.env.HOME + '/.bitcoin'
|
||||||
|
},
|
||||||
|
store: memdown
|
||||||
|
};
|
||||||
|
var db = new DB(config);
|
||||||
|
db.dataPath.should.equal(process.env.HOME + '/.bitcoin/regtest/bitcore-node.db');
|
||||||
|
Networks.remove(regtest);
|
||||||
|
// Add testnet back
|
||||||
|
Networks.add({
|
||||||
|
name: 'testnet',
|
||||||
|
alias: 'testnet',
|
||||||
|
pubkeyhash: 0x6f,
|
||||||
|
privatekey: 0xef,
|
||||||
|
scripthash: 0xc4,
|
||||||
|
xpubkey: 0x043587cf,
|
||||||
|
xprivkey: 0x04358394,
|
||||||
|
networkMagic: 0x0b110907,
|
||||||
|
port: 18333,
|
||||||
|
dnsSeeds: [
|
||||||
|
'testnet-seed.bitcoin.petertodd.org',
|
||||||
|
'testnet-seed.bluematt.me',
|
||||||
|
'testnet-seed.alexykot.me',
|
||||||
|
'testnet-seed.bitcoin.schildbach.de'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#start', function() {
|
describe('#start', function() {
|
||||||
it('should emit ready', function(done) {
|
it('should emit ready', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -37,7 +128,7 @@ describe('Bitcoin DB', function() {
|
||||||
|
|
||||||
describe('#stop', function() {
|
describe('#stop', function() {
|
||||||
it('should immediately call the callback', function(done) {
|
it('should immediately call the callback', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
|
|
||||||
db.stop(function(err) {
|
db.stop(function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
|
@ -48,7 +139,7 @@ describe('Bitcoin DB', function() {
|
||||||
|
|
||||||
describe('#getTransaction', function() {
|
describe('#getTransaction', function() {
|
||||||
it('will return a NotFound error', function(done) {
|
it('will return a NotFound error', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -61,7 +152,7 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('will return an error from bitcoind', function(done) {
|
it('will return an error from bitcoind', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -74,7 +165,7 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('will return an error from bitcoind', function(done) {
|
it('will return an error from bitcoind', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -92,7 +183,7 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#getBlock', function() {
|
describe('#getBlock', function() {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
var blockBuffer = new Buffer(blockData, 'hex');
|
var blockBuffer = new Buffer(blockData, 'hex');
|
||||||
var expectedBlock = Block.fromBuffer(blockBuffer);
|
var expectedBlock = Block.fromBuffer(blockBuffer);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
|
@ -121,19 +212,9 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#putBlock', function() {
|
|
||||||
it('should call callback', function(done) {
|
|
||||||
var db = new DB({store: memdown});
|
|
||||||
db.putBlock('block', function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getPrevHash', function() {
|
describe('#getPrevHash', function() {
|
||||||
it('should return prevHash from bitcoind', function(done) {
|
it('should return prevHash from bitcoind', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -150,7 +231,7 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should give an error if bitcoind could not find it', function(done) {
|
it('should give an error if bitcoind could not find it', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -173,7 +254,7 @@ describe('Bitcoin DB', function() {
|
||||||
buffer: txBuffer
|
buffer: txBuffer
|
||||||
};
|
};
|
||||||
|
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -188,7 +269,7 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should give an error if one occurred', function(done) {
|
it('should give an error if one occurred', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -204,7 +285,7 @@ describe('Bitcoin DB', function() {
|
||||||
|
|
||||||
describe('#sendTransaction', function() {
|
describe('#sendTransaction', function() {
|
||||||
it('should give the txid on success', function(done) {
|
it('should give the txid on success', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -219,7 +300,7 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should give an error if bitcoind threw an error', function(done) {
|
it('should give an error if bitcoind threw an error', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -236,7 +317,7 @@ describe('Bitcoin DB', function() {
|
||||||
|
|
||||||
describe("#estimateFee", function() {
|
describe("#estimateFee", function() {
|
||||||
it('should pass along the fee from bitcoind', function(done) {
|
it('should pass along the fee from bitcoind', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
db.node.modules.bitcoind = {
|
db.node.modules.bitcoind = {
|
||||||
|
@ -252,95 +333,35 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#getOutputTotal', function() {
|
describe('#connectBlock', function() {
|
||||||
it('should return the correct value including the coinbase', function() {
|
|
||||||
var totals = [10, 20, 30];
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
var transactions = totals.map(function(total) {
|
|
||||||
return {
|
|
||||||
_getOutputAmount: function() {
|
|
||||||
return total;
|
|
||||||
},
|
|
||||||
isCoinbase: function() {
|
|
||||||
return total === 10 ? true : false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
var grandTotal = db.getOutputTotal(transactions);
|
|
||||||
grandTotal.should.equal(60);
|
|
||||||
});
|
|
||||||
it('should return the correct value excluding the coinbase', function() {
|
|
||||||
var totals = [10, 20, 30];
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
var transactions = totals.map(function(total) {
|
|
||||||
return {
|
|
||||||
_getOutputAmount: function() {
|
|
||||||
return total;
|
|
||||||
},
|
|
||||||
isCoinbase: function() {
|
|
||||||
return total === 10 ? true : false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
var grandTotal = db.getOutputTotal(transactions, true);
|
|
||||||
grandTotal.should.equal(50);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getInputTotal', function() {
|
|
||||||
it('should return the correct value', function() {
|
|
||||||
var totals = [10, 20, 30];
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
var transactions = totals.map(function(total) {
|
|
||||||
return {
|
|
||||||
_getInputAmount: function() {
|
|
||||||
return total;
|
|
||||||
},
|
|
||||||
isCoinbase: sinon.stub().returns(false)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
var grandTotal = db.getInputTotal(transactions);
|
|
||||||
grandTotal.should.equal(60);
|
|
||||||
});
|
|
||||||
it('should return 0 if the tx is a coinbase', function() {
|
|
||||||
var db = new DB({store: memdown});
|
|
||||||
var tx = {
|
|
||||||
isCoinbase: sinon.stub().returns(true)
|
|
||||||
};
|
|
||||||
var total = db.getInputTotal([tx]);
|
|
||||||
total.should.equal(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_onChainAddBlock', function() {
|
|
||||||
it('should remove block from mempool and call blockHandler with true', function(done) {
|
it('should remove block from mempool and call blockHandler with true', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.mempool = {
|
db.mempool = {
|
||||||
removeBlock: sinon.stub()
|
removeBlock: sinon.stub()
|
||||||
};
|
};
|
||||||
db.blockHandler = sinon.stub().callsArg(2);
|
db.runAllBlockHandlers = sinon.stub().callsArg(2);
|
||||||
db._onChainAddBlock({hash: 'hash'}, function(err) {
|
db.connectBlock({hash: 'hash'}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
db.blockHandler.args[0][1].should.equal(true);
|
db.runAllBlockHandlers.args[0][1].should.equal(true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#_onChainRemoveBlock', function() {
|
describe('#disconnectBlock', function() {
|
||||||
it('should call blockHandler with false', function(done) {
|
it('should call blockHandler with false', function(done) {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.blockHandler = sinon.stub().callsArg(2);
|
db.runAllBlockHandlers = sinon.stub().callsArg(2);
|
||||||
db._onChainRemoveBlock({hash: 'hash'}, function(err) {
|
db.disconnectBlock({hash: 'hash'}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
db.blockHandler.args[0][1].should.equal(false);
|
db.runAllBlockHandlers.args[0][1].should.equal(false);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#blockHandler', function() {
|
describe('#runAllBlockHandlers', function() {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
var Module1 = function() {};
|
var Module1 = function() {};
|
||||||
Module1.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op1', 'op2', 'op3']);
|
Module1.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op1', 'op2', 'op3']);
|
||||||
var Module2 = function() {};
|
var Module2 = function() {};
|
||||||
|
@ -355,7 +376,7 @@ describe('Bitcoin DB', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should call blockHandler in all modules and perform operations', function(done) {
|
it('should call blockHandler in all modules and perform operations', function(done) {
|
||||||
db.blockHandler('block', true, function(err) {
|
db.runAllBlockHandlers('block', true, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
db.store.batch.args[0][0].should.deep.equal(['op1', 'op2', 'op3', 'op4', 'op5']);
|
db.store.batch.args[0][0].should.deep.equal(['op1', 'op2', 'op3', 'op4', 'op5']);
|
||||||
done();
|
done();
|
||||||
|
@ -367,7 +388,7 @@ describe('Bitcoin DB', function() {
|
||||||
Module3.prototype.blockHandler = sinon.stub().callsArgWith(2, new Error('error'));
|
Module3.prototype.blockHandler = sinon.stub().callsArgWith(2, new Error('error'));
|
||||||
db.node.modules.module3 = new Module3();
|
db.node.modules.module3 = new Module3();
|
||||||
|
|
||||||
db.blockHandler('block', true, function(err) {
|
db.runAllBlockHandlers('block', true, function(err) {
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -376,12 +397,11 @@ describe('Bitcoin DB', function() {
|
||||||
|
|
||||||
describe('#getAPIMethods', function() {
|
describe('#getAPIMethods', function() {
|
||||||
it('should return the correct db methods', function() {
|
it('should return the correct db methods', function() {
|
||||||
var db = new DB({store: memdown});
|
var db = new DB(baseConfig);
|
||||||
db.node = {};
|
db.node = {};
|
||||||
db.node.modules = {};
|
db.node.modules = {};
|
||||||
var methods = db.getAPIMethods();
|
var methods = db.getAPIMethods();
|
||||||
methods.length.should.equal(5);
|
methods.length.should.equal(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
|
@ -101,6 +101,9 @@ describe('Bitcore Node', function() {
|
||||||
it('should return db methods and modules methods', function() {
|
it('should return db methods and modules methods', function() {
|
||||||
var node = new Node(baseConfig);
|
var node = new Node(baseConfig);
|
||||||
node.modules = {
|
node.modules = {
|
||||||
|
db: {
|
||||||
|
getAPIMethods: sinon.stub().returns(['db1', 'db2']),
|
||||||
|
},
|
||||||
module1: {
|
module1: {
|
||||||
getAPIMethods: sinon.stub().returns(['mda1', 'mda2'])
|
getAPIMethods: sinon.stub().returns(['mda1', 'mda2'])
|
||||||
},
|
},
|
||||||
|
@ -108,10 +111,6 @@ describe('Bitcore Node', function() {
|
||||||
getAPIMethods: sinon.stub().returns(['mdb1', 'mdb2'])
|
getAPIMethods: sinon.stub().returns(['mdb1', 'mdb2'])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var db = {
|
|
||||||
getAPIMethods: sinon.stub().returns(['db1', 'db2']),
|
|
||||||
};
|
|
||||||
node.db = db;
|
|
||||||
|
|
||||||
var methods = node.getAllAPIMethods();
|
var methods = node.getAllAPIMethods();
|
||||||
methods.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
methods.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
||||||
|
@ -121,6 +120,9 @@ describe('Bitcore Node', function() {
|
||||||
it('should return modules publish events', function() {
|
it('should return modules publish events', function() {
|
||||||
var node = new Node(baseConfig);
|
var node = new Node(baseConfig);
|
||||||
node.modules = {
|
node.modules = {
|
||||||
|
db: {
|
||||||
|
getPublishEvents: sinon.stub().returns(['db1', 'db2']),
|
||||||
|
},
|
||||||
module1: {
|
module1: {
|
||||||
getPublishEvents: sinon.stub().returns(['mda1', 'mda2'])
|
getPublishEvents: sinon.stub().returns(['mda1', 'mda2'])
|
||||||
},
|
},
|
||||||
|
@ -128,11 +130,6 @@ describe('Bitcore Node', function() {
|
||||||
getPublishEvents: sinon.stub().returns(['mdb1', 'mdb2'])
|
getPublishEvents: sinon.stub().returns(['mdb1', 'mdb2'])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var db = {
|
|
||||||
getPublishEvents: sinon.stub().returns(['db1', 'db2']),
|
|
||||||
};
|
|
||||||
node.db = db;
|
|
||||||
|
|
||||||
var events = node.getAllPublishEvents();
|
var events = node.getAllPublishEvents();
|
||||||
events.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
events.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
||||||
});
|
});
|
||||||
|
@ -141,12 +138,8 @@ describe('Bitcore Node', function() {
|
||||||
it('should call the necessary methods', function() {
|
it('should call the necessary methods', function() {
|
||||||
var TestNode = proxyquire('../lib/node', {});
|
var TestNode = proxyquire('../lib/node', {});
|
||||||
TestNode.prototype._initialize = sinon.spy();
|
TestNode.prototype._initialize = sinon.spy();
|
||||||
TestNode.prototype._loadDB = sinon.spy();
|
|
||||||
TestNode.prototype._loadAPI = sinon.spy();
|
|
||||||
TestNode.prototype._loadConsensus = sinon.spy();
|
TestNode.prototype._loadConsensus = sinon.spy();
|
||||||
var node = new TestNode(baseConfig);
|
var node = new TestNode(baseConfig);
|
||||||
node._loadDB.callCount.should.equal(1);
|
|
||||||
node._loadAPI.callCount.should.equal(1);
|
|
||||||
node._loadConsensus.callCount.should.equal(1);
|
node._loadConsensus.callCount.should.equal(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -243,8 +236,9 @@ describe('Bitcore Node', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
node.db = {
|
node.modules = {};
|
||||||
_onChainRemoveBlock: function(block, callback) {
|
node.modules.db = {
|
||||||
|
disconnectBlock: function(block, callback) {
|
||||||
setImmediate(callback);
|
setImmediate(callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -286,8 +280,8 @@ describe('Bitcore Node', function() {
|
||||||
hashes: {}
|
hashes: {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
node.db = {
|
node.modules.db = {
|
||||||
_onChainAddBlock: function(block, callback) {
|
connectBlock: function(block, callback) {
|
||||||
node.chain.tip.__height += 1;
|
node.chain.tip.__height += 1;
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
@ -336,8 +330,8 @@ describe('Bitcore Node', function() {
|
||||||
hashes: {}
|
hashes: {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
node.db = {
|
node.modules.db = {
|
||||||
_onChainAddBlock: function(block, callback) {
|
connectBlock: function(block, callback) {
|
||||||
node.chain.tip.__height += 1;
|
node.chain.tip.__height += 1;
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
@ -387,119 +381,21 @@ describe('Bitcore Node', function() {
|
||||||
node.network.name.should.equal('livenet');
|
node.network.name.should.equal('livenet');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#_loadDB', function() {
|
|
||||||
it('should load the db', function() {
|
|
||||||
var DB = function(config) {
|
|
||||||
config.path.should.equal(process.env.HOME + '/.bitcoin/bitcore-node.db');
|
|
||||||
};
|
|
||||||
var config = {
|
|
||||||
DB: DB,
|
|
||||||
datadir: process.env.HOME + '/.bitcoin'
|
|
||||||
};
|
|
||||||
|
|
||||||
var node = new Node(config);
|
|
||||||
node.network = Networks.livenet;
|
|
||||||
node._loadDB(config);
|
|
||||||
node.db.should.be.instanceof(DB);
|
|
||||||
});
|
|
||||||
it('should load the db for testnet', function() {
|
|
||||||
var DB = function(config) {
|
|
||||||
config.path.should.equal(process.env.HOME + '/.bitcoin/testnet3/bitcore-node.db');
|
|
||||||
};
|
|
||||||
var config = {
|
|
||||||
DB: DB,
|
|
||||||
datadir: process.env.HOME + '/.bitcoin'
|
|
||||||
};
|
|
||||||
|
|
||||||
var node = new Node(config);
|
|
||||||
node.network = Networks.testnet;
|
|
||||||
node._loadDB(config);
|
|
||||||
node.db.should.be.instanceof(DB);
|
|
||||||
});
|
|
||||||
it('error with unknown network', function() {
|
|
||||||
var config = {
|
|
||||||
datadir: process.env.HOME + '/.bitcoin'
|
|
||||||
};
|
|
||||||
|
|
||||||
var node = new Node(config);
|
|
||||||
node.network = 'not a network';
|
|
||||||
(function() {
|
|
||||||
node._loadDB(config);
|
|
||||||
}).should.throw('Unknown network');
|
|
||||||
});
|
|
||||||
it('should load the db with regtest', function() {
|
|
||||||
var DB = function(config) {
|
|
||||||
config.path.should.equal(process.env.HOME + '/.bitcoin/regtest/bitcore-node.db');
|
|
||||||
};
|
|
||||||
var config = {
|
|
||||||
DB: DB,
|
|
||||||
datadir: process.env.HOME + '/.bitcoin'
|
|
||||||
};
|
|
||||||
|
|
||||||
var node = new Node(config);
|
|
||||||
// Switch to use regtest
|
|
||||||
Networks.remove(Networks.testnet);
|
|
||||||
Networks.add({
|
|
||||||
name: 'regtest',
|
|
||||||
alias: 'regtest',
|
|
||||||
pubkeyhash: 0x6f,
|
|
||||||
privatekey: 0xef,
|
|
||||||
scripthash: 0xc4,
|
|
||||||
xpubkey: 0x043587cf,
|
|
||||||
xprivkey: 0x04358394,
|
|
||||||
networkMagic: 0xfabfb5da,
|
|
||||||
port: 18444,
|
|
||||||
dnsSeeds: [ ]
|
|
||||||
});
|
|
||||||
var regtest = Networks.get('regtest');
|
|
||||||
node.network = regtest;
|
|
||||||
node._loadDB(config);
|
|
||||||
node.db.should.be.instanceof(DB);
|
|
||||||
Networks.remove(regtest);
|
|
||||||
// Add testnet back
|
|
||||||
Networks.add({
|
|
||||||
name: 'testnet',
|
|
||||||
alias: 'testnet',
|
|
||||||
pubkeyhash: 0x6f,
|
|
||||||
privatekey: 0xef,
|
|
||||||
scripthash: 0xc4,
|
|
||||||
xpubkey: 0x043587cf,
|
|
||||||
xprivkey: 0x04358394,
|
|
||||||
networkMagic: 0x0b110907,
|
|
||||||
port: 18333,
|
|
||||||
dnsSeeds: [
|
|
||||||
'testnet-seed.bitcoin.petertodd.org',
|
|
||||||
'testnet-seed.bluematt.me',
|
|
||||||
'testnet-seed.alexykot.me',
|
|
||||||
'testnet-seed.bitcoin.schildbach.de'
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('#_loadConsensus', function() {
|
describe('#_loadConsensus', function() {
|
||||||
|
|
||||||
var node;
|
var node;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
node = new Node(baseConfig);
|
node = new Node(baseConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will set properties', function() {
|
it('will set properties', function() {
|
||||||
node._loadConsensus();
|
node._loadConsensus();
|
||||||
should.exist(node.chain);
|
should.exist(node.chain);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#_initialize', function() {
|
describe('#_initialize', function() {
|
||||||
|
|
||||||
var node;
|
var node;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
var TestNode = proxyquire('../lib/node', {});
|
var TestNode = proxyquire('../lib/node', {});
|
||||||
TestNode.prototype._loadConfiguration = sinon.spy();
|
TestNode.prototype._loadConfiguration = sinon.spy();
|
||||||
TestNode.prototype._initializeBitcoind = sinon.spy();
|
|
||||||
TestNode.prototype._initializeDatabase = sinon.spy();
|
|
||||||
TestNode.prototype._initializeChain = sinon.spy();
|
TestNode.prototype._initializeChain = sinon.spy();
|
||||||
|
|
||||||
// mock the _initialize during construction
|
// mock the _initialize during construction
|
||||||
|
@ -510,14 +406,13 @@ describe('Bitcore Node', function() {
|
||||||
node.chain = {
|
node.chain = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
node.Block = 'Block';
|
node.modules = {};
|
||||||
node.bitcoind = {
|
node.modules.bitcoind = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
node.db = {
|
node.modules.db = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
|
|
||||||
// restore the original method
|
// restore the original method
|
||||||
node._initialize = _initialize;
|
node._initialize = _initialize;
|
||||||
});
|
});
|
||||||
|
@ -526,15 +421,9 @@ describe('Bitcore Node', function() {
|
||||||
node.once('ready', function() {
|
node.once('ready', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
node.start = sinon.stub().callsArg(0);
|
node.start = sinon.stub().callsArg(0);
|
||||||
|
|
||||||
node._initialize();
|
node._initialize();
|
||||||
|
|
||||||
// event handlers
|
|
||||||
node._initializeDatabase.callCount.should.equal(1);
|
|
||||||
node._initializeChain.callCount.should.equal(1);
|
node._initializeChain.callCount.should.equal(1);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit an error if an error occurred starting services', function(done) {
|
it('should emit an error if an error occurred starting services', function(done) {
|
||||||
|
@ -549,34 +438,6 @@ describe('Bitcore Node', function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#_initializeDatabase', function() {
|
|
||||||
it('will log on ready event', function(done) {
|
|
||||||
var node = new Node(baseConfig);
|
|
||||||
node.db = new EventEmitter();
|
|
||||||
sinon.stub(index.log, 'info');
|
|
||||||
node.db.on('ready', function() {
|
|
||||||
setImmediate(function() {
|
|
||||||
index.log.info.callCount.should.equal(1);
|
|
||||||
index.log.info.restore();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
node._initializeDatabase();
|
|
||||||
node.db.emit('ready');
|
|
||||||
});
|
|
||||||
it('will call emit an error from db', function(done) {
|
|
||||||
var node = new Node(baseConfig);
|
|
||||||
node.db = new EventEmitter();
|
|
||||||
node.on('error', function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.should.equal('test error');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
node._initializeDatabase();
|
|
||||||
node.db.emit('error', new Error('test error'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_initializeChain', function() {
|
describe('#_initializeChain', function() {
|
||||||
|
|
||||||
it('will call sync when there is a new tip', function(done) {
|
it('will call sync when there is a new tip', function(done) {
|
||||||
|
|
|
@ -4,10 +4,6 @@ var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var bitcoinlib = require('../');
|
var bitcoinlib = require('../');
|
||||||
var Transaction = bitcoinlib.Transaction;
|
var Transaction = bitcoinlib.Transaction;
|
||||||
var transactionData = require('./data/bitcoin-transactions.json');
|
|
||||||
var memdown = require('memdown');
|
|
||||||
var DB = bitcoinlib.DB;
|
|
||||||
var db = new DB({store: memdown});
|
|
||||||
var levelup = require('levelup');
|
var levelup = require('levelup');
|
||||||
|
|
||||||
describe('Bitcoin Transaction', function() {
|
describe('Bitcoin Transaction', function() {
|
||||||
|
|
Loading…
Reference in New Issue