fcoin/lib/bcoin/chainblock.js
Christopher Jeffrey bba77b4150 more mempool work.
2016-03-31 22:00:00 -07:00

315 lines
7.7 KiB
JavaScript

/**
* chainblock.js - chainblock object for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* https://github.com/indutny/bcoin
*/
var bcoin = require('../bcoin');
var bn = require('bn.js');
var constants = bcoin.protocol.constants;
var network = bcoin.protocol.network;
var utils = require('./utils');
var assert = utils.assert;
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
/**
* ChainBlock
*/
function ChainBlock(chain, data, prev) {
if (!(this instanceof ChainBlock))
return new ChainBlock(chain, data);
this.chain = chain;
this.hash = data.hash;
this.version = data.version;
this.prevBlock = data.prevBlock;
this.merkleRoot = data.merkleRoot;
this.ts = data.ts;
this.bits = data.bits;
this.nonce = data.nonce;
this.height = data.height;
this.chainwork = data.chainwork || this.getChainwork(prev);
this.ancestors = [];
}
ChainBlock.prototype.getProof = function getProof() {
var target = utils.fromCompact(this.bits);
if (target.isNeg() || target.cmpn(0) === 0)
return new bn(0);
return new bn(1).ushln(256).div(target.addn(1));
};
ChainBlock.prototype.getChainwork = function getChainwork(prev) {
return (prev ? prev.chainwork : new bn(0)).add(this.getProof());
};
ChainBlock.prototype.isGenesis = function isGenesis() {
return this.hash === network.genesis.hash;
};
ChainBlock.prototype.ensureAncestors = function ensureAncestors(callback) {
var majorityWindow = network.block.majorityWindow;
var medianTimespan = constants.block.medianTimespan;
var powDiffInterval = network.powDiffInterval;
var allowMinDiff = network.powAllowMinDifficultyBlocks;
var max = Math.max(majorityWindow, medianTimespan);
if ((this.height + 1) % powDiffInterval === 0 || allowMinDiff)
max = Math.max(max, powDiffInterval);
assert(this.ancestors.length === 0);
return this.alloc(max, callback);
};
ChainBlock.prototype.alloc = function alloc(max, callback) {
var self = this;
var i;
return this.getAncestors(max, function(err, ancestors) {
if (err)
return callback(err);
assert(ancestors);
self.ancestors.length = 0;
for (i = 0; i < ancestors.length; i++)
self.ancestors.push(ancestors[i]);
return callback();
});
};
ChainBlock.prototype.getAncestors = function getAncestors(max, callback) {
var entry = this;
var ancestors = this.ancestors.slice();
if (max === 0)
return callback(null, []);
if (ancestors.length)
entry = ancestors.pop();
assert(utils.isFinite(max));
// Try to do this iteratively and synchronously
// so we don't have to wait on nextTicks.
for (;;) {
ancestors.push(entry);
if (ancestors.length >= max)
return callback(null, ancestors);
if (!this.chain.db.hasCache(entry.prevBlock)) {
ancestors.pop();
break;
}
entry = this.chain.db.getCache(entry.prevBlock);
}
(function next(err, entry) {
if (err)
return callback(err);
if (!entry)
return callback(null, ancestors);
ancestors.push(entry);
if (ancestors.length >= max)
return callback(null, ancestors);
entry.getPrevious(next);
})(null, entry);
};
ChainBlock.prototype.free = function free() {
this.ancestors.length = 0;
};
ChainBlock.prototype.isMainChain = function isMainChain(callback) {
return this.chain.db.isMainChain(this, callback);
};
ChainBlock.prototype.getAncestorByHeight = function getAncestorByHeight(height, callback) {
if (height < 0)
return utils.nextTick(callback);
assert(height >= 0);
assert(height <= this.height);
return this.getAncestor(this.height - height, function(err, entry) {
if (err)
return callback(err);
if (!entry)
return callback();
assert(entry.height === height);
return callback(null, entry);
});
};
ChainBlock.prototype.getAncestor = function getAncestor(index, callback) {
assert(index >= 0);
return this.getAncestors(index + 1, function(err, ancestors) {
if (err)
return callback(err);
if (ancestors.length < index + 1)
return callback();
return callback(null, ancestors[index]);
});
};
ChainBlock.prototype.getPrevious = function getPrevious(callback) {
return this.chain.db.get(this.prevBlock, callback);
};
ChainBlock.prototype.getNext = function getNext(callback) {
var self = this;
return this.chain.db.getNextHash(this.hash, function(err, hash) {
if (err)
return callback(err);
if (!hash)
return callback();
return self.chain.db.get(hash, callback);
});
};
ChainBlock.prototype.getMedianTime = function getMedianTime(ancestors) {
var entry = this;
var median = [];
var timeSpan = constants.block.medianTimespan;
var i;
if (!ancestors)
ancestors = this.ancestors;
for (i = 0; i < timeSpan && entry; i++, entry = ancestors[i])
median.push(entry.ts);
median = median.sort();
return median[median.length / 2 | 0];
};
ChainBlock.prototype.isOutdated = function isOutdated(version, ancestors) {
return this.isSuperMajority(version, network.block.majorityRejectOutdated, ancestors);
};
ChainBlock.prototype.isUpgraded = function isUpgraded(version, ancestors) {
return this.isSuperMajority(version, network.block.majorityEnforceUpgrade, ancestors);
};
ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, required, ancestors) {
var entry = this;
var found = 0;
var majorityWindow = network.block.majorityWindow;
var i;
if (!ancestors)
ancestors = this.ancestors;
for (i = 0; i < majorityWindow && found < required && entry; i++) {
if (entry.version >= version)
found++;
entry = ancestors[i + 1];
}
return found >= required;
};
ChainBlock.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback) {
var self = this;
return this.getAncestors(constants.block.medianTimespan, function(err, ancestors) {
if (err)
return callback(err);
return callback(null, self.getMedianTime(ancestors));
});
};
ChainBlock.prototype.__defineGetter__('rhash', function() {
return utils.revHex(this.hash);
});
ChainBlock.prototype.toRaw = function toRaw() {
var p = new BufferWriter();
p.write32(this.version);
p.writeHash(this.prevBlock);
p.writeHash(this.merkleRoot);
p.writeU32(this.ts);
p.writeU32(this.bits);
p.writeU32(this.nonce);
p.writeU32(this.height);
p.writeBytes(this.chainwork.toBuffer('le', 32));
return p.render();
};
ChainBlock.fromRaw = function fromRaw(chain, buf) {
var p = new BufferReader(buf);
var hash = utils.dsha256(buf.slice(0, 80));
return new ChainBlock(chain, {
hash: utils.toHex(hash),
version: p.readU32(), // Technically signed
prevBlock: p.readHash('hex'),
merkleRoot: p.readHash('hex'),
ts: p.readU32(),
bits: p.readU32(),
nonce: p.readU32(),
height: p.readU32(),
chainwork: new bn(p.readBytes(32), 'le')
});
};
ChainBlock.prototype.toJSON = function toJSON() {
return {
version: this.version,
hash: utils.revHex(this.hash),
prevBlock: utils.revHex(this.prevBlock),
merkleRoot: utils.revHex(this.merkleRoot),
ts: this.ts,
bits: this.bits,
nonce: this.nonce,
height: this.height,
chainwork: this.chainwork.toString('hex')
};
};
ChainBlock.fromJSON = function fromJSON(chain, json) {
json.hash = utils.revHex(json.hash);
json.prevBlock = utils.revHex(json.prevBlock);
json.merkleRoot = utils.revHex(json.merkleRoot);
json.chainwork = new bn(json.chainwork, 'hex');
return new ChainBlock(chain, json);
};
ChainBlock.prototype.inspect = function inspect() {
var json = this.toJSON();
json.ancestors = this.ancestors.length;
return json;
};
ChainBlock.isChainBlock = function isChainBlock(obj) {
return obj
&& bn.isBN(obj.chainwork)
&& typeof obj.getMedianTime === 'function';
};
/**
* Expose
*/
module.exports = ChainBlock;