bitcore-node-zcash/test/db.unit.js

322 lines
11 KiB
JavaScript

'use strict';
var should = require('chai').should();
var sinon = require('sinon');
var chainlib = require('chainlib');
var levelup = chainlib.deps.levelup;
var bitcoindjs = require('../');
var DB = bitcoindjs.DB;
var blockData = require('./data/livenet-345003.json');
var bitcore = require('bitcore');
var EventEmitter = require('events').EventEmitter;
var errors = bitcoindjs.errors;
var memdown = require('memdown');
var inherits = require('util').inherits;
var BaseModule = require('../lib/module');
describe('Bitcoin DB', function() {
var coinbaseAmount = 50 * 1e8;
describe('#getBlock', function() {
var db = new DB({store: memdown});
db.bitcoind = {
getBlock: sinon.stub().callsArgWith(1, null, new Buffer(blockData, 'hex'))
};
db.Block = {
fromBuffer: sinon.stub().returns('block')
};
it('should get the block from bitcoind.js', function(done) {
db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) {
should.not.exist(err);
block.should.equal('block');
done();
});
});
it('should give an error when bitcoind.js gives an error', function(done) {
db.bitcoind.getBlock = sinon.stub().callsArgWith(1, new Error('error'));
db.getBlock('00000000000000000593b60d8b4f40fd1ec080bdb0817d475dae47b5f5b1f735', function(err, block) {
should.exist(err);
err.message.should.equal('error');
done();
});
});
});
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('#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() {
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) {
var db = new DB({store: memdown});
db.mempool = {
removeBlock: sinon.stub()
};
db.blockHandler = sinon.stub().callsArg(2);
db._onChainAddBlock({hash: 'hash'}, function(err) {
should.not.exist(err);
db.mempool.removeBlock.args[0][0].should.equal('hash');
db.blockHandler.args[0][1].should.equal(true);
done();
});
});
});
describe('#_onChainRemoveBlock', function() {
it('should call blockHandler with false', function(done) {
var db = new DB({store: memdown});
db.blockHandler = sinon.stub().callsArg(2);
db._onChainRemoveBlock({hash: 'hash'}, function(err) {
should.not.exist(err);
db.blockHandler.args[0][1].should.equal(false);
done();
});
});
});
describe('#blockHandler', function() {
var db = new DB({store: memdown});
var Module1 = function() {};
Module1.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op1', 'op2', 'op3']);
var Module2 = function() {};
Module2.prototype.blockHandler = sinon.stub().callsArgWith(2, null, ['op4', 'op5']);
db.modules = [
new Module1(),
new Module2()
];
db.store = {
batch: sinon.stub().callsArg(1)
};
it('should call blockHandler in all modules and perform operations', function(done) {
db.blockHandler('block', true, function(err) {
should.not.exist(err);
db.store.batch.args[0][0].should.deep.equal(['op1', 'op2', 'op3', 'op4', 'op5']);
done();
});
});
it('should give an error if one of the modules gives an error', function(done) {
var Module3 = function() {};
Module3.prototype.blockHandler = sinon.stub().callsArgWith(2, new Error('error'));
db.modules.push(new Module3());
db.blockHandler('block', true, function(err) {
should.exist(err);
done();
});
});
});
describe('#getAPIMethods', function() {
it('should return the correct db methods', function() {
var db = new DB({store: memdown});
db.modules = [];
var methods = db.getAPIMethods();
methods.length.should.equal(2);
});
it('should also return modules API methods', function() {
var module1 = {
getAPIMethods: function() {
return [
['module1-one', module1, module1, 2],
['module1-two', module1, module1, 2]
];
}
};
var module2 = {
getAPIMethods: function() {
return [
['moudle2-one', module2, module2, 1]
];
}
};
var db = new DB({store: memdown});
db.modules = [module1, module2];
var methods = db.getAPIMethods();
methods.length.should.equal(5);
});
});
describe('#addModule', function() {
it('instantiate module and add to db.modules', function() {
var Module1 = function(options) {
BaseModule.call(this, options);
};
inherits(Module1, BaseModule);
var db = new DB({store: memdown});
db.modules = [];
db.addModule(Module1);
db.modules.length.should.equal(1);
should.exist(db.modules[0].db);
});
it('should throw an error if module is not an instance of BaseModule', function() {
var Module2 = function(options) {};
var db = new DB({store: memdown});
db.modules = [];
(function() {
db.addModule(Module2);
}).should.throw('bitcore.ErrorInvalidArgumentType');
});
});
});