Migrate chainlib functionality into bitcore-node.
This commit is contained in:
parent
ab0417b7ce
commit
bbc421a31e
5
index.js
5
index.js
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = require('./lib/node');
|
module.exports = require('./lib');
|
||||||
module.exports.daemon = require('./lib/daemon');
|
module.exports.daemon = require('./lib/daemon');
|
||||||
module.exports.Node = require('./lib/node');
|
module.exports.Node = require('./lib/node');
|
||||||
module.exports.Block = require('./lib/block');
|
module.exports.Block = require('./lib/block');
|
||||||
|
@ -22,6 +22,3 @@ module.exports.scaffold.defaultConfig = require('./lib/scaffold/default-config')
|
||||||
|
|
||||||
module.exports.cli = {};
|
module.exports.cli = {};
|
||||||
module.exports.cli.main = require('./cli/main');
|
module.exports.cli.main = require('./cli/main');
|
||||||
|
|
||||||
module.exports.deps = {};
|
|
||||||
module.exports.deps.chainlib = require('chainlib');
|
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
// functionality by including the wallet in the build.
|
// functionality by including the wallet in the build.
|
||||||
// To run the tests: $ mocha -R spec integration/regtest-node.js
|
// To run the tests: $ mocha -R spec integration/regtest-node.js
|
||||||
|
|
||||||
var chainlib = require('chainlib');
|
var index = require('..');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var log = chainlib.log;
|
var log = index.log;
|
||||||
log.debug = function() {};
|
log.debug = function() {};
|
||||||
|
|
||||||
if (process.env.BITCORENODE_ENV !== 'test') {
|
if (process.env.BITCORENODE_ENV !== 'test') {
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
// functionality by including the wallet in the build.
|
// functionality by including the wallet in the build.
|
||||||
// To run the tests: $ mocha -R spec integration/regtest.js
|
// To run the tests: $ mocha -R spec integration/regtest.js
|
||||||
|
|
||||||
var chainlib = require('chainlib');
|
var index = require('..');
|
||||||
var log = chainlib.log;
|
var log = index.log;
|
||||||
|
|
||||||
if (process.env.BITCORENODE_ENV !== 'test') {
|
if (process.env.BITCORENODE_ENV !== 'test') {
|
||||||
log.info('Please set the environment variable BITCORENODE_ENV=test and make sure bindings are compiled for testing');
|
log.info('Please set the environment variable BITCORENODE_ENV=test and make sure bindings are compiled for testing');
|
||||||
|
|
111
lib/block.js
111
lib/block.js
|
@ -1,31 +1,60 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var util = require('util');
|
|
||||||
var chainlib = require('chainlib');
|
|
||||||
var BaseBlock = chainlib.Block;
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var BufferReader = bitcore.encoding.BufferReader;
|
var BufferReader = bitcore.encoding.BufferReader;
|
||||||
var BN = bitcore.crypto.BN;
|
var BufferWriter = bitcore.encoding.BufferWriter;
|
||||||
|
var Hash = bitcore.crypto.Hash;
|
||||||
|
|
||||||
|
//TODO: use bitcore.Block
|
||||||
|
|
||||||
function Block(obj) {
|
function Block(obj) {
|
||||||
if (!obj) {
|
/* jshint maxstatements: 25 */
|
||||||
obj = {};
|
if (!(this instanceof Block)) {
|
||||||
|
return new Block(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseBlock.call(this, obj);
|
this.version = obj.version || 1;
|
||||||
|
this.prevHash = obj.prevHash;
|
||||||
|
|
||||||
|
if (!obj.hasOwnProperty('prevHash')) {
|
||||||
|
throw new TypeError('"prevHash" is expected');
|
||||||
|
}
|
||||||
|
if (!obj.timestamp) {
|
||||||
|
throw new TypeError('"timestamp" is expected');
|
||||||
|
}
|
||||||
|
this.timestamp = obj.timestamp;
|
||||||
|
if (typeof this.timestamp === 'string') {
|
||||||
|
this.timestamp = new Date(obj.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.merkleRoot = obj.merkleRoot;
|
||||||
|
|
||||||
|
if (obj.data) {
|
||||||
|
if (!Buffer.isBuffer(obj.data)) {
|
||||||
|
throw new TypeError('"data" is expected to be a buffer');
|
||||||
|
}
|
||||||
|
this.data = obj.data;
|
||||||
|
} else {
|
||||||
|
this.data = new Buffer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashProperty = {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return this.getHash();
|
||||||
|
},
|
||||||
|
set: function() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'hash', hashProperty);
|
||||||
|
|
||||||
this.bits = obj.bits;
|
this.bits = obj.bits;
|
||||||
this.nonce = obj.nonce || 0;
|
this.nonce = obj.nonce || 0;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(Block, BaseBlock);
|
|
||||||
|
|
||||||
Block.prototype.validate = function(chain, callback) {
|
|
||||||
// bitcoind does all validation
|
|
||||||
setImmediate(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.fromBuffer = function(buffer) {
|
Block.fromBuffer = function(buffer) {
|
||||||
var br = new BufferReader(buffer);
|
var br = new BufferReader(buffer);
|
||||||
return Block.fromBufferReader(br);
|
return Block.fromBufferReader(br);
|
||||||
|
@ -48,17 +77,15 @@ Block.fromBufferReader = function(br) {
|
||||||
return new Block(obj);
|
return new Block(obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
Block.prototype.toObject = Block.prototype.toJSON = function() {
|
Block.prototype.validate = function(chain, callback) {
|
||||||
return {
|
// bitcoind does all validation
|
||||||
hash: this.hash,
|
setImmediate(callback);
|
||||||
version: this.version,
|
};
|
||||||
prevHash: this.prevHash,
|
|
||||||
merkleRoot: this.merkleRoot,
|
Block.prototype.headerToBuffer = function() {
|
||||||
timestamp: this.timestamp.toISOString(),
|
var bw = new BufferWriter();
|
||||||
bits: this.bits,
|
this.headerToBufferWriter(bw);
|
||||||
nonce: this.nonce,
|
return bw.concat();
|
||||||
data: this.data.toString('hex')
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Block.prototype.headerToBufferWriter = function(bw) {
|
Block.prototype.headerToBufferWriter = function(bw) {
|
||||||
|
@ -104,4 +131,38 @@ Block.prototype.headerToBufferWriter = function(bw) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Block.prototype.toObject = Block.prototype.toJSON = function() {
|
||||||
|
return {
|
||||||
|
hash: this.hash,
|
||||||
|
version: this.version,
|
||||||
|
prevHash: this.prevHash,
|
||||||
|
merkleRoot: this.merkleRoot,
|
||||||
|
timestamp: this.timestamp.toISOString(),
|
||||||
|
bits: this.bits,
|
||||||
|
nonce: this.nonce,
|
||||||
|
data: this.data.toString('hex')
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Block.prototype.toBufferWriter = function(bw) {
|
||||||
|
// header
|
||||||
|
this.headerToBufferWriter(bw);
|
||||||
|
|
||||||
|
// transaction data
|
||||||
|
bw.write(this.data);
|
||||||
|
return bw;
|
||||||
|
};
|
||||||
|
|
||||||
|
Block.prototype.toBuffer = function() {
|
||||||
|
var bw = new BufferWriter();
|
||||||
|
this.toBufferWriter(bw);
|
||||||
|
return bw.concat();
|
||||||
|
};
|
||||||
|
|
||||||
|
Block.prototype.getHash = function() {
|
||||||
|
var hashBuffer = BufferReader(Hash.sha256sha256(this.headerToBuffer())).readReverse();
|
||||||
|
var hash = hashBuffer.toString('hex');
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Block;
|
module.exports = Block;
|
||||||
|
|
233
lib/chain.js
233
lib/chain.js
|
@ -1,19 +1,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var chainlib = require('chainlib');
|
|
||||||
var BaseChain = chainlib.Chain;
|
|
||||||
var BN = bitcore.crypto.BN;
|
var BN = bitcore.crypto.BN;
|
||||||
|
var $ = bitcore.util.preconditions;
|
||||||
var Block = require('./block');
|
var Block = require('./block');
|
||||||
|
var index = require('./index');
|
||||||
|
var log = index.log;
|
||||||
|
var utils = require('./utils');
|
||||||
|
|
||||||
Chain.DEFAULTS = {
|
var MAX_STACK_DEPTH = 1000;
|
||||||
MAX_HASHES: new BN('10000000000000000000000000000000000000000000000000000000000000000', 'hex'),
|
|
||||||
TARGET_TIMESPAN: 14 * 24 * 60 * 60 * 1000, // two weeks
|
|
||||||
TARGET_SPACING: 10 * 60 * 1000, // ten minutes
|
|
||||||
MAX_BITS: 0x1d00ffff,
|
|
||||||
MIN_BITS: 0x03000000
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will instantiate a new Chain instance
|
* Will instantiate a new Chain instance
|
||||||
|
@ -26,38 +23,123 @@ Chain.DEFAULTS = {
|
||||||
* @extends BaseChain
|
* @extends BaseChain
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function Chain(options) {
|
function Chain(opts) {
|
||||||
/* jshint maxstatements: 20 */
|
/* jshint maxstatements: 30 */
|
||||||
/* jshint maxcomplexity: 12 */
|
|
||||||
if (!(this instanceof Chain)) {
|
if (!(this instanceof Chain)) {
|
||||||
return new Chain(options);
|
return new Chain(opts);
|
||||||
}
|
}
|
||||||
if (!options) {
|
|
||||||
options = {};
|
var self = this;
|
||||||
|
if(!opts) {
|
||||||
|
opts = {};
|
||||||
}
|
}
|
||||||
BaseChain.call(this, options);
|
|
||||||
|
|
||||||
this.minBits = options.minBits || Chain.DEFAULTS.MIN_BITS;
|
this.db = opts.db;
|
||||||
this.maxBits = options.maxBits || Chain.DEFAULTS.MAX_BITS;
|
this.p2p = opts.p2p;
|
||||||
|
|
||||||
this.maxHashes = options.maxHashes || Chain.DEFAULTS.MAX_HASHES;
|
this.genesis = opts.genesis;
|
||||||
|
this.genesisOptions = opts.genesisOptions;
|
||||||
|
this.genesisWeight = new BN(0);
|
||||||
|
this.tip = null;
|
||||||
|
this.overrideTip = opts.overrideTip;
|
||||||
|
this.cache = {
|
||||||
|
hashes: {}, // dictionary of hash -> prevHash
|
||||||
|
chainHashes: {}
|
||||||
|
};
|
||||||
|
this.lastSavedMetadata = null;
|
||||||
|
this.lastSavedMetadataThreshold = 0; // Set this during syncing for faster performance
|
||||||
|
this.blockQueue = [];
|
||||||
|
this.processingBlockQueue = false;
|
||||||
|
this.builder = opts.builder || false;
|
||||||
|
this.ready = false;
|
||||||
|
|
||||||
this.targetTimespan = options.targetTimespan || Chain.DEFAULTS.TARGET_TIMESPAN;
|
this.on('initialized', function() {
|
||||||
this.targetSpacing = options.targetSpacing || Chain.DEFAULTS.TARGET_SPACING;
|
self.initialized = true;
|
||||||
|
});
|
||||||
|
|
||||||
this.node = options.node;
|
this.on('initialized', this._onInitialized.bind(this));
|
||||||
|
|
||||||
|
this.on('ready', function() {
|
||||||
|
log.debug('Chain is ready');
|
||||||
|
self.ready = true;
|
||||||
|
self.startBuilder();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.minBits = opts.minBits || Chain.DEFAULTS.MIN_BITS;
|
||||||
|
this.maxBits = opts.maxBits || Chain.DEFAULTS.MAX_BITS;
|
||||||
|
|
||||||
|
this.maxHashes = opts.maxHashes || Chain.DEFAULTS.MAX_HASHES;
|
||||||
|
|
||||||
|
this.targetTimespan = opts.targetTimespan || Chain.DEFAULTS.TARGET_TIMESPAN;
|
||||||
|
this.targetSpacing = opts.targetSpacing || Chain.DEFAULTS.TARGET_SPACING;
|
||||||
|
|
||||||
|
this.node = opts.node;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(Chain, BaseChain);
|
util.inherits(Chain, EventEmitter);
|
||||||
|
|
||||||
|
Chain.DEFAULTS = {
|
||||||
|
MAX_HASHES: new BN('10000000000000000000000000000000000000000000000000000000000000000', 'hex'),
|
||||||
|
TARGET_TIMESPAN: 14 * 24 * 60 * 60 * 1000, // two weeks
|
||||||
|
TARGET_SPACING: 10 * 60 * 1000, // ten minutes
|
||||||
|
MAX_BITS: 0x1d00ffff,
|
||||||
|
MIN_BITS: 0x03000000
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype._onInitialized = function() {
|
||||||
|
this.emit('ready');
|
||||||
|
};
|
||||||
|
|
||||||
Chain.prototype.start = function(callback) {
|
Chain.prototype.start = function(callback) {
|
||||||
this.genesis = Block.fromBuffer(this.node.bitcoind.genesisBuffer);
|
this.genesis = Block.fromBuffer(this.node.bitcoind.genesisBuffer);
|
||||||
this.on('initialized', callback);
|
this.once('initialized', callback);
|
||||||
this.initialize();
|
this.initialize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Chain.prototype.initialize = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Does our database already have a tip?
|
||||||
|
self.db.getMetadata(function getMetadataCallback(err, metadata) {
|
||||||
|
if(err) {
|
||||||
|
return self.emit('error', err);
|
||||||
|
} else if(!metadata || !metadata.tip) {
|
||||||
|
self.tip = self.genesis;
|
||||||
|
self.tip.__height = 0;
|
||||||
|
self.tip.__weight = self.genesisWeight;
|
||||||
|
self.db.putBlock(self.genesis, function putBlockCallback(err) {
|
||||||
|
if(err) {
|
||||||
|
return self.emit('error', err);
|
||||||
|
}
|
||||||
|
self.db._onChainAddBlock(self.genesis, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return self.emit('error', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emit('addblock', self.genesis);
|
||||||
|
self.saveMetadata();
|
||||||
|
self.emit('initialized');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
metadata.tip = metadata.tip;
|
||||||
|
self.db.getBlock(metadata.tip, function getBlockCallback(err, tip) {
|
||||||
|
if(err) {
|
||||||
|
return self.emit('error', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tip = tip;
|
||||||
|
self.tip.__height = metadata.tipHeight;
|
||||||
|
self.tip.__weight = new BN(metadata.tipWeight, 'hex');
|
||||||
|
self.cache = metadata.cache;
|
||||||
|
self.emit('initialized');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Chain.prototype.stop = function(callback) {
|
Chain.prototype.stop = function(callback) {
|
||||||
setImmediate(callback);
|
setImmediate(callback);
|
||||||
};
|
};
|
||||||
|
@ -78,23 +160,6 @@ Chain.prototype.startBuilder = function() {
|
||||||
// Unused in bitcoind.js
|
// Unused in bitcoind.js
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.buildGenesisBlock = function buildGenesisBlock(options) {
|
|
||||||
if (!options) {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
var genesis = new Block({
|
|
||||||
prevHash: null,
|
|
||||||
height: 0,
|
|
||||||
timestamp: options.timestamp || new Date(),
|
|
||||||
nonce: options.nonce || 0,
|
|
||||||
bits: options.bits || this.maxBits
|
|
||||||
});
|
|
||||||
var data = this.db.buildGenesisData();
|
|
||||||
genesis.merkleRoot = data.merkleRoot;
|
|
||||||
genesis.data = data.buffer;
|
|
||||||
return genesis;
|
|
||||||
};
|
|
||||||
|
|
||||||
Chain.prototype.getWeight = function getWeight(blockHash, callback) {
|
Chain.prototype.getWeight = function getWeight(blockHash, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var blockIndex = self.db.bitcoind.getBlockIndex(blockHash);
|
var blockIndex = self.db.bitcoind.getBlockIndex(blockHash);
|
||||||
|
@ -108,4 +173,90 @@ Chain.prototype.getWeight = function getWeight(blockHash, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will get an array of hashes all the way to the genesis block for
|
||||||
|
* the chain based on "block hash" as the tip.
|
||||||
|
*
|
||||||
|
* @param {String} block hash - a block hash
|
||||||
|
* @param {Function} callback - A function that accepts: Error and Array of hashes
|
||||||
|
*/
|
||||||
|
Chain.prototype.getHashes = function getHashes(tipHash, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
$.checkArgument(utils.isHash(tipHash));
|
||||||
|
|
||||||
|
var hashes = [];
|
||||||
|
var depth = 0;
|
||||||
|
|
||||||
|
getHashAndContinue(null, tipHash);
|
||||||
|
|
||||||
|
function getHashAndContinue(err, hash) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
depth++;
|
||||||
|
|
||||||
|
hashes.unshift(hash);
|
||||||
|
|
||||||
|
if (hash === self.genesis.hash) {
|
||||||
|
// Stop at the genesis block
|
||||||
|
self.cache.chainHashes[tipHash] = hashes;
|
||||||
|
callback(null, hashes);
|
||||||
|
} else if(self.cache.chainHashes[hash]) {
|
||||||
|
hashes.shift();
|
||||||
|
hashes = self.cache.chainHashes[hash].concat(hashes);
|
||||||
|
delete self.cache.chainHashes[hash];
|
||||||
|
self.cache.chainHashes[tipHash] = hashes;
|
||||||
|
callback(null, hashes);
|
||||||
|
} else {
|
||||||
|
// Continue with the previous hash
|
||||||
|
// check cache first
|
||||||
|
var prevHash = self.cache.hashes[hash];
|
||||||
|
if(prevHash) {
|
||||||
|
// Don't let the stack get too deep. Otherwise we will crash.
|
||||||
|
if(depth >= MAX_STACK_DEPTH) {
|
||||||
|
depth = 0;
|
||||||
|
return setImmediate(function() {
|
||||||
|
getHashAndContinue(null, prevHash);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return getHashAndContinue(null, prevHash);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// do a db call if we don't have it
|
||||||
|
self.db.getPrevHash(hash, function(err, prevHash) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getHashAndContinue(null, prevHash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype.saveMetadata = function saveMetadata(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
callback = callback || function() {};
|
||||||
|
|
||||||
|
if(self.lastSavedMetadata && Date.now() < self.lastSavedMetadata.getTime() + self.lastSavedMetadataThreshold) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadata = {
|
||||||
|
tip: self.tip ? self.tip.hash : null,
|
||||||
|
tipHeight: self.tip && self.tip.__height ? self.tip.__height : 0,
|
||||||
|
tipWeight: self.tip && self.tip.__weight ? self.tip.__weight.toString(16) : '0',
|
||||||
|
cache: self.cache
|
||||||
|
};
|
||||||
|
|
||||||
|
self.lastSavedMetadata = new Date();
|
||||||
|
|
||||||
|
self.db.putMetadata(metadata, callback);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = Chain;
|
module.exports = Chain;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('util');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var bitcoind = require('bindings')('bitcoind.node');
|
var bitcoind = require('bindings')('bitcoind.node');
|
||||||
var chainlib = require('chainlib');
|
var index = require('./');
|
||||||
var log = chainlib.log;
|
var log = index.log;
|
||||||
var util = require('util');
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
|
|
||||||
|
|
160
lib/db.js
160
lib/db.js
|
@ -1,24 +1,51 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var chainlib = require('chainlib');
|
|
||||||
var BaseDB = chainlib.DB;
|
|
||||||
var Transaction = require('./transaction');
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var levelup = require('levelup');
|
||||||
|
var leveldown = require('leveldown');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
|
var BufferReader = bitcore.encoding.BufferReader;
|
||||||
var BufferWriter = bitcore.encoding.BufferWriter;
|
var BufferWriter = bitcore.encoding.BufferWriter;
|
||||||
var errors = require('./errors');
|
var index = require('./');
|
||||||
var log = chainlib.log;
|
var errors = index.errors;
|
||||||
|
var log = index.log;
|
||||||
|
var Transaction = require('./transaction');
|
||||||
var BaseModule = require('./module');
|
var BaseModule = require('./module');
|
||||||
var AddressModule = require('./modules/address');
|
var AddressModule = require('./modules/address');
|
||||||
|
|
||||||
function DB(options) {
|
function DB(options) {
|
||||||
|
/* jshint maxstatements: 30 */
|
||||||
|
/* jshint maxcomplexity: 20 */
|
||||||
|
|
||||||
|
if (!(this instanceof DB)) {
|
||||||
|
return new DB(options);
|
||||||
|
}
|
||||||
if(!options) {
|
if(!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseDB.call(this, options);
|
this.coinbaseAmount = options.coinbaseAmount || 50 * 1e8;
|
||||||
|
|
||||||
|
var levelupStore = leveldown;
|
||||||
|
|
||||||
|
if(options.store) {
|
||||||
|
levelupStore = options.store;
|
||||||
|
} else if(!options.path) {
|
||||||
|
throw new Error('Please include database path in options');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.store = levelup(options.path, { db: levelupStore });
|
||||||
|
this.chain = options.chain;
|
||||||
|
this.Block = options.Block || require('./block');
|
||||||
|
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.coinbaseAddress = options.coinbaseAddress;
|
||||||
this.coinbaseAmount = options.coinbaseAmount || 50 * 1e8;
|
this.coinbaseAmount = options.coinbaseAmount || 50 * 1e8;
|
||||||
|
@ -40,7 +67,19 @@ function DB(options) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(DB, BaseDB);
|
DB.PREFIXES = {
|
||||||
|
TX: 'tx',
|
||||||
|
PREV_HASH: 'ph',
|
||||||
|
BLOCK: 'blk',
|
||||||
|
DATA: 'data',
|
||||||
|
WEIGHT: 'wt'
|
||||||
|
};
|
||||||
|
|
||||||
|
util.inherits(DB, EventEmitter);
|
||||||
|
|
||||||
|
DB.prototype.initialize = function() {
|
||||||
|
this.emit('ready');
|
||||||
|
};
|
||||||
|
|
||||||
DB.prototype.start = function(callback) {
|
DB.prototype.start = function(callback) {
|
||||||
// Add all db option modules
|
// Add all db option modules
|
||||||
|
@ -151,48 +190,83 @@ DB.prototype._updateWeight = function(hash, weight, callback) {
|
||||||
setImmediate(callback);
|
setImmediate(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.buildGenesisData = function() {
|
/**
|
||||||
var coinbaseTx = this.buildCoinbaseTransaction();
|
* Saves metadata to the database
|
||||||
var bw = new BufferWriter();
|
* @param {Object} metadata - The metadata
|
||||||
bw.writeVarintNum(1);
|
* @param {Function} callback - A function that accepts: Error
|
||||||
bw.write(coinbaseTx.toBuffer());
|
*/
|
||||||
var merkleRoot = this.getMerkleRoot([coinbaseTx]);
|
DB.prototype.putMetadata = function(metadata, callback) {
|
||||||
var buffer = bw.concat();
|
this.store.put('metadata', JSON.stringify(metadata), {}, callback);
|
||||||
return {
|
|
||||||
merkleRoot: merkleRoot,
|
|
||||||
buffer: buffer
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.buildCoinbaseTransaction = function(transactions, data) {
|
/**
|
||||||
if(!this.coinbaseAddress) {
|
* Retrieves metadata from the database
|
||||||
throw new Error('coinbaseAddress required to build coinbase');
|
* @param {Function} callback - A function that accepts: Error and Object
|
||||||
}
|
*/
|
||||||
|
DB.prototype.getMetadata = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if(!data) {
|
self.store.get('metadata', {}, function(err, data) {
|
||||||
data = bitcore.crypto.Random.getRandomBuffer(40);
|
if(err instanceof levelup.errors.NotFoundError) {
|
||||||
}
|
return callback(null, {});
|
||||||
|
} else if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
var fees = 0;
|
var metadata;
|
||||||
|
try {
|
||||||
|
metadata = JSON.parse(data);
|
||||||
|
} catch(e) {
|
||||||
|
return callback(new Error('Could not parse metadata'));
|
||||||
|
}
|
||||||
|
|
||||||
if(transactions && transactions.length) {
|
callback(null, metadata);
|
||||||
fees = this.getInputTotal(transactions) - this.getOutputTotal(transactions, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var coinbaseTx = new this.Transaction();
|
|
||||||
coinbaseTx.to(this.coinbaseAddress, this.coinbaseAmount + fees);
|
|
||||||
|
|
||||||
var script = bitcore.Script.buildDataOut(data);
|
|
||||||
|
|
||||||
var input = new bitcore.Transaction.Input({
|
|
||||||
prevTxId: '0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
outputIndex: 0xffffffff,
|
|
||||||
sequenceNumber: 4294967295,
|
|
||||||
script: script
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
coinbaseTx.inputs = [input];
|
/**
|
||||||
return coinbaseTx;
|
* Closes the underlying store database
|
||||||
|
* @param {Function} callback - A function that accepts: Error
|
||||||
|
*/
|
||||||
|
DB.prototype.close = function(callback) {
|
||||||
|
this.store.close(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
DB.prototype.addTransactionsToBlock = function addTransactionsToBlock(block, transactions) {
|
||||||
|
var txs = this.getTransactionsFromBlock(block);
|
||||||
|
txs = txs.concat(transactions);
|
||||||
|
var txsBuffer = this.Transaction.manyToBuffer(txs);
|
||||||
|
var bw = new BufferWriter();
|
||||||
|
bw.writeVarintNum(txs.length);
|
||||||
|
bw.write(txsBuffer);
|
||||||
|
block.merkleRoot = this.getMerkleRoot(txs);
|
||||||
|
block.data = bw.concat();
|
||||||
|
block.__transactions = txs;
|
||||||
|
};
|
||||||
|
|
||||||
|
DB.prototype.getTransactionsFromBlock = function getTransactionsFromBlock(block) {
|
||||||
|
var self = this;
|
||||||
|
if (block.data.length === 0) {
|
||||||
|
return [];
|
||||||
|
} else if(block.__transactions) {
|
||||||
|
return block.__transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
var br = new BufferReader(block.data);
|
||||||
|
var count = br.readVarintNum();
|
||||||
|
var transactions = [];
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var tx;
|
||||||
|
if (self.Transaction.prototype.fromBufferReader) {
|
||||||
|
tx = self.Transaction().fromBufferReader(br);
|
||||||
|
} else {
|
||||||
|
tx = self.Transaction.fromBufferReader(br);
|
||||||
|
}
|
||||||
|
transactions.push(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.__transactions = transactions;
|
||||||
|
return transactions;
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype.getOutputTotal = function(transactions, excludeCoinbase) {
|
DB.prototype.getOutputTotal = function(transactions, excludeCoinbase) {
|
||||||
|
@ -226,8 +300,6 @@ DB.prototype.getInputTotal = function(transactions) {
|
||||||
DB.prototype._onChainAddBlock = function(block, callback) {
|
DB.prototype._onChainAddBlock = function(block, callback) {
|
||||||
log.debug('DB handling new chain block');
|
log.debug('DB handling new chain block');
|
||||||
|
|
||||||
// Remove block from mempool
|
|
||||||
this.mempool.removeBlock(block.hash);
|
|
||||||
this.blockHandler(block, true, callback);
|
this.blockHandler(block, true, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var createError = require('errno').create;
|
var createError = require('errno').create;
|
||||||
var chainlib = require('chainlib');
|
|
||||||
|
|
||||||
var errors = chainlib.errors;
|
var BitcoreNodeError = createError('BitcoreNodeError');
|
||||||
|
var NoOutputs = createError('NoOutputs', BitcoreNodeError);
|
||||||
|
var NoOutput = createError('NoOutput', BitcoreNodeError);
|
||||||
|
|
||||||
errors.Transaction = createError('Transaction', errors.Error);
|
var Wallet = createError('WalletError', BitcoreNodeError);
|
||||||
errors.Transaction.NotFound = createError('NotFound', errors.Transaction);
|
Wallet.InsufficientFunds = createError('InsufficientFunds', Wallet);
|
||||||
|
|
||||||
module.exports = errors;
|
var Consensus = createError('Consensus', BitcoreNodeError);
|
||||||
|
Consensus.BlockExists = createError('BlockExists', Consensus);
|
||||||
|
|
||||||
|
var Transaction = createError('Transaction', BitcoreNodeError);
|
||||||
|
Transaction.NotFound = createError('NotFound', Transaction);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Error: BitcoreNodeError,
|
||||||
|
NoOutputs: NoOutputs,
|
||||||
|
NoOutput: NoOutput,
|
||||||
|
Wallet: Wallet,
|
||||||
|
Consensus: Consensus,
|
||||||
|
Transaction: Transaction
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
var Logger = require('./logger');
|
||||||
|
module.exports.errors = require('./errors');
|
||||||
|
module.exports.log = new Logger();
|
|
@ -0,0 +1,62 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var colors = require('colors/safe');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps console.log with some special magic
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function Logger(namespace) {
|
||||||
|
this.namespace = namespace || 'bitcore-node';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an info message
|
||||||
|
* #info
|
||||||
|
*/
|
||||||
|
Logger.prototype.info = function() {
|
||||||
|
this._log.apply(this, ['blue', 'info'].concat(Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an error message
|
||||||
|
* #error
|
||||||
|
*/
|
||||||
|
Logger.prototype.error = function() {
|
||||||
|
this._log.apply(this, ['red', 'error'].concat(Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an debug message
|
||||||
|
* #debug
|
||||||
|
*/
|
||||||
|
Logger.prototype.debug = function() {
|
||||||
|
this._log.apply(this, ['magenta', 'debug'].concat(Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an warn message
|
||||||
|
* #warn
|
||||||
|
*/
|
||||||
|
Logger.prototype.warn = function() {
|
||||||
|
this._log.apply(this, ['yellow', 'warn'].concat(Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxies console.log with color and arg parsing magic
|
||||||
|
* #_log
|
||||||
|
*/
|
||||||
|
Logger.prototype._log = function(color, type) {
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
args = args.slice(1);
|
||||||
|
var name = colors.bold('{' + this.namespace + '}');
|
||||||
|
var type = colors[color].italic(args.shift() + ':');
|
||||||
|
args[0] = name + ' ' + type + ' ' + args[0];
|
||||||
|
console.log.apply(console, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Logger;
|
|
@ -3,10 +3,10 @@
|
||||||
var BaseModule = require('../module');
|
var BaseModule = require('../module');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var chainlib = require('chainlib');
|
var index = require('../');
|
||||||
var log = chainlib.log;
|
var log = index.log;
|
||||||
var levelup = chainlib.deps.levelup;
|
var levelup = require('levelup');
|
||||||
var errors = chainlib.errors;
|
var errors = index.errors;
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = bitcore.deps._;
|
||||||
|
|
50
lib/node.js
50
lib/node.js
|
@ -1,28 +1,39 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
|
||||||
var Chain = require('./chain');
|
|
||||||
var Block = require('./block');
|
|
||||||
var DB = require('./db');
|
|
||||||
var chainlib = require('chainlib');
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var BaseNode = chainlib.Node;
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
var async = require('async');
|
||||||
var mkdirp = require('mkdirp');
|
var mkdirp = require('mkdirp');
|
||||||
var log = chainlib.log;
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var Networks = bitcore.Networks;
|
var Networks = bitcore.Networks;
|
||||||
var _ = bitcore.deps._;
|
var _ = bitcore.deps._;
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = bitcore.util.preconditions;
|
||||||
|
var Chain = require('./chain');
|
||||||
|
var Block = require('./block');
|
||||||
|
var DB = require('./db');
|
||||||
|
var index = require('./');
|
||||||
|
var log = index.log;
|
||||||
var daemon = require('./daemon');
|
var daemon = require('./daemon');
|
||||||
var Bus = require('./bus');
|
var Bus = require('./bus');
|
||||||
|
|
||||||
function Node(config) {
|
function Node(config) {
|
||||||
BaseNode.call(this, config);
|
if(!(this instanceof Node)) {
|
||||||
|
return new Node(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.db = null;
|
||||||
|
this.chain = null;
|
||||||
|
this.p2p = null;
|
||||||
|
this.network = null;
|
||||||
|
|
||||||
|
this._loadConfiguration(config);
|
||||||
|
this._initialize();
|
||||||
|
|
||||||
this.testnet = config.testnet;
|
this.testnet = config.testnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(Node, BaseNode);
|
util.inherits(Node, EventEmitter);
|
||||||
|
|
||||||
Node.prototype.openBus = function() {
|
Node.prototype.openBus = function() {
|
||||||
return new Bus({db: this.db});
|
return new Bus({db: this.db});
|
||||||
|
@ -47,10 +58,12 @@ Node.prototype.getAllPublishEvents = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype._loadConfiguration = function(config) {
|
Node.prototype._loadConfiguration = function(config) {
|
||||||
var self = this;
|
|
||||||
this._loadBitcoinConf(config);
|
this._loadBitcoinConf(config);
|
||||||
this._loadBitcoind(config);
|
this._loadBitcoind(config);
|
||||||
Node.super_.prototype._loadConfiguration.call(self, config);
|
this._loadNetwork(config);
|
||||||
|
this._loadDB(config);
|
||||||
|
this._loadAPI();
|
||||||
|
this._loadConsensus(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.DEFAULT_DAEMON_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n';
|
Node.DEFAULT_DAEMON_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n';
|
||||||
|
@ -360,6 +373,21 @@ 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;
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var socketio = require('socket.io');
|
var socketio = require('socket.io');
|
||||||
var BitcoreNode = require('../node');
|
var BitcoreNode = require('../node');
|
||||||
var chainlib = require('chainlib');
|
var index = require('../');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var _ = bitcore.deps._;
|
var _ = bitcore.deps._;
|
||||||
var log = chainlib.log;
|
var log = index.log;
|
||||||
log.debug = function() {};
|
log.debug = function() {};
|
||||||
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var levelup = require('levelup');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var chainlib = require('chainlib');
|
var BufferWriter = bitcore.encoding.BufferWriter;
|
||||||
var BaseTransaction = chainlib.Transaction;
|
|
||||||
var BaseDatabase = chainlib.DB;
|
|
||||||
var levelup = chainlib.deps.levelup;
|
|
||||||
var _ = bitcore.deps._;
|
|
||||||
|
|
||||||
Transaction.prototype.populateInputs = function(db, poolTransactions, callback) {
|
Transaction.prototype.populateInputs = function(db, poolTransactions, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -62,7 +59,12 @@ Transaction.prototype._checkSpent = function(db, input, poolTransactions, callba
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.manyToBuffer = function(transactions) {
|
Transaction.manyToBuffer = function(transactions) {
|
||||||
return BaseTransaction.manyToBuffer(transactions);
|
var bw = new BufferWriter();
|
||||||
|
var count = transactions.length;
|
||||||
|
for(var i = 0; i < count; i++) {
|
||||||
|
transactions[i].toBufferWriter(bw);
|
||||||
|
}
|
||||||
|
return bw.concat();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Transaction;
|
module.exports = Transaction;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var MAX_SAFE_INTEGER = 0x1fffffffffffff; // 2 ^ 53 - 1
|
||||||
|
|
||||||
|
var utils = {};
|
||||||
|
utils.isHash = function isHash(value) {
|
||||||
|
return typeof value === 'string' && value.length === 64 && /^[0-9a-fA-F]+$/.test(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.isSafeNatural = function isSafeNatural(value) {
|
||||||
|
return typeof value === 'number' &&
|
||||||
|
isFinite(value) &&
|
||||||
|
Math.floor(value) === value &&
|
||||||
|
value >= 0 &&
|
||||||
|
value <= MAX_SAFE_INTEGER;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils.startAtZero = function startAtZero(obj, key) {
|
||||||
|
if (!obj.hasOwnProperty(key)) {
|
||||||
|
obj[key] = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = utils;
|
|
@ -44,12 +44,14 @@
|
||||||
"bitcoind"
|
"bitcoind"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "1.3.0",
|
"async": "^1.3.0",
|
||||||
"bindings": "^1.2.1",
|
"bindings": "^1.2.1",
|
||||||
"bitcore": "^0.13.0",
|
"bitcore": "^0.13.0",
|
||||||
"chainlib": "^0.2.0",
|
"colors": "^1.1.2",
|
||||||
"commander": "^2.8.1",
|
"commander": "^2.8.1",
|
||||||
"errno": "^0.1.2",
|
"errno": "^0.1.4",
|
||||||
|
"leveldown": "^1.4.1",
|
||||||
|
"levelup": "^1.2.1",
|
||||||
"liftoff": "^2.1.0",
|
"liftoff": "^2.1.0",
|
||||||
"memdown": "^1.0.0",
|
"memdown": "^1.0.0",
|
||||||
"mkdirp": "0.5.0",
|
"mkdirp": "0.5.0",
|
||||||
|
|
|
@ -3,21 +3,21 @@
|
||||||
var chai = require('chai');
|
var chai = require('chai');
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var async = require('async');
|
|
||||||
var proxyquire = require('proxyquire');
|
|
||||||
var memdown = require('memdown');
|
var memdown = require('memdown');
|
||||||
|
|
||||||
var bitcoindjs = require('../');
|
var index = require('../');
|
||||||
var DB = bitcoindjs.DB;
|
var DB = index.DB;
|
||||||
var Chain = bitcoindjs.Chain;
|
var Chain = index.Chain;
|
||||||
var Block = bitcoindjs.Block;
|
var Block = index.Block;
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var BN = bitcore.crypto.BN;
|
||||||
|
|
||||||
var chainData = require('./data/testnet-blocks.json');
|
var chainData = require('./data/testnet-blocks.json');
|
||||||
|
|
||||||
describe('Bitcoin Chain', function() {
|
describe('Bitcoin Chain', function() {
|
||||||
|
|
||||||
describe('@constructor', function() {
|
describe('@constructor', function() {
|
||||||
|
|
||||||
it('can create a new instance with and without `new`', function() {
|
it('can create a new instance with and without `new`', function() {
|
||||||
var chain = new Chain();
|
var chain = new Chain();
|
||||||
chain = Chain();
|
chain = Chain();
|
||||||
|
@ -39,6 +39,125 @@ describe('Bitcoin Chain', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#initialize', function() {
|
||||||
|
|
||||||
|
it('should initialize the chain with the genesis block if no metadata is found in the db', function(done) {
|
||||||
|
var db = {};
|
||||||
|
db.getMetadata = sinon.stub().callsArgWith(0, null, {});
|
||||||
|
db.putBlock = sinon.stub().callsArg(1);
|
||||||
|
db.putMetadata = sinon.stub().callsArg(1);
|
||||||
|
db.getTransactionsFromBlock = sinon.stub();
|
||||||
|
db._onChainAddBlock = sinon.stub().callsArg(1);
|
||||||
|
db.mempool = {
|
||||||
|
on: sinon.spy()
|
||||||
|
};
|
||||||
|
var chain = new Chain({db: db, genesis: {hash: 'genesis'}});
|
||||||
|
|
||||||
|
chain.on('ready', function() {
|
||||||
|
should.exist(chain.tip);
|
||||||
|
db.putBlock.callCount.should.equal(1);
|
||||||
|
chain.tip.hash.should.equal('genesis');
|
||||||
|
Number(chain.tip.__weight.toString(10)).should.equal(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
chain.on('error', function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
chain.initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize the chain with the metadata from the database if it exists', function(done) {
|
||||||
|
var db = {};
|
||||||
|
db.getMetadata = sinon.stub().callsArgWith(0, null, {tip: 'block2', tipWeight: 2});
|
||||||
|
db.putBlock = sinon.stub().callsArg(1);
|
||||||
|
db.putMetadata = sinon.stub().callsArg(1);
|
||||||
|
db.getBlock = sinon.stub().callsArgWith(1, null, {hash: 'block2', prevHash: 'block1'});
|
||||||
|
db.getTransactionsFromBlock = sinon.stub();
|
||||||
|
db.mempool = {
|
||||||
|
on: sinon.spy()
|
||||||
|
};
|
||||||
|
var chain = new Chain({db: db, genesis: {hash: 'genesis'}});
|
||||||
|
chain.getHeightForBlock = sinon.stub().callsArgWith(1, null, 10);
|
||||||
|
chain.getWeight = sinon.stub().callsArgWith(1, null, new BN(50));
|
||||||
|
chain.on('ready', function() {
|
||||||
|
should.exist(chain.tip);
|
||||||
|
db.putBlock.callCount.should.equal(0);
|
||||||
|
chain.tip.hash.should.equal('block2');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
chain.on('error', function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
chain.initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emit error from getMetadata', function(done) {
|
||||||
|
var db = {
|
||||||
|
getMetadata: function(cb) {
|
||||||
|
cb(new Error('getMetadataError'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
db.getTransactionsFromBlock = sinon.stub();
|
||||||
|
db.mempool = {
|
||||||
|
on: sinon.spy()
|
||||||
|
};
|
||||||
|
var chain = new Chain({db: db, genesis: {hash: 'genesis'}});
|
||||||
|
chain.on('error', function(error) {
|
||||||
|
should.exist(error);
|
||||||
|
error.message.should.equal('getMetadataError');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
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 chain = new Chain({db: db, 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) {
|
||||||
|
var db = {
|
||||||
|
getMetadata: function(cb) {
|
||||||
|
cb(null, {tip: 'tip'});
|
||||||
|
},
|
||||||
|
getBlock: function(tip, cb) {
|
||||||
|
cb(new Error('getBlockError'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
db.getTransactionsFromBlock = sinon.stub();
|
||||||
|
db.mempool = {
|
||||||
|
on: sinon.spy()
|
||||||
|
};
|
||||||
|
var chain = new Chain({db: db, genesis: {hash: 'genesis'}});
|
||||||
|
chain.on('error', function(error) {
|
||||||
|
should.exist(error);
|
||||||
|
error.message.should.equal('getBlockError');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
chain.initialize();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#stop', function() {
|
describe('#stop', function() {
|
||||||
it('should call the callback', function(done) {
|
it('should call the callback', function(done) {
|
||||||
var chain = new Chain();
|
var chain = new Chain();
|
||||||
|
@ -71,44 +190,6 @@ describe('Bitcoin Chain', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#buildGenesisBlock', function() {
|
|
||||||
it('can handle no options', function() {
|
|
||||||
var db = {
|
|
||||||
buildGenesisData: sinon.stub().returns({})
|
|
||||||
};
|
|
||||||
var chain = new Chain({db: db});
|
|
||||||
var block = chain.buildGenesisBlock();
|
|
||||||
should.exist(block);
|
|
||||||
block.should.be.instanceof(Block);
|
|
||||||
db.buildGenesisData.calledOnce.should.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('set timestamp, nonce, bits, merkleRoot and data of the genesis', function() {
|
|
||||||
var db = {
|
|
||||||
buildGenesisData: sinon.stub().returns({
|
|
||||||
merkleRoot: 'merkleRoot',
|
|
||||||
buffer: new Buffer('abcdef', 'hex')
|
|
||||||
})
|
|
||||||
};
|
|
||||||
var chain = new Chain({db: db});
|
|
||||||
var timestamp = '2015-03-20T14:46:01.118Z';
|
|
||||||
var block = chain.buildGenesisBlock({
|
|
||||||
timestamp: timestamp,
|
|
||||||
nonce: 1,
|
|
||||||
bits: 520617984
|
|
||||||
});
|
|
||||||
should.exist(block);
|
|
||||||
block.should.be.instanceof(Block);
|
|
||||||
block.timestamp.toISOString().should.equal(timestamp);
|
|
||||||
block.nonce.should.equal(1);
|
|
||||||
block.bits.should.equal(520617984);
|
|
||||||
block.merkleRoot.should.equal('merkleRoot');
|
|
||||||
block.data.should.deep.equal(new Buffer('abcdef', 'hex'));
|
|
||||||
db.buildGenesisData.calledOnce.should.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getWeight', function() {
|
describe('#getWeight', function() {
|
||||||
var work = '000000000000000000000000000000000000000000005a7b3c42ea8b844374e9';
|
var work = '000000000000000000000000000000000000000000005a7b3c42ea8b844374e9';
|
||||||
var chain = new Chain();
|
var chain = new Chain();
|
||||||
|
@ -136,4 +217,53 @@ describe('Bitcoin Chain', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#getHashes', function() {
|
||||||
|
|
||||||
|
it('should get an array of chain hashes', function(done) {
|
||||||
|
|
||||||
|
var blocks = {};
|
||||||
|
var genesisBlock = Block.fromBuffer(new Buffer(chainData[0], 'hex'));
|
||||||
|
var block1 = Block.fromBuffer(new Buffer(chainData[1], 'hex'));
|
||||||
|
var block2 = Block.fromBuffer(new Buffer(chainData[2], 'hex'));
|
||||||
|
blocks[genesisBlock.hash] = genesisBlock;
|
||||||
|
blocks[block1.hash] = block1;
|
||||||
|
blocks[block2.hash] = block2;
|
||||||
|
|
||||||
|
var db = new DB({store: memdown});
|
||||||
|
db.getPrevHash = function(blockHash, cb) {
|
||||||
|
cb(null, blocks[blockHash].prevHash);
|
||||||
|
};
|
||||||
|
|
||||||
|
var chain = new Chain({
|
||||||
|
db: db,
|
||||||
|
genesis: genesisBlock
|
||||||
|
});
|
||||||
|
|
||||||
|
chain.tip = block2;
|
||||||
|
|
||||||
|
chain.on('ready', function() {
|
||||||
|
|
||||||
|
// remove one of the cached hashes to force db call
|
||||||
|
delete chain.cache.hashes[block1.hash];
|
||||||
|
|
||||||
|
// the test
|
||||||
|
chain.getHashes(block2.hash, function(err, hashes) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(hashes);
|
||||||
|
hashes.length.should.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
chain.on('error', function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
chain.initialize();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -227,90 +227,6 @@ describe('Bitcoin DB', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#buildGenesisData', function() {
|
|
||||||
it('build genisis data', function() {
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
db.buildCoinbaseTransaction = sinon.stub().returns({
|
|
||||||
toBuffer: sinon.stub().returns(new Buffer('abcdef', 'hex'))
|
|
||||||
});
|
|
||||||
db.getMerkleRoot = sinon.stub().returns('merkleRoot');
|
|
||||||
var data = db.buildGenesisData();
|
|
||||||
data.buffer.should.deep.equal(new Buffer('01abcdef', 'hex'));
|
|
||||||
data.merkleRoot.should.equal('merkleRoot');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#buildCoinbaseTransaction', function() {
|
|
||||||
it('should correctly build a coinbase transaction with no fees', function() {
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
db.coinbaseAddress = 'mzso6uXxfDCq4L6xAffUD9BPWo6bdFBZ2L';
|
|
||||||
db.coinbaseAmount = coinbaseAmount;
|
|
||||||
var coinbaseTx = db.buildCoinbaseTransaction();
|
|
||||||
coinbaseTx.inputs.length.should.equal(1);
|
|
||||||
var input = coinbaseTx.inputs[0];
|
|
||||||
var expectedTxId = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
||||||
input.prevTxId.toString('hex').should.equal(expectedTxId);
|
|
||||||
should.exist(input.outputIndex);
|
|
||||||
should.exist(input.sequenceNumber);
|
|
||||||
should.exist(input._script); // coinbase input script returns null
|
|
||||||
coinbaseTx.outputs.length.should.equal(1);
|
|
||||||
var output = coinbaseTx.outputs[0];
|
|
||||||
output.satoshis.should.equal(coinbaseAmount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should correctly build a coinbase transaction with fees', function() {
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
db.coinbaseAddress = 'mzso6uXxfDCq4L6xAffUD9BPWo6bdFBZ2L';
|
|
||||||
db.coinbaseAmount = coinbaseAmount;
|
|
||||||
var transactions = [
|
|
||||||
{
|
|
||||||
_getInputAmount: sinon.stub().returns(5000),
|
|
||||||
_getOutputAmount: sinon.stub().returns(4000),
|
|
||||||
isCoinbase: sinon.stub().returns(false)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_getInputAmount: sinon.stub().returns(8000),
|
|
||||||
_getOutputAmount: sinon.stub().returns(7000),
|
|
||||||
isCoinbase: sinon.stub().returns(false)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
var coinbaseTx = db.buildCoinbaseTransaction(transactions);
|
|
||||||
coinbaseTx.inputs.length.should.equal(1);
|
|
||||||
var input = coinbaseTx.inputs[0];
|
|
||||||
var expectedTxId = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
||||||
input.prevTxId.toString('hex').should.equal(expectedTxId);
|
|
||||||
should.exist(input.outputIndex);
|
|
||||||
should.exist(input.sequenceNumber);
|
|
||||||
should.exist(input._script); // coinbase input returns null
|
|
||||||
coinbaseTx.outputs.length.should.equal(1);
|
|
||||||
var output = coinbaseTx.outputs[0];
|
|
||||||
output.satoshis.should.equal(coinbaseAmount + 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if coinbaseAddress not included', function() {
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
(function() {
|
|
||||||
db.buildCoinbaseTransaction();
|
|
||||||
}).should.throw('coinbaseAddress required to build coinbase');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('will build a coinbase database with different data', function() {
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
db.coinbaseAddress = 'mzso6uXxfDCq4L6xAffUD9BPWo6bdFBZ2L';
|
|
||||||
var tx1 = db.buildCoinbaseTransaction().uncheckedSerialize();
|
|
||||||
var tx2 = db.buildCoinbaseTransaction().uncheckedSerialize();
|
|
||||||
tx1.should.not.equal(tx2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can pass in custom data', function() {
|
|
||||||
var db = new DB({path: 'path', store: memdown});
|
|
||||||
db.coinbaseAddress = 'mzso6uXxfDCq4L6xAffUD9BPWo6bdFBZ2L';
|
|
||||||
var tx1 = db.buildCoinbaseTransaction(null, new Buffer('abcdef', 'hex'));
|
|
||||||
var data = tx1.inputs[0]._script.getData();
|
|
||||||
data.should.deep.equal(new Buffer('abcdef', 'hex'));
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getOutputTotal', function() {
|
describe('#getOutputTotal', function() {
|
||||||
it('should return the correct value including the coinbase', function() {
|
it('should return the correct value including the coinbase', function() {
|
||||||
|
@ -343,7 +259,7 @@ describe('Bitcoin DB', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
var grandTotal = db.getOutputTotal(transactions, true);
|
var grandTotal = db.getOutputTotal(transactions, true);
|
||||||
grandTotal.should.equal(50)
|
grandTotal.should.equal(50);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -381,7 +297,6 @@ describe('Bitcoin DB', function() {
|
||||||
db.blockHandler = sinon.stub().callsArg(2);
|
db.blockHandler = sinon.stub().callsArg(2);
|
||||||
db._onChainAddBlock({hash: 'hash'}, function(err) {
|
db._onChainAddBlock({hash: 'hash'}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
db.mempool.removeBlock.args[0][0].should.equal('hash');
|
|
||||||
db.blockHandler.args[0][1].should.equal(true);
|
db.blockHandler.args[0][1].should.equal(true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,8 +8,7 @@ var blockData = require('../data/livenet-345003.json');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var errors = bitcorenode.errors;
|
var errors = bitcorenode.errors;
|
||||||
var chainlib = require('chainlib');
|
var levelup = require('levelup');
|
||||||
var levelup = chainlib.deps.levelup;
|
|
||||||
|
|
||||||
var mockdb = {
|
var mockdb = {
|
||||||
bitcoind: {
|
bitcoind: {
|
||||||
|
|
|
@ -2,42 +2,42 @@
|
||||||
|
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var util = require('util');
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var Networks = bitcore.Networks;
|
var Networks = bitcore.Networks;
|
||||||
var blockData = require('./data/livenet-345003.json');
|
var blockData = require('./data/livenet-345003.json');
|
||||||
var Block = require('../lib/block');
|
var Block = require('../lib/block');
|
||||||
var proxyquire = require('proxyquire');
|
var proxyquire = require('proxyquire');
|
||||||
var chainlib = require('chainlib');
|
var index = require('..');
|
||||||
var OriginalNode = chainlib.Node;
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var bitcoinConfBuffer = fs.readFileSync(__dirname + '/data/bitcoin.conf');
|
var bitcoinConfBuffer = fs.readFileSync(__dirname + '/data/bitcoin.conf');
|
||||||
var chainHashes = require('./data/hashes.json');
|
var chainHashes = require('./data/hashes.json');
|
||||||
|
|
||||||
var BaseNode = function() {};
|
|
||||||
util.inherits(BaseNode, EventEmitter);
|
|
||||||
BaseNode.log = chainlib.log;
|
|
||||||
BaseNode.prototype._loadConfiguration = sinon.spy();
|
|
||||||
BaseNode.prototype._initialize = sinon.spy();
|
|
||||||
chainlib.Node = BaseNode;
|
|
||||||
|
|
||||||
var BadNode = proxyquire('../lib/node', {
|
|
||||||
chainlib: chainlib,
|
|
||||||
fs: {
|
|
||||||
readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/data/badbitcoin.conf'))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Node = proxyquire('../lib/node', {
|
|
||||||
chainlib: chainlib,
|
|
||||||
fs: {
|
|
||||||
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
chainlib.Node = OriginalNode;
|
|
||||||
|
|
||||||
describe('Bitcoind Node', function() {
|
describe('Bitcoind Node', function() {
|
||||||
|
|
||||||
|
var Node;
|
||||||
|
var BadNode;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
|
||||||
|
BadNode = proxyquire('../lib/node', {
|
||||||
|
fs: {
|
||||||
|
readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/data/badbitcoin.conf'))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
BadNode.prototype._loadConfiguration = sinon.spy();
|
||||||
|
BadNode.prototype._initialize = sinon.spy();
|
||||||
|
|
||||||
|
Node = proxyquire('../lib/node', {
|
||||||
|
fs: {
|
||||||
|
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Node.prototype._loadConfiguration = sinon.spy();
|
||||||
|
Node.prototype._initialize = sinon.spy();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('#openBus', function() {
|
describe('#openBus', function() {
|
||||||
it('will create a new bus', function() {
|
it('will create a new bus', function() {
|
||||||
var node = new Node({});
|
var node = new Node({});
|
||||||
|
@ -89,13 +89,23 @@ describe('Bitcoind Node', function() {
|
||||||
});
|
});
|
||||||
describe('#_loadConfiguration', function() {
|
describe('#_loadConfiguration', function() {
|
||||||
it('should call the necessary methods', function() {
|
it('should call the necessary methods', function() {
|
||||||
var node = new Node({});
|
var TestNode = proxyquire('../lib/node', {
|
||||||
node._loadBitcoinConf = sinon.spy();
|
fs: {
|
||||||
node._loadBitcoind = sinon.spy();
|
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
||||||
node._loadConfiguration({});
|
}
|
||||||
node._loadBitcoind.called.should.equal(true);
|
});
|
||||||
node._loadBitcoinConf.called.should.equal(true);
|
TestNode.prototype._initialize = sinon.spy();
|
||||||
BaseNode.prototype._loadConfiguration.called.should.equal(true);
|
TestNode.prototype._loadBitcoinConf = sinon.spy();
|
||||||
|
TestNode.prototype._loadBitcoind = sinon.spy();
|
||||||
|
TestNode.prototype._loadDB = sinon.spy();
|
||||||
|
TestNode.prototype._loadAPI = sinon.spy();
|
||||||
|
TestNode.prototype._loadConsensus = sinon.spy();
|
||||||
|
var node = new TestNode({});
|
||||||
|
node._loadBitcoind.callCount.should.equal(1);
|
||||||
|
node._loadBitcoinConf.callCount.should.equal(1);
|
||||||
|
node._loadDB.callCount.should.equal(1);
|
||||||
|
node._loadAPI.callCount.should.equal(1);
|
||||||
|
node._loadConsensus.callCount.should.equal(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#_loadBitcoinConf', function() {
|
describe('#_loadBitcoinConf', function() {
|
||||||
|
@ -432,7 +442,12 @@ describe('Bitcoind Node', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#_loadConsensus', function() {
|
describe('#_loadConsensus', function() {
|
||||||
var node = new Node({});
|
|
||||||
|
var node;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
node = new Node({});
|
||||||
|
});
|
||||||
|
|
||||||
it('will set properties', function() {
|
it('will set properties', function() {
|
||||||
node._loadConsensus();
|
node._loadConsensus();
|
||||||
|
@ -447,7 +462,21 @@ describe('Bitcoind Node', function() {
|
||||||
var node;
|
var node;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
node = new Node({});
|
var TestNode = proxyquire('../lib/node', {
|
||||||
|
fs: {
|
||||||
|
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TestNode.prototype._loadConfiguration = sinon.spy();
|
||||||
|
TestNode.prototype._initializeBitcoind = sinon.spy();
|
||||||
|
TestNode.prototype._initializeDatabase = sinon.spy();
|
||||||
|
TestNode.prototype._initializeChain = sinon.spy();
|
||||||
|
|
||||||
|
// mock the _initialize during construction
|
||||||
|
var _initialize = TestNode.prototype._initialize;
|
||||||
|
TestNode.prototype._initialize = sinon.spy();
|
||||||
|
|
||||||
|
node = new TestNode({});
|
||||||
node.chain = {
|
node.chain = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
|
@ -455,12 +484,12 @@ describe('Bitcoind Node', function() {
|
||||||
node.bitcoind = {
|
node.bitcoind = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
node._initializeBitcoind = sinon.spy();
|
|
||||||
node._initializeDatabase = sinon.spy();
|
|
||||||
node._initializeChain = sinon.spy();
|
|
||||||
node.db = {
|
node.db = {
|
||||||
on: sinon.spy()
|
on: sinon.spy()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// restore the original method
|
||||||
|
node._initialize = _initialize;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize', function(done) {
|
it('should initialize', function(done) {
|
||||||
|
@ -544,11 +573,11 @@ describe('Bitcoind Node', function() {
|
||||||
it('will log on ready event', function(done) {
|
it('will log on ready event', function(done) {
|
||||||
var node = new Node({});
|
var node = new Node({});
|
||||||
node.db = new EventEmitter();
|
node.db = new EventEmitter();
|
||||||
sinon.stub(chainlib.log, 'info');
|
sinon.stub(index.log, 'info');
|
||||||
node.db.on('ready', function() {
|
node.db.on('ready', function() {
|
||||||
setImmediate(function() {
|
setImmediate(function() {
|
||||||
chainlib.log.info.callCount.should.equal(1);
|
index.log.info.callCount.should.equal(1);
|
||||||
chainlib.log.info.restore();
|
index.log.info.restore();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,8 +8,7 @@ var transactionData = require('./data/bitcoin-transactions.json');
|
||||||
var memdown = require('memdown');
|
var memdown = require('memdown');
|
||||||
var DB = bitcoinlib.DB;
|
var DB = bitcoinlib.DB;
|
||||||
var db = new DB({store: memdown});
|
var db = new DB({store: memdown});
|
||||||
var chainlib = require('chainlib');
|
var levelup = require('levelup');
|
||||||
var levelup = chainlib.deps.levelup;
|
|
||||||
|
|
||||||
describe('Bitcoin Transaction', function() {
|
describe('Bitcoin Transaction', function() {
|
||||||
describe('#populateInputs', function() {
|
describe('#populateInputs', function() {
|
||||||
|
|
Loading…
Reference in New Issue