Merge remote-tracking branch 'matiu/feature/Block-test'
Conflicts: util/util.js
This commit is contained in:
commit
fa47ee9984
297
Block.js
297
Block.js
|
@ -93,15 +93,11 @@ Block.prototype.checkProofOfWork = function checkProofOfWork() {
|
||||||
|
|
||||||
// TODO: Create a compare method in node-buffertools that uses the correct
|
// TODO: Create a compare method in node-buffertools that uses the correct
|
||||||
// endian so we don't have to reverse both buffers before comparing.
|
// endian so we don't have to reverse both buffers before comparing.
|
||||||
buffertools.reverse(this.hash);
|
var reverseHash = buffertools.reverse(this.hash);
|
||||||
|
if (buffertools.compare(reverseHash, target) > 0) {
|
||||||
if (buffertools.compare(this.hash, target) > 0) {
|
|
||||||
throw new VerificationError('Difficulty target not met');
|
throw new VerificationError('Difficulty target not met');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the hash to its normal order
|
|
||||||
buffertools.reverse(this.hash);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,7 +195,7 @@ Block.prototype.checkMerkleRoot = function checkMerkleRoot(txs) {
|
||||||
throw new VerificationError('No merkle root');
|
throw new VerificationError('No merkle root');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffertools.compare(this.calcMerkleRoot(), this.merkle_root) == 0) {
|
if (buffertools.compare(this.calcMerkleRoot(txs), new Buffer(this.merkle_root)) !== 0) {
|
||||||
throw new VerificationError('Merkle root incorrect');
|
throw new VerificationError('Merkle root incorrect');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,214 +232,6 @@ Block.prototype.toString = function toString() {
|
||||||
return "<Block " + util.formatHashAlt(this.hash) + " height="+this.height+">";
|
return "<Block " + util.formatHashAlt(this.hash) + " height="+this.height+">";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes some properties based on information from the parent block.
|
|
||||||
*/
|
|
||||||
Block.prototype.attachTo = function attachTo(parent) {
|
|
||||||
this.height = parent.height + 1;
|
|
||||||
this.setChainWork(parent.getChainWork().add(this.getWork()));
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.prototype.setChainWork = function setChainWork(chainWork) {
|
|
||||||
if (Buffer.isBuffer(chainWork)) {
|
|
||||||
// Nothing to do
|
|
||||||
} else if ("function" === typeof chainWork.toBuffer) { // duck-typing bignum
|
|
||||||
chainWork = chainWork.toBuffer();
|
|
||||||
} else {
|
|
||||||
throw new Error("Block.setChainWork(): Invalid datatype");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chainWork = chainWork;
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.prototype.getChainWork = function getChainWork() {
|
|
||||||
return Bignum.fromBuffer(this.chainWork);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares the chainWork of two blocks.
|
|
||||||
*/
|
|
||||||
Block.prototype.moreWorkThan = function moreWorkThan(otherBlock) {
|
|
||||||
return this.getChainWork().cmp(otherBlock.getChainWork()) > 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the difficulty target for the next block after this one.
|
|
||||||
*/
|
|
||||||
Block.prototype.getNextWork =
|
|
||||||
function getNextWork(blockChain, nextBlock, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var powLimit = blockChain.getMinDiff();
|
|
||||||
var powLimitTarget = util.decodeDiffBits(powLimit, true);
|
|
||||||
|
|
||||||
var targetTimespan = blockChain.getTargetTimespan();
|
|
||||||
var targetSpacing = blockChain.getTargetSpacing();
|
|
||||||
var interval = targetTimespan / targetSpacing;
|
|
||||||
|
|
||||||
if (this.height == 0) {
|
|
||||||
callback(null, this.bits);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.height+1) % interval !== 0) {
|
|
||||||
if (blockChain.isTestnet()) {
|
|
||||||
// Special testnet difficulty rules
|
|
||||||
var lastBlock = blockChain.getTopBlock();
|
|
||||||
|
|
||||||
// If the new block's timestamp is more than 2 * 10 minutes
|
|
||||||
// then allow mining of a min-difficulty block.
|
|
||||||
if (nextBlock.timestamp > this.timestamp + targetSpacing*2) {
|
|
||||||
callback(null, powLimit);
|
|
||||||
} else {
|
|
||||||
// Return last non-"special-min-difficulty" block
|
|
||||||
if (this.bits != powLimit) {
|
|
||||||
// Current block is non-min-diff
|
|
||||||
callback(null, this.bits);
|
|
||||||
} else {
|
|
||||||
// Recurse backwards until a non min-diff block is found.
|
|
||||||
function lookForLastNonMinDiff(block, callback) {
|
|
||||||
try {
|
|
||||||
if (block.height > 0 &&
|
|
||||||
block.height % interval !== 0 &&
|
|
||||||
block.bits == powLimit) {
|
|
||||||
blockChain.getBlockByHeight(
|
|
||||||
block.height - 1,
|
|
||||||
function (err, lastBlock) {
|
|
||||||
try {
|
|
||||||
if (err) throw err;
|
|
||||||
lookForLastNonMinDiff(lastBlock, callback);
|
|
||||||
} catch (err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
callback(null, block.bits);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
lookForLastNonMinDiff(this, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Not adjustment interval, next block has same difficulty
|
|
||||||
callback(null, this.bits);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Get the first block from the old difficulty period
|
|
||||||
blockChain.getBlockByHeight(
|
|
||||||
this.height - interval + 1,
|
|
||||||
function (err, lastBlock) {
|
|
||||||
try {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
// Determine how long the difficulty period really took
|
|
||||||
var actualTimespan = self.timestamp - lastBlock.timestamp;
|
|
||||||
|
|
||||||
// There are some limits to how much we will adjust the difficulty in
|
|
||||||
// one step
|
|
||||||
if (actualTimespan < targetTimespan/4) {
|
|
||||||
actualTimespan = targetTimespan/4;
|
|
||||||
}
|
|
||||||
if (actualTimespan > targetTimespan*4) {
|
|
||||||
actualTimespan = targetTimespan*4;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldTarget = util.decodeDiffBits(self.bits, true);
|
|
||||||
var newTarget = oldTarget.mul(actualTimespan).div(targetTimespan);
|
|
||||||
|
|
||||||
if (newTarget.cmp(powLimitTarget) > 0) {
|
|
||||||
newTarget = powLimitTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug1('Difficulty retarget (target='+targetTimespan +
|
|
||||||
', actual='+actualTimespan+')');
|
|
||||||
Debug1('Before: '+oldTarget.toBuffer().toString('hex'));
|
|
||||||
Debug1('After: '+newTarget.toBuffer().toString('hex'));
|
|
||||||
|
|
||||||
callback(null, util.encodeDiffBits(newTarget));
|
|
||||||
} catch (err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var medianTimeSpan = 11;
|
|
||||||
|
|
||||||
Block.prototype.getMedianTimePast =
|
|
||||||
function getMedianTimePast(blockChain, callback)
|
|
||||||
{
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
Step(
|
|
||||||
function getBlocks() {
|
|
||||||
var heights = [];
|
|
||||||
for (var i = 0, m = medianTimeSpan; i < m && (self.height - i) >= 0; i++) {
|
|
||||||
heights.push(self.height - i);
|
|
||||||
}
|
|
||||||
blockChain.getBlocksByHeights(heights, this);
|
|
||||||
},
|
|
||||||
function calcMedian(err, blocks) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
var timestamps = blocks.map(function (block) {
|
|
||||||
if (!block) {
|
|
||||||
throw new Error("Prior block missing, cannot calculate median time");
|
|
||||||
}
|
|
||||||
|
|
||||||
return +block.timestamp;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort timestamps
|
|
||||||
timestamps = timestamps.sort();
|
|
||||||
|
|
||||||
// Return median timestamp
|
|
||||||
this(null, timestamps[Math.floor(timestamps.length/2)]);
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.prototype.verifyChild =
|
|
||||||
function verifyChild(blockChain, child, callback)
|
|
||||||
{
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
Step(
|
|
||||||
function getExpectedDifficulty() {
|
|
||||||
self.getNextWork(blockChain, child, this);
|
|
||||||
},
|
|
||||||
function verifyExpectedDifficulty(err, nextWork) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
if (+child.bits !== +nextWork) {
|
|
||||||
throw new VerificationError("Incorrect proof of work '"+child.bits+"',"+
|
|
||||||
" should be '"+nextWork+"'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this();
|
|
||||||
},
|
|
||||||
function getMinimumTimestamp(err) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
self.getMedianTimePast(blockChain, this);
|
|
||||||
},
|
|
||||||
function verifyTimestamp(err, medianTimePast) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
if (child.timestamp <= medianTimePast) {
|
|
||||||
throw new VerificationError("Block's timestamp is too early");
|
|
||||||
}
|
|
||||||
|
|
||||||
this();
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.prototype.createCoinbaseTx =
|
Block.prototype.createCoinbaseTx =
|
||||||
function createCoinbaseTx(beneficiary)
|
function createCoinbaseTx(beneficiary)
|
||||||
|
@ -461,85 +249,6 @@ function createCoinbaseTx(beneficiary)
|
||||||
return tx;
|
return tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
Block.prototype.prepareNextBlock =
|
|
||||||
function prepareNextBlock(blockChain, beneficiary, time, callback)
|
|
||||||
{
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var newBlock = new Block();
|
|
||||||
Step(
|
|
||||||
function getMedianTimePastStep() {
|
|
||||||
self.getMedianTimePast(blockChain, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
function getNextWorkStep(err, medianTimePast) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
if (!time) {
|
|
||||||
// TODO: Use getAdjustedTime for the second timestamp
|
|
||||||
time = Math.max(medianTimePast+1,
|
|
||||||
Math.floor(new Date().getTime() / 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.getNextWork(blockChain, newBlock, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
function applyNextWorkStep(err, nextWork) {
|
|
||||||
if (err) throw err;
|
|
||||||
newBlock.bits = nextWork;
|
|
||||||
this(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
function miscStep(err) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
newBlock.version = 1;
|
|
||||||
newBlock.timestamp = time;
|
|
||||||
newBlock.prev_hash = self.getHash().slice(0);
|
|
||||||
newBlock.height = self.height+1;
|
|
||||||
|
|
||||||
// Create coinbase transaction
|
|
||||||
var txs = [];
|
|
||||||
|
|
||||||
var tx = newBlock.createCoinbaseTx(beneficiary);
|
|
||||||
txs.push(tx);
|
|
||||||
|
|
||||||
newBlock.merkle_root = newBlock.calcMerkleRoot(txs);
|
|
||||||
|
|
||||||
// Return reference to (unfinished) block
|
|
||||||
this(null, {block: newBlock, txs: txs});
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.prototype.mineNextBlock =
|
|
||||||
function mineNextBlock(blockChain, beneficiary, time, miner, callback)
|
|
||||||
{
|
|
||||||
this.prepareNextBlock(blockChain, beneficiary, time, function (err, data) {
|
|
||||||
try {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
var newBlock = data.block;
|
|
||||||
var txs = data.txs;
|
|
||||||
|
|
||||||
newBlock.solve(miner, function (err, nonce) {
|
|
||||||
newBlock.nonce = nonce;
|
|
||||||
|
|
||||||
// Make sure hash is cached
|
|
||||||
newBlock.getHash();
|
|
||||||
|
|
||||||
callback(err, newBlock, txs);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return reference to (unfinished) block
|
|
||||||
return newBlock;
|
|
||||||
} catch (e) {
|
|
||||||
callback(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Block.prototype.solve = function solve(miner, callback) {
|
Block.prototype.solve = function solve(miner, callback) {
|
||||||
var header = this.getHeader();
|
var header = this.getHeader();
|
||||||
var target = util.decodeDiffBits(this.bits);
|
var target = util.decodeDiffBits(this.bits);
|
||||||
|
|
|
@ -16,7 +16,8 @@ module.exports = function(grunt) {
|
||||||
stdout: true,
|
stdout: true,
|
||||||
stderr: true
|
stderr: true
|
||||||
},
|
},
|
||||||
command: 'node ./browser/build.js -a',
|
command: grunt.option('target') === 'dev' ?
|
||||||
|
'node ./browser/build.js -a -d ' : 'node ./browser/build.js -a'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -18,6 +18,8 @@ var PrivateKey = imports.PrivateKey || require('./PrivateKey');
|
||||||
var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]);
|
var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]);
|
||||||
var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN);
|
var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN);
|
||||||
|
|
||||||
|
Transaction.COINBASE_OP = COINBASE_OP;
|
||||||
|
|
||||||
function TransactionIn(data) {
|
function TransactionIn(data) {
|
||||||
if ("object" !== typeof data) {
|
if ("object" !== typeof data) {
|
||||||
data = {};
|
data = {};
|
||||||
|
@ -43,7 +45,10 @@ TransactionIn.prototype.getScript = function getScript() {
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionIn.prototype.isCoinBase = function isCoinBase() {
|
TransactionIn.prototype.isCoinBase = function isCoinBase() {
|
||||||
return buffertools.compare(this.o, COINBASE_OP) === 0;
|
if (!this.o) return false;
|
||||||
|
|
||||||
|
//The new Buffer is for Firefox compatibility
|
||||||
|
return buffertools.compare(new Buffer(this.o), COINBASE_OP) === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionIn.prototype.serialize = function serialize() {
|
TransactionIn.prototype.serialize = function serialize() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ requireWhenAccessed('networks', './networks');
|
||||||
requireWhenAccessed('util', './util/util');
|
requireWhenAccessed('util', './util/util');
|
||||||
requireWhenAccessed('EncodedData', './util/EncodedData');
|
requireWhenAccessed('EncodedData', './util/EncodedData');
|
||||||
requireWhenAccessed('VersionedData', './util/VersionedData');
|
requireWhenAccessed('VersionedData', './util/VersionedData');
|
||||||
|
requireWhenAccessed('BinaryParser', './util/BinaryParser');
|
||||||
requireWhenAccessed('Address', './Address');
|
requireWhenAccessed('Address', './Address');
|
||||||
requireWhenAccessed('Opcode', './Opcode');
|
requireWhenAccessed('Opcode', './Opcode');
|
||||||
requireWhenAccessed('Script', './Script');
|
requireWhenAccessed('Script', './Script');
|
||||||
|
|
|
@ -52,6 +52,7 @@ var modules = [
|
||||||
'util/util',
|
'util/util',
|
||||||
'util/EncodedData',
|
'util/EncodedData',
|
||||||
'util/VersionedData',
|
'util/VersionedData',
|
||||||
|
'util/BinaryParser',
|
||||||
];
|
];
|
||||||
|
|
||||||
var createBitcore = function(opts) {
|
var createBitcore = function(opts) {
|
||||||
|
|
Binary file not shown.
|
@ -4,9 +4,33 @@ var chai = chai || require('chai');
|
||||||
var bitcore = bitcore || require('../bitcore');
|
var bitcore = bitcore || require('../bitcore');
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
|
|
||||||
|
var testdata = testdata || require('./testdata');
|
||||||
var BlockModule = bitcore.Block;
|
var BlockModule = bitcore.Block;
|
||||||
|
var BinaryParser = bitcore.BinaryParser;
|
||||||
var Block;
|
var Block;
|
||||||
|
|
||||||
|
|
||||||
|
var getBlock = function (onlyHeader) {
|
||||||
|
|
||||||
|
var testnetMagic = bitcore.networks.testnet.magic.toString('hex');
|
||||||
|
|
||||||
|
var b = new Block();
|
||||||
|
// this is block 86756 from testnet3
|
||||||
|
var p = new BinaryParser(testdata.dataRawBlock);
|
||||||
|
|
||||||
|
|
||||||
|
var magic = p.buffer(4).toString('hex');
|
||||||
|
|
||||||
|
|
||||||
|
if (magic !== testnetMagic )
|
||||||
|
throw new Error('CRITICAL ERROR: Magic number mismatch: ' +
|
||||||
|
magic + ' : ' + testnetMagic);
|
||||||
|
|
||||||
|
p.word32le();
|
||||||
|
b.parse(p, onlyHeader);
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
|
||||||
describe('Block', function() {
|
describe('Block', function() {
|
||||||
it('should initialze the main object', function() {
|
it('should initialze the main object', function() {
|
||||||
should.exist(BlockModule);
|
should.exist(BlockModule);
|
||||||
|
@ -16,8 +40,149 @@ describe('Block', function() {
|
||||||
should.exist(Block);
|
should.exist(Block);
|
||||||
});
|
});
|
||||||
it('should be able to create instance', function() {
|
it('should be able to create instance', function() {
|
||||||
var p = new Block();
|
var b = new Block();
|
||||||
should.exist(p);
|
should.exist(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse a block from hex', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
should.exist(b);
|
||||||
|
should.exist(b.getHash());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to check block contents', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
should.exist(b.getHash());
|
||||||
|
b.checkHash().should.equal(true);
|
||||||
|
b.checkProofOfWork().should.equal(true);
|
||||||
|
b.getWork().toString().should.equal('17180131332');
|
||||||
|
b.checkTimestamp().should.equal(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
it('#checkBlock should be able to check block contents', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
should.exist(b.getHash());
|
||||||
|
b.checkBlock().should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should be able to check Transactions', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
|
||||||
|
b.checkTransactions(b.txs).should.equal(true);
|
||||||
|
b.checkTransactions.bind([]).should.throw();
|
||||||
|
|
||||||
|
var coinbase = b.txs.shift;
|
||||||
|
b.checkTransactions.bind(b.txs).should.throw();
|
||||||
|
b.txs.push(coinbase);
|
||||||
|
b.checkTransactions.bind(b.txs).should.throw();
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to checkMerkleRoot', function() {
|
||||||
|
|
||||||
|
var b = getBlock();
|
||||||
|
b.getMerkleTree(b.txs).length.should.equal(45);
|
||||||
|
bitcore.buffertools.toHex(b.calcMerkleRoot(b.txs)).should.equal(bitcore.buffertools.toHex(new Buffer(b.merkle_root)));
|
||||||
|
|
||||||
|
b.checkMerkleRoot(b.txs);
|
||||||
|
|
||||||
|
delete b['merkle_root'];
|
||||||
|
b.checkMerkleRoot.bind(b.txs).should.throw();
|
||||||
|
|
||||||
|
|
||||||
|
b.merkle_root=new Buffer('wrong');
|
||||||
|
b.checkMerkleRoot.bind(b.txs).should.throw();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('should be able to checkProofOfWork', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
|
||||||
|
b.hash = bitcore.buffertools.reverse(new Buffer('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11', 'hex'));
|
||||||
|
b.checkHash().should.equal(true);
|
||||||
|
b.checkProofOfWork().should.equal(true);
|
||||||
|
|
||||||
|
// wrong hash hash, ok proof of work
|
||||||
|
b.hash = bitcore.buffertools.reverse(new Buffer('000000000000016390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11', 'hex'));
|
||||||
|
b.checkProofOfWork().should.equal(true);
|
||||||
|
b.checkHash().should.equal(false);
|
||||||
|
|
||||||
|
|
||||||
|
// wrong hash hash, wrong proof of work
|
||||||
|
b.hash = bitcore.buffertools.reverse(new Buffer('0000000bbb99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11', 'hex'));
|
||||||
|
b.checkHash().should.equal(false);
|
||||||
|
b.checkProofOfWork.bind().should.throw();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should be able to check via checkBlock', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
b.checkBlock.bind(b.txs).should.throw();
|
||||||
|
b.getHash();
|
||||||
|
b.checkBlock(b.txs).should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to get components from blocks', function() {
|
||||||
|
var b = getBlock(true);
|
||||||
|
|
||||||
|
bitcore.util.formatHashFull(b.getHash()).should.equal('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11');
|
||||||
|
|
||||||
|
bitcore.util.formatHashFull(b.getHeader()).should.equal('d6383bd51c3fffc051be10ce58e6d52d1eb00470ae1ab4d5a3375c0f51382c6f249fff84e9888286974cfc97000000003c35b5e70b13d5b938fef4e998a977c17bea978390273b7c50a9aa4b00000002');
|
||||||
|
|
||||||
|
bitcore.util.formatHashFull(b.merkle_root).should.equal('58e6d52d1eb00470ae1ab4d5a3375c0f51382c6f249fff84e9888286974cfc97');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#getBlockValue should return the correct block value', function() {
|
||||||
|
var c = bitcore.util.COIN;
|
||||||
|
bitcore.Block.getBlockValue(0).div(c).toNumber().should.equal(50);
|
||||||
|
bitcore.Block.getBlockValue(1).div(c).toNumber().should.equal(50);
|
||||||
|
bitcore.Block.getBlockValue(209999).div(c).toNumber().should.equal(50);
|
||||||
|
bitcore.Block.getBlockValue(210000).div(c).toNumber().should.equal(25);
|
||||||
|
bitcore.Block.getBlockValue(2100000).toNumber().should.equal(4882812);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#getStandardizedObject should return object', function() {
|
||||||
|
var b = getBlock();
|
||||||
|
var o = b.getStandardizedObject(b.txs);
|
||||||
|
|
||||||
|
o.hash.should.equal('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11');
|
||||||
|
o.n_tx.should.equal(22);
|
||||||
|
o.size.should.equal(8003);
|
||||||
|
var o2 = b.getStandardizedObject();
|
||||||
|
o2.hash.should.equal('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11');
|
||||||
|
o2.size.should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#miner should call the callback', function(done) {
|
||||||
|
var b = getBlock();
|
||||||
|
var Miner = function() {};
|
||||||
|
Miner.prototype.solve = function (header,target,cb) {
|
||||||
|
this.called=1;
|
||||||
|
should.exist(header);
|
||||||
|
should.exist(target);
|
||||||
|
return cb();
|
||||||
|
};
|
||||||
|
var miner = new Miner();
|
||||||
|
b.solve(miner, function () {
|
||||||
|
miner.called.should.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#createCoinbaseTx should create a tx', function() {
|
||||||
|
var b = new Block();
|
||||||
|
var pubkey = new Buffer('02d20b3fba521dcf88dfaf0eee8c15a8ba692d7eb0cb957d5bcf9f4cc052fb9cc6');
|
||||||
|
var tx = b.createCoinbaseTx(pubkey);
|
||||||
|
should.exist(tx);
|
||||||
|
tx.isCoinBase().should.equal(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -30,3 +30,6 @@ module.exports.dataSigNonCanonical = dataSigNonCanonical;
|
||||||
module.exports.dataBase58KeysValid = dataBase58KeysValid;
|
module.exports.dataBase58KeysValid = dataBase58KeysValid;
|
||||||
module.exports.dataBase58KeysInvalid = dataBase58KeysInvalid;
|
module.exports.dataBase58KeysInvalid = dataBase58KeysInvalid;
|
||||||
|
|
||||||
|
var buffer = new Buffer(fs.readFileSync('test/data/blk86756-testnet.dat'));
|
||||||
|
module.exports.dataRawBlock = buffer;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ function MissingSourceError(msg, missingTxHash) {
|
||||||
// TODO: Since this happens in normal operation, perhaps we should
|
// TODO: Since this happens in normal operation, perhaps we should
|
||||||
// avoid generating a whole stack trace.
|
// avoid generating a whole stack trace.
|
||||||
Error.call(this);
|
Error.call(this);
|
||||||
Error.captureStackTrace(this, arguments.callee);
|
// This is not compatible with firefox.
|
||||||
|
// Error.captureStackTrace(this, arguments.callee);
|
||||||
this.message = msg;
|
this.message = msg;
|
||||||
this.missingTxHash = missingTxHash;
|
this.missingTxHash = missingTxHash;
|
||||||
this.name = 'MissingSourceError';
|
this.name = 'MissingSourceError';
|
||||||
|
@ -19,6 +20,7 @@ MissingSourceError.prototype.__proto__ = Error.prototype;
|
||||||
|
|
||||||
exports.MissingSourceError = MissingSourceError;
|
exports.MissingSourceError = MissingSourceError;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in several places to indicate invalid data.
|
* Used in several places to indicate invalid data.
|
||||||
*
|
*
|
||||||
|
@ -29,7 +31,9 @@ function VerificationError(msg, missingTxHash) {
|
||||||
// TODO: Since this happens in normal operation, perhaps we should
|
// TODO: Since this happens in normal operation, perhaps we should
|
||||||
// avoid generating a whole stack trace.
|
// avoid generating a whole stack trace.
|
||||||
Error.call(this);
|
Error.call(this);
|
||||||
Error.captureStackTrace(this, arguments.callee);
|
|
||||||
|
// This is not compatible with firefox.
|
||||||
|
// Error.captureStackTrace(this, arguments.callee);
|
||||||
this.message = msg;
|
this.message = msg;
|
||||||
this.missingTxHash = missingTxHash;
|
this.missingTxHash = missingTxHash;
|
||||||
this.name = 'VerificationError';
|
this.name = 'VerificationError';
|
||||||
|
|
11
util/util.js
11
util/util.js
|
@ -359,8 +359,17 @@ var generateNonce = exports.generateNonce = function() {
|
||||||
*/
|
*/
|
||||||
var decodeDiffBits = exports.decodeDiffBits = function(diffBits, asBigInt) {
|
var decodeDiffBits = exports.decodeDiffBits = function(diffBits, asBigInt) {
|
||||||
diffBits = +diffBits;
|
diffBits = +diffBits;
|
||||||
|
|
||||||
var target = bignum(diffBits & 0xffffff);
|
var target = bignum(diffBits & 0xffffff);
|
||||||
target = target.shiftLeft(8 * ((diffBits >>> 24) - 3));
|
/*
|
||||||
|
* shiftLeft is not implemented on the bignum browser
|
||||||
|
*
|
||||||
|
* target = target.shiftLeft(8*((diffBits >>> 24) - 3));
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mov = 8*((diffBits >>> 24) - 3);
|
||||||
|
while (mov-- > 0)
|
||||||
|
target = target.mul(2);
|
||||||
|
|
||||||
if (asBigInt) {
|
if (asBigInt) {
|
||||||
return target;
|
return target;
|
||||||
|
|
Loading…
Reference in New Issue