chain: alias all db methods. remove chainentry spaghetti code.

This commit is contained in:
Christopher Jeffrey 2017-09-06 06:02:40 -07:00
parent b81643473e
commit 5f82c0d8c2
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 641 additions and 480 deletions

View File

@ -72,25 +72,32 @@ function Chain(options) {
this.logger = this.options.logger.context('chain');
this.workers = this.options.workers;
this.db = new ChainDB(this.options);
this.locker = new Lock(true);
this.invalid = new LRU(100);
this.state = new DeploymentState();
this.tip = new ChainEntry(this);
this.tip = new ChainEntry();
this.height = -1;
this.synced = false;
this.orphanMap = new Map();
this.orphanPrev = new Map();
this.db = new ChainDB(this);
}
Object.setPrototypeOf(Chain.prototype, AsyncObject.prototype);
/**
* Size of set to pick median time from.
* @const {Number}
* @default
*/
Chain.MEDIAN_TIMESPAN = 11;
/**
* Open the chain, wait for the database to load.
* @method
* @alias Chain#open
* @returns {Promise}
*/
@ -146,7 +153,6 @@ Chain.prototype._close = function _close() {
/**
* Perform all necessary contextual verification on a block.
* @method
* @private
* @param {Block} block
* @param {ChainEntry} prev
@ -170,7 +176,6 @@ Chain.prototype.verifyContext = async function verifyContext(block, prev, flags)
/**
* Perform all necessary contextual verification
* on a block, without POW check.
* @method
* @param {Block} block
* @returns {Promise}
*/
@ -187,7 +192,6 @@ Chain.prototype.verifyBlock = async function verifyBlock(block) {
/**
* Perform all necessary contextual verification
* on a block, without POW check (no lock).
* @method
* @private
* @param {Block} block
* @returns {Promise}
@ -208,11 +212,134 @@ Chain.prototype.isGenesis = function isGenesis(block) {
return block.hash('hex') === this.network.genesis.hash;
};
/**
* Test whether the hash is in the main chain.
* @param {Hash} hash
* @returns {Promise} - Returns Boolean.
*/
Chain.prototype.isMainHash = function isMainHash(hash) {
return this.db.isMainHash(hash);
};
/**
* Test whether the entry is in the main chain.
* @param {ChainEntry} entry
* @returns {Promise} - Returns Boolean.
*/
Chain.prototype.isMainChain = function isMainChain(entry) {
return this.db.isMainChain(entry);
};
/**
* Get ancestor by `height`.
* @param {ChainEntry} entry
* @param {Number} height
* @returns {Promise} - Returns ChainEntry.
*/
Chain.prototype.getAncestor = function getAncestor(entry, height) {
return this.db.getAncestor(entry, height);
};
/**
* Get previous entry.
* @param {ChainEntry} entry
* @returns {Promise} - Returns ChainEntry.
*/
Chain.prototype.getPrevious = function getPrevious(entry) {
return this.db.getPrevious(entry);
};
/**
* Get previous cached entry.
* @param {ChainEntry} entry
* @returns {ChainEntry|null}
*/
Chain.prototype.getPrevCache = function getPrevCache(entry) {
return this.db.getPrevCache(entry);
};
/**
* Get next entry.
* @param {ChainEntry} entry
* @returns {Promise} - Returns ChainEntry.
*/
Chain.prototype.getNext = function getNext(entry) {
return this.db.getNext(entry);
};
/**
* Get next entry.
* @param {ChainEntry} entry
* @returns {Promise} - Returns ChainEntry.
*/
Chain.prototype.getNextEntry = function getNextEntry(entry) {
return this.db.getNextEntry(entry);
};
/**
* Calculate median time past.
* @param {ChainEntry} prev
* @param {Number?} time
* @returns {Promise} - Returns Number.
*/
Chain.prototype.getMedianTime = async function getMedianTime(prev, time) {
let timespan = Chain.MEDIAN_TIMESPAN;
const median = [];
// In case we ever want to check
// the MTP of the _current_ block
// (necessary for BIP148).
if (time != null) {
median.push(time);
timespan -= 1;
}
let entry = prev;
for (let i = 0; i < timespan && entry; i++) {
median.push(entry.time);
const cache = this.getPrevCache(entry);
if (cache)
entry = cache;
else
entry = await this.getPrevious(entry);
}
median.sort(cmp);
return median[median.length >>> 1];
};
/**
* Test whether the entry is potentially
* an ancestor of a checkpoint.
* @param {ChainEntry} prev
* @returns {Boolean}
*/
Chain.prototype.isHistorical = function isHistorical(prev) {
if (this.options.checkpoints) {
if (prev.height + 1 <= this.network.lastCheckpoint)
return true;
}
return false;
};
/**
* Contextual verification for a block, including
* version deployments (IsSuperMajority), versionbits,
* coinbase height, finality checks.
* @method
* @private
* @param {Block} block
* @param {ChainEntry} prev
@ -244,7 +371,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
// We can do this safely because every
// block in between each checkpoint was
// validated outside in the header chain.
if (prev.isHistorical()) {
if (this.isHistorical(prev)) {
if (this.options.spv)
return new DeploymentState();
@ -279,7 +406,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
}
// Ensure the timestamp is correct.
const mtp = await prev.getMedianTime();
const mtp = await this.getMedianTime(prev);
if (block.time <= mtp) {
throw new VerifyError(block,
@ -403,7 +530,6 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
/**
* Check all deployments on a chain, ranging from p2sh to segwit.
* @method
* @param {Number} time
* @param {ChainEntry} prev
* @returns {Promise} - Returns {@link DeploymentState}.
@ -474,7 +600,7 @@ Chain.prototype.getDeployments = async function getDeployments(time, prev) {
// assumption that deployment checks should
// only ever examine the values of the
// previous block (necessary for mining).
const mtp = await prev.getMedianTime(time);
const mtp = await this.getMedianTime(prev, time);
if (mtp >= 1501545600 && mtp <= 1510704000)
state.bip148 = true;
}
@ -525,7 +651,6 @@ Chain.prototype.setDeploymentState = function setDeploymentState(state) {
* Determine whether to check block for duplicate txids in blockchain
* history (BIP30). If we're on a chain that has bip34 activated, we
* can skip this.
* @method
* @private
* @see https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
* @param {Block} block
@ -537,7 +662,7 @@ Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev,
if (this.options.spv)
return;
if (prev.isHistorical())
if (this.isHistorical(prev))
return;
// BIP34 made it impossible to
@ -574,7 +699,6 @@ Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev,
* will attempt to do this on the worker pool). If
* `checkpoints` is enabled, it will skip verification
* for historical data.
* @method
* @private
* @see TX#verifyInputs
* @see TX#verify
@ -592,7 +716,7 @@ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) {
const interval = this.network.halvingInterval;
const height = prev.height + 1;
const historical = prev.isHistorical();
const historical = this.isHistorical(prev);
let sigops = 0;
let reward = 0;
@ -698,26 +822,9 @@ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) {
return view;
};
/**
* Get the cached height for a hash if present.
* @private
* @param {Hash} hash
* @returns {Number}
*/
Chain.prototype.checkHeight = function checkHeight(hash) {
const entry = this.db.getCache(hash);
if (!entry)
return -1;
return entry.height;
};
/**
* Find the block at which a fork ocurred.
* @private
* @method
* @param {ChainEntry} fork - The current chain.
* @param {ChainEntry} longer - The competing chain.
* @returns {Promise}
@ -726,7 +833,7 @@ Chain.prototype.checkHeight = function checkHeight(hash) {
Chain.prototype.findFork = async function findFork(fork, longer) {
while (fork.hash !== longer.hash) {
while (longer.height > fork.height) {
longer = await longer.getPrevious();
longer = await this.getPrevious(longer);
if (!longer)
throw new Error('No previous entry for new tip.');
}
@ -734,7 +841,7 @@ Chain.prototype.findFork = async function findFork(fork, longer) {
if (fork.hash === longer.hash)
return fork;
fork = await fork.getPrevious();
fork = await this.getPrevious(fork);
if (!fork)
throw new Error('No previous entry for old tip.');
@ -747,7 +854,6 @@ Chain.prototype.findFork = async function findFork(fork, longer) {
* Reorganize the blockchain (connect and disconnect inputs).
* Called when a competing chain with a higher chainwork
* is received.
* @method
* @private
* @param {ChainEntry} competitor - The competing chain's tip.
* @returns {Promise}
@ -764,7 +870,7 @@ Chain.prototype.reorganize = async function reorganize(competitor) {
let entry = tip;
while (entry.hash !== fork.hash) {
disconnect.push(entry);
entry = await entry.getPrevious();
entry = await this.getPrevious(entry);
assert(entry);
}
@ -773,7 +879,7 @@ Chain.prototype.reorganize = async function reorganize(competitor) {
entry = competitor;
while (entry.hash !== fork.hash) {
connect.push(entry);
entry = await entry.getPrevious();
entry = await this.getPrevious(entry);
assert(entry);
}
@ -805,7 +911,6 @@ Chain.prototype.reorganize = async function reorganize(competitor) {
/**
* Reorganize the blockchain for SPV. This
* will reset the chain to the fork block.
* @method
* @private
* @param {ChainEntry} competitor - The competing chain's tip.
* @returns {Promise}
@ -822,7 +927,7 @@ Chain.prototype.reorganizeSPV = async function reorganizeSPV(competitor) {
let entry = tip;
while (entry.hash !== fork.hash) {
disconnect.push(entry);
entry = await entry.getPrevious();
entry = await this.getPrevious(entry);
assert(entry);
}
@ -857,7 +962,6 @@ Chain.prototype.reorganizeSPV = async function reorganizeSPV(competitor) {
/**
* Disconnect an entry from the chain (updates the tip).
* @method
* @param {ChainEntry} entry
* @returns {Promise}
*/
@ -871,7 +975,7 @@ Chain.prototype.disconnect = async function disconnect(entry) {
block = entry.toHeaders();
}
const prev = await entry.getPrevious();
const prev = await this.getPrevious(entry);
const view = await this.db.disconnect(entry, block);
assert(prev);
@ -889,7 +993,6 @@ Chain.prototype.disconnect = async function disconnect(entry) {
* This will do contextual-verification on the block
* (necessary because we cannot validate the inputs
* in alternate chains when they come in).
* @method
* @param {ChainEntry} entry
* @param {Number} flags
* @returns {Promise}
@ -906,7 +1009,7 @@ Chain.prototype.reconnect = async function reconnect(entry) {
block = entry.toHeaders();
}
const prev = await entry.getPrevious();
const prev = await this.getPrevious(entry);
assert(prev);
let view, state;
@ -940,7 +1043,6 @@ Chain.prototype.reconnect = async function reconnect(entry) {
* that comes in. It may add and connect the block (main chain),
* save the block without connection (alternate chain), or
* reorganize the chain (a higher fork).
* @method
* @private
* @param {ChainEntry} entry
* @param {Block} block
@ -966,7 +1068,7 @@ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, f
}
// Warn of unknown versionbits.
if (entry.hasUnknown()) {
if (entry.hasUnknown(this.network)) {
this.logger.warning(
'Unknown version bits in block %d: %s.',
entry.height, util.hex32(entry.version));
@ -1006,7 +1108,6 @@ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, f
/**
* Save block on an alternate chain.
* @method
* @private
* @param {ChainEntry} entry
* @param {Block} block
@ -1032,7 +1133,7 @@ Chain.prototype.saveAlternate = async function saveAlternate(entry, block, prev,
}
// Warn of unknown versionbits.
if (entry.hasUnknown()) {
if (entry.hasUnknown(this.network)) {
this.logger.warning(
'Unknown version bits in block %d: %s.',
entry.height, util.hex32(entry.version));
@ -1062,7 +1163,6 @@ Chain.prototype.saveAlternate = async function saveAlternate(entry, block, prev,
* Reset the chain to the desired block. This
* is useful for replaying the blockchain download
* for SPV.
* @method
* @param {Hash|Number} block
* @returns {Promise}
*/
@ -1078,7 +1178,6 @@ Chain.prototype.reset = async function reset(block) {
/**
* Reset the chain to the desired block without a lock.
* @method
* @private
* @param {Hash|Number} block
* @returns {Promise}
@ -1112,7 +1211,6 @@ Chain.prototype._reset = async function _reset(block, silent) {
/**
* Reset the chain to a height or hash. Useful for replaying
* the blockchain download for SPV.
* @method
* @param {Hash|Number} block - hash/height
* @returns {Promise}
*/
@ -1128,7 +1226,6 @@ Chain.prototype.replay = async function replay(block) {
/**
* Reset the chain without a lock.
* @method
* @private
* @param {Hash|Number} block - hash/height
* @param {Boolean?} silent
@ -1136,12 +1233,12 @@ Chain.prototype.replay = async function replay(block) {
*/
Chain.prototype._replay = async function _replay(block, silent) {
const entry = await this.db.getEntry(block);
const entry = await this.getEntry(block);
if (!entry)
throw new Error('Block not found.');
if (!await entry.isMainChain())
if (!await this.isMainChain(entry))
throw new Error('Cannot reset on alternate chain.');
if (entry.isGenesis()) {
@ -1154,7 +1251,6 @@ Chain.prototype._replay = async function _replay(block, silent) {
/**
* Invalidate block.
* @method
* @param {Hash} hash
* @returns {Promise}
*/
@ -1170,7 +1266,6 @@ Chain.prototype.invalidate = async function invalidate(hash) {
/**
* Invalidate block (no lock).
* @method
* @param {Hash} hash
* @returns {Promise}
*/
@ -1182,14 +1277,13 @@ Chain.prototype._invalidate = async function _invalidate(hash) {
/**
* Retroactively prune the database.
* @method
* @returns {Promise}
*/
Chain.prototype.prune = async function prune() {
const unlock = await this.locker.lock();
try {
return await this.db.prune(this.tip.hash);
return await this.db.prune();
} finally {
unlock();
}
@ -1197,7 +1291,6 @@ Chain.prototype.prune = async function prune() {
/**
* Scan the blockchain for transactions containing specified address hashes.
* @method
* @param {Hash} start - Block hash to start at.
* @param {Bloom} filter - Bloom filter containing tx and address hashes.
* @param {Function} iter - Iterator.
@ -1215,7 +1308,6 @@ Chain.prototype.scan = async function scan(start, filter, iter) {
/**
* Add a block to the chain, perform all necessary verification.
* @method
* @param {Block} block
* @param {Number?} flags
* @param {Number?} id
@ -1234,7 +1326,6 @@ Chain.prototype.add = async function add(block, flags, id) {
/**
* Add a block to the chain without a lock.
* @method
* @private
* @param {Block} block
* @param {Number?} flags
@ -1283,13 +1374,13 @@ Chain.prototype._add = async function _add(block, flags, id) {
}
// Do we already have this block?
if (await this.db.hasEntry(hash)) {
if (await this.hasEntry(hash)) {
this.logger.debug('Already have block: %s.', block.rhash());
throw new VerifyError(block, 'duplicate', 'duplicate', 0);
}
// Find the previous block entry.
const prev = await this.db.getEntry(block.prevBlock);
const prev = await this.getEntry(block.prevBlock);
// If previous block wasn't ever seen,
// add it current to orphans and return.
@ -1310,7 +1401,6 @@ Chain.prototype._add = async function _add(block, flags, id) {
/**
* Connect block to chain.
* @method
* @private
* @param {ChainEntry} prev
* @param {Block} block
@ -1348,7 +1438,7 @@ Chain.prototype.connect = async function connect(prev, block, flags) {
}
// Create a new chain entry.
const entry = ChainEntry.fromBlock(this, block, prev);
const entry = ChainEntry.fromBlock(block, prev);
// The block is on a alternate chain if the
// chainwork is less than or equal to
@ -1373,7 +1463,6 @@ Chain.prototype.connect = async function connect(prev, block, flags) {
/**
* Handle orphans.
* @method
* @private
* @param {ChainEntry} entry
* @returns {Promise}
@ -1700,7 +1789,6 @@ Chain.prototype.removeInvalid = function removeInvalid(hash) {
/**
* Test the chain to see if it contains
* a block, or has recently seen a block.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns Boolean.
*/
@ -1728,6 +1816,47 @@ Chain.prototype.getEntry = function getEntry(hash) {
return this.db.getEntry(hash);
};
/**
* Retrieve a chain entry by height.
* @param {Number} height
* @returns {Promise} - Returns {@link ChainEntry}.
*/
Chain.prototype.getEntryByHeight = function getEntryByHeight(height) {
return this.db.getEntryByHeight(height);
};
/**
* Retrieve a chain entry by hash.
* @param {Hash} hash
* @returns {Promise} - Returns {@link ChainEntry}.
*/
Chain.prototype.getEntryByHash = function getEntryByHash(hash) {
return this.db.getEntryByHash(hash);
};
/**
* Get the hash of a block by height. Note that this
* will only return hashes in the main chain.
* @param {Number} height
* @returns {Promise} - Returns {@link Hash}.
*/
Chain.prototype.getHash = function getHash(height) {
return this.db.getHash(height);
};
/**
* Get the height of a block by hash.
* @param {Hash} hash
* @returns {Promise} - Returns Number.
*/
Chain.prototype.getHeight = function getHeight(hash) {
return this.db.getHeight(hash);
};
/**
* Test the chain to see if it contains a block.
* @param {Hash} hash
@ -1738,6 +1867,157 @@ Chain.prototype.hasEntry = function hasEntry(hash) {
return this.db.hasEntry(hash);
};
/**
* Get the _next_ block hash (does not work by height).
* @param {Hash} hash
* @returns {Promise} - Returns {@link Hash}.
*/
Chain.prototype.getNextHash = function getNextHash(hash) {
return this.db.getNextHash(hash);
};
/**
* Check whether coins are still unspent. Necessary for bip30.
* @see https://bitcointalk.org/index.php?topic=67738.0
* @param {TX} tx
* @returns {Promise} - Returns Boolean.
*/
Chain.prototype.hasCoins = function hasCoins(tx) {
return this.db.hasCoins(tx);
};
/**
* Get all tip hashes.
* @returns {Promise} - Returns {@link Hash}[].
*/
Chain.prototype.getTips = function getTips() {
return this.db.getTips();
};
/**
* Get a coin (unspents only).
* @private
* @param {Outpoint} prevout
* @returns {Promise} - Returns {@link CoinEntry}.
*/
Chain.prototype.readCoin = function readCoin(prevout) {
return this.db.readCoin(prevout);
};
/**
* Get a coin (unspents only).
* @param {Hash} hash
* @param {Number} index
* @returns {Promise} - Returns {@link Coin}.
*/
Chain.prototype.getCoin = function getCoin(hash, index) {
return this.db.getCoin(hash, index);
};
/**
* Retrieve a block from the database (not filled with coins).
* @param {Hash} hash
* @returns {Promise} - Returns {@link Block}.
*/
Chain.prototype.getBlock = function getBlock(hash) {
return this.db.getBlock(hash);
};
/**
* Retrieve a block from the database (not filled with coins).
* @param {Hash} hash
* @returns {Promise} - Returns {@link Block}.
*/
Chain.prototype.getRawBlock = function getRawBlock(block) {
return this.db.getRawBlock(block);
};
/**
* Get a historical block coin viewpoint.
* @param {Block} hash
* @returns {Promise} - Returns {@link CoinView}.
*/
Chain.prototype.getBlockView = function getBlockView(block) {
return this.db.getBlockView(block);
};
/**
* Get a transaction with metadata.
* @param {Hash} hash
* @returns {Promise} - Returns {@link TXMeta}.
*/
Chain.prototype.getMeta = function getMeta(hash) {
return this.db.getMeta(hash);
};
/**
* Retrieve a transaction.
* @param {Hash} hash
* @returns {Promise} - Returns {@link TX}.
*/
Chain.prototype.getTX = function getTX(hash) {
return this.db.getTX(hash);
};
/**
* @param {Hash} hash
* @returns {Promise} - Returns Boolean.
*/
Chain.prototype.hasTX = function hasTX(hash) {
return this.db.hasTX(hash);
};
/**
* Get all coins pertinent to an address.
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link Coin}[].
*/
Chain.prototype.getCoinsByAddress = function getCoinsByAddress(addrs) {
return this.db.getCoinsByAddress(addrs);
};
/**
* Get all transaction hashes to an address.
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link Hash}[].
*/
Chain.prototype.getHashesByAddress = function getHashesByAddress(addrs) {
return this.db.getHashesByAddress(addrs);
};
/**
* Get all transactions pertinent to an address.
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link TX}[].
*/
Chain.prototype.getTXByAddress = function getTXByAddress(addrs) {
return this.db.getTXByAddress(addrs);
};
/**
* Get all transactions pertinent to an address.
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link TXMeta}[].
*/
Chain.prototype.getMetaByAddress = function getMetaByAddress(addrs) {
return this.db.getMetaByAddress(addrs);
};
/**
* Get an orphan block.
* @param {Hash} hash
@ -1768,9 +2048,18 @@ Chain.prototype.hasPending = function hasPending(hash) {
return this.locker.hasPending(hash);
};
/**
* Get coin viewpoint.
* @param {TX} tx
* @returns {Promise} - Returns {@link CoinView}.
*/
Chain.prototype.getCoinView = function getCoinView(tx) {
return this.db.getCoinView(tx);
};
/**
* Get coin viewpoint (spent).
* @method
* @param {TX} tx
* @returns {Promise} - Returns {@link CoinView}.
*/
@ -1842,7 +2131,6 @@ Chain.prototype.getProgress = function getProgress() {
/**
* Calculate chain locator (an array of hashes).
* @method
* @param {Hash?} start - Height or hash to treat as the tip.
* The current tip will be used if not present. Note that this can be a
* non-existent hash, which is useful for headers-first locators.
@ -1860,7 +2148,6 @@ Chain.prototype.getLocator = async function getLocator(start) {
/**
* Calculate chain locator without a lock.
* @method
* @private
* @param {Hash?} start
* @returns {Promise}
@ -1872,7 +2159,8 @@ Chain.prototype._getLocator = async function _getLocator(start) {
assert(typeof start === 'string');
let entry = await this.db.getEntry(start);
let entry = await this.getEntry(start);
const hashes = [];
if (!entry) {
@ -1882,9 +2170,10 @@ Chain.prototype._getLocator = async function _getLocator(start) {
let hash = entry.hash;
let height = entry.height;
const main = await entry.isMainChain();
let step = 1;
const main = await this.isMainChain(entry);
hashes.push(hash);
while (height > 0) {
@ -1899,10 +2188,10 @@ Chain.prototype._getLocator = async function _getLocator(start) {
if (main) {
// If we're on the main chain, we can
// do a fast lookup of the hash.
hash = await this.db.getHash(height);
hash = await this.getHash(height);
assert(hash);
} else {
const ancestor = await entry.getAncestor(height);
const ancestor = await this.getAncestor(entry, height);
assert(ancestor);
hash = ancestor.hash;
}
@ -1968,7 +2257,6 @@ Chain.prototype.getProofTime = function getProofTime(to, from) {
/**
* Calculate the next target based on the chain tip.
* @method
* @returns {Promise} - returns Number
* (target is in compact/mantissa form).
*/
@ -1979,7 +2267,6 @@ Chain.prototype.getCurrentTarget = async function getCurrentTarget() {
/**
* Calculate the next target.
* @method
* @param {Number} time - Next block timestamp.
* @param {ChainEntry} prev - Previous entry.
* @returns {Promise} - returns Number
@ -2005,14 +2292,14 @@ Chain.prototype.getTarget = async function getTarget(time, prev) {
while (prev.height !== 0
&& prev.height % pow.retargetInterval !== 0
&& prev.bits === pow.bits) {
const cache = prev.getPrevCache();
const cache = this.getPrevCache(prev);
if (cache) {
prev = cache;
continue;
}
prev = await prev.getPrevious();
prev = await this.getPrevious(prev);
assert(prev);
}
}
@ -2023,7 +2310,7 @@ Chain.prototype.getTarget = async function getTarget(time, prev) {
const height = prev.height - (pow.retargetInterval - 1);
assert(height >= 0);
const first = await prev.getAncestor(height);
const first = await this.getAncestor(prev, height);
assert(first);
return this.retarget(prev, first);
@ -2065,7 +2352,6 @@ Chain.prototype.retarget = function retarget(prev, first) {
/**
* Find a locator. Analagous to bitcoind's `FindForkInGlobalIndex()`.
* @method
* @param {Hash[]} locator - Hashes.
* @returns {Promise} - Returns {@link Hash} (the
* hash of the latest known block).
@ -2073,7 +2359,7 @@ Chain.prototype.retarget = function retarget(prev, first) {
Chain.prototype.findLocator = async function findLocator(locator) {
for (const hash of locator) {
if (await this.db.isMainChain(hash))
if (await this.isMainHash(hash))
return hash;
}
@ -2084,7 +2370,6 @@ Chain.prototype.findLocator = async function findLocator(locator) {
* Check whether a versionbits deployment is active (BIP9: versionbits).
* @example
* await chain.isActive(tip, deployments.segwit);
* @method
* @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
* @param {ChainEntry} prev - Previous chain entry.
* @param {String} id - Deployment id.
@ -2098,7 +2383,6 @@ Chain.prototype.isActive = async function isActive(prev, deployment) {
/**
* Get chain entry state for a deployment (BIP9: versionbits).
* @method
* @example
* await chain.getState(tip, deployments.segwit);
* @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
@ -2120,7 +2404,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
if (((prev.height + 1) % window) !== 0) {
const height = prev.height - ((prev.height + 1) % window);
prev = await prev.getAncestor(height);
prev = await this.getAncestor(prev, height);
if (!prev)
return thresholdStates.DEFINED;
@ -2141,7 +2425,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
break;
}
const time = await entry.getMedianTime();
const time = await this.getMedianTime(entry);
if (time < deployment.startTime) {
state = thresholdStates.DEFINED;
@ -2152,7 +2436,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
compute.push(entry);
const height = entry.height - window;
entry = await entry.getAncestor(height);
entry = await this.getAncestor(entry, height);
}
while (compute.length) {
@ -2160,7 +2444,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
switch (state) {
case thresholdStates.DEFINED: {
const time = await entry.getMedianTime();
const time = await this.getMedianTime(entry);
if (time >= deployment.timeout) {
state = thresholdStates.FAILED;
@ -2175,7 +2459,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
break;
}
case thresholdStates.STARTED: {
const time = await entry.getMedianTime();
const time = await this.getMedianTime(entry);
let block = entry;
let count = 0;
@ -2193,7 +2477,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
break;
}
block = await block.getPrevious();
block = await this.getPrevious(block);
assert(block);
}
@ -2221,7 +2505,6 @@ Chain.prototype.getState = async function getState(prev, deployment) {
/**
* Compute the version for a new block (BIP9: versionbits).
* @method
* @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
* @param {ChainEntry} prev - Previous chain entry (usually the tip).
* @returns {Promise} - Returns Number.
@ -2247,13 +2530,12 @@ Chain.prototype.computeBlockVersion = async function computeBlockVersion(prev) {
/**
* Get the current deployment state of the chain. Called on load.
* @method
* @private
* @returns {Promise} - Returns {@link DeploymentState}.
*/
Chain.prototype.getDeploymentState = async function getDeploymentState() {
const prev = await this.tip.getPrevious();
const prev = await this.getPrevious(this.tip);
if (!prev) {
assert(this.tip.isGenesis());
@ -2269,7 +2551,6 @@ Chain.prototype.getDeploymentState = async function getDeploymentState() {
/**
* Check transaction finality, taking into account MEDIAN_TIME_PAST
* if it is present in the lock flags.
* @method
* @param {ChainEntry} prev - Previous chain entry.
* @param {TX} tx
* @param {LockFlags} flags
@ -2284,7 +2565,7 @@ Chain.prototype.verifyFinal = async function verifyFinal(prev, tx, flags) {
return tx.isFinal(height, -1);
if (flags & common.lockFlags.MEDIAN_TIME_PAST) {
const time = await prev.getMedianTime();
const time = await this.getMedianTime(prev);
return tx.isFinal(height, time);
}
@ -2293,7 +2574,6 @@ Chain.prototype.verifyFinal = async function verifyFinal(prev, tx, flags) {
/**
* Get the necessary minimum time and height sequence locks for a transaction.
* @method
* @param {ChainEntry} prev
* @param {TX} tx
* @param {CoinView} view
@ -2333,10 +2613,10 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) {
height = Math.max(height - 1, 0);
const entry = await prev.getAncestor(height);
const entry = await this.getAncestor(prev, height);
assert(entry, 'Database is corrupt.');
let time = await entry.getMedianTime();
let time = await this.getMedianTime(entry);
time += ((sequence & MASK) << GRANULARITY) - 1;
minTime = Math.max(minTime, time);
}
@ -2346,7 +2626,6 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) {
/**
* Verify sequence locks.
* @method
* @param {ChainEntry} prev
* @param {TX} tx
* @param {CoinView} view
@ -2365,7 +2644,7 @@ Chain.prototype.verifyLocks = async function verifyLocks(prev, tx, view, flags)
if (time === -1)
return true;
const mtp = await prev.getMedianTime();
const mtp = await this.getMedianTime(prev);
if (time >= mtp)
return false;
@ -2650,6 +2929,14 @@ function Orphan(block, flags, id) {
this.time = util.now();
}
/*
* Helpers
*/
function cmp(a, b) {
return a - b;
}
/*
* Expose
*/

View File

@ -33,7 +33,6 @@ const U32 = encoding.U32;
* The database backend for the {@link Chain} object.
* @alias module:blockchain.ChainDB
* @constructor
* @param {Chain} chain
* @param {Boolean?} options.prune - Whether to prune the chain.
* @param {Boolean?} options.spv - SPV-mode, will not save block
* data, only entries.
@ -45,12 +44,11 @@ const U32 = encoding.U32;
* @emits ChainDB#error
*/
function ChainDB(chain) {
function ChainDB(options) {
if (!(this instanceof ChainDB))
return new ChainDB(chain);
return new ChainDB(options);
this.chain = chain;
this.options = chain.options;
this.options = options;
this.network = this.options.network;
this.logger = this.options.logger.context('chaindb');
@ -74,7 +72,6 @@ ChainDB.layout = layout;
/**
* Open the chain db, wait for the database to load.
* @method
* @returns {Promise}
*/
@ -201,7 +198,6 @@ ChainDB.prototype.drop = function drop() {
/**
* Commit current batch.
* @method
* @returns {Promise}
*/
@ -267,7 +263,6 @@ ChainDB.prototype.getCache = function getCache(block) {
/**
* Get the height of a block by hash.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns Number.
*/
@ -297,7 +292,6 @@ ChainDB.prototype.getHeight = async function getHeight(hash) {
/**
* Get the hash of a block by height. Note that this
* will only return hashes in the main chain.
* @method
* @param {Number} height
* @returns {Promise} - Returns {@link Hash}.
*/
@ -326,7 +320,6 @@ ChainDB.prototype.getHash = async function getHash(height) {
/**
* Retrieve a chain entry by height.
* @method
* @param {Number} height
* @returns {Promise} - Returns {@link ChainEntry}.
*/
@ -349,7 +342,7 @@ ChainDB.prototype.getEntryByHeight = async function getEntryByHeight(height) {
const hash = data.toString('hex');
const state = this.chain.state;
const state = this.state;
const entry = await this.getEntryByHash(hash);
if (!entry)
@ -358,7 +351,7 @@ ChainDB.prototype.getEntryByHeight = async function getEntryByHeight(height) {
// By the time getEntry has completed,
// a reorg may have occurred. This entry
// may not be on the main chain anymore.
if (this.chain.state === state)
if (this.state === state)
this.cacheHeight.set(entry.height, entry);
return entry;
@ -366,7 +359,6 @@ ChainDB.prototype.getEntryByHeight = async function getEntryByHeight(height) {
/**
* Retrieve a chain entry by hash.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link ChainEntry}.
*/
@ -387,7 +379,7 @@ ChainDB.prototype.getEntryByHash = async function getEntryByHash(hash) {
if (!raw)
return null;
const entry = ChainEntry.fromRaw(this.chain, raw);
const entry = ChainEntry.fromRaw(raw);
// There's no efficient way to check whether
// this is in the main chain or not, so
@ -411,7 +403,6 @@ ChainDB.prototype.getEntry = function getEntry(block) {
/**
* Test whether the chain contains a block.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns Boolean.
*/
@ -421,18 +412,102 @@ ChainDB.prototype.hasEntry = async function hasEntry(hash) {
return height !== -1;
};
/**
* Get ancestor by `height`.
* @param {ChainEntry} entry
* @param {Number} height
* @returns {Promise} - Returns ChainEntry.
*/
ChainDB.prototype.getAncestor = async function getAncestor(entry, height) {
if (height < 0)
return null;
assert(height >= 0);
assert(height <= entry.height);
if (await this.isMainChain(entry))
return await this.getEntryByHeight(height);
while (entry.height !== height) {
const cache = this.getPrevCache(entry);
if (cache)
entry = cache;
else
entry = await this.getPrevious(entry);
assert(entry);
}
return entry;
};
/**
* Get previous entry.
* @param {ChainEntry} entry
* @returns {Promise} - Returns ChainEntry.
*/
ChainDB.prototype.getPrevious = function getPrevious(entry) {
return this.getEntryByHash(entry.prevBlock);
};
/**
* Get previous cached entry.
* @param {ChainEntry} entry
* @returns {ChainEntry|null}
*/
ChainDB.prototype.getPrevCache = function getPrevCache(entry) {
return this.cacheHash.get(entry.prevBlock) || null;
};
/**
* Get next entry.
* @param {ChainEntry} entry
* @returns {Promise} - Returns ChainEntry.
*/
ChainDB.prototype.getNext = async function getNext(entry) {
const hash = await this.getNextHash(entry.hash);
if (!hash)
return null;
return await this.getEntryByHash(hash);
};
/**
* Get next entry.
* @param {ChainEntry} entry
* @returns {Promise} - Returns ChainEntry.
*/
ChainDB.prototype.getNextEntry = async function getNextEntry(entry) {
const next = await this.getEntryByHeight(entry.height + 1);
if (!next)
return null;
// Not on main chain.
if (next.prevBlock !== entry.hash)
return null;
return next;
};
/**
* Retrieve the tip entry from the tip record.
* @returns {Promise} - Returns {@link ChainEntry}.
*/
ChainDB.prototype.getTip = function getTip() {
return this.getEntry(this.state.hash());
return this.getEntryByHash(this.state.tip);
};
/**
* Retrieve the tip entry from the tip record.
* @method
* @returns {Promise} - Returns {@link ChainState}.
*/
@ -447,14 +522,13 @@ ChainDB.prototype.getState = async function getState() {
/**
* Write genesis block to database.
* @method
* @returns {Promise}
*/
ChainDB.prototype.saveGenesis = async function saveGenesis() {
const genesis = this.network.genesisBlock;
const block = Block.fromRaw(genesis, 'hex');
const entry = ChainEntry.fromBlock(this.chain, block);
const entry = ChainEntry.fromBlock(block);
this.logger.info('Writing genesis block to ChainDB.');
@ -463,7 +537,6 @@ ChainDB.prototype.saveGenesis = async function saveGenesis() {
/**
* Retrieve the database flags.
* @method
* @returns {Promise} - Returns {@link ChainFlags}.
*/
@ -478,7 +551,6 @@ ChainDB.prototype.getFlags = async function getFlags() {
/**
* Verify current options against db options.
* @method
* @param {ChainState} state
* @returns {Promise}
*/
@ -547,13 +619,12 @@ ChainDB.prototype.verifyFlags = async function verifyFlags(state) {
if (needsPrune) {
await this.logger.info('Retroactively pruning chain.');
await this.prune(state.hash());
await this.prune(state.tip);
}
};
/**
* Get state caches.
* @method
* @returns {Promise} - Returns {@link StateCache}.
*/
@ -609,7 +680,6 @@ ChainDB.prototype.writeDeployments = function writeDeployments(batch) {
/**
* Check for outdated deployments.
* @method
* @private
* @returns {Promise}
*/
@ -647,7 +717,6 @@ ChainDB.prototype.checkDeployments = async function checkDeployments() {
/**
* Potentially invalidate state cache.
* @method
* @returns {Promise}
*/
@ -684,7 +753,6 @@ ChainDB.prototype.verifyDeployments = async function verifyDeployments() {
/**
* Invalidate state cache.
* @method
* @private
* @returns {Promise}
*/
@ -701,12 +769,10 @@ ChainDB.prototype.invalidateCache = async function invalidateCache(bit, batch) {
/**
* Retroactively prune the database.
* @method
* @param {Hash} tip
* @returns {Promise}
*/
ChainDB.prototype.prune = async function prune(tip) {
ChainDB.prototype.prune = async function prune() {
const options = this.options;
const keepBlocks = this.network.block.keepBlocks;
const pruneAfter = this.network.block.pruneAfterHeight;
@ -716,7 +782,7 @@ ChainDB.prototype.prune = async function prune(tip) {
if (flags.prune)
throw new Error('Chain is already pruned.');
const height = await this.getHeight(tip);
const height = await this.getHeight(this.state.tip);
if (height <= pruneAfter + keepBlocks)
return false;
@ -756,7 +822,6 @@ ChainDB.prototype.prune = async function prune(tip) {
/**
* Get the _next_ block hash (does not work by height).
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link Hash}.
*/
@ -772,22 +837,22 @@ ChainDB.prototype.getNextHash = async function getNextHash(hash) {
/**
* Check to see if a block is on the main chain.
* @method
* @param {ChainEntry|Hash} hash
* @param {Hash} hash
* @returns {Promise} - Returns Boolean.
*/
ChainDB.prototype.isMainChain = async function isMainChain(hash) {
ChainDB.prototype.isMainHash = async function isMainHash(hash) {
assert(typeof hash === 'string');
if (hash === this.chain.tip.hash
|| hash === this.network.genesis.hash) {
return true;
}
if (hash === encoding.NULL_HASH)
return false;
if (hash === this.network.genesis.hash)
return true;
if (hash === this.state.tip)
return true;
const cacheHash = this.cacheHash.get(hash);
if (cacheHash) {
@ -802,6 +867,30 @@ ChainDB.prototype.isMainChain = async function isMainChain(hash) {
return false;
};
/**
* Test whether the entry is in the main chain.
* @param {ChainEntry} entry
* @returns {Promise} - Returns Boolean.
*/
ChainDB.prototype.isMainChain = async function isMainChain(entry) {
if (entry.isGenesis())
return true;
if (entry.hash === this.state.tip)
return true;
const cache = this.getCache(entry.height);
if (cache)
return entry.hash === cache.hash;
if (await this.getNextHash(entry.hash))
return true;
return false;
};
/**
* Get all entries.
* @returns {Promise} - Returns {@link ChainEntry}[].
@ -811,7 +900,7 @@ ChainDB.prototype.getEntries = function getEntries() {
return this.db.values({
gte: layout.e(encoding.ZERO_HASH),
lte: layout.e(encoding.MAX_HASH),
parse: value => ChainEntry.fromRaw(this.chain, value)
parse: value => ChainEntry.fromRaw(value)
});
};
@ -830,7 +919,6 @@ ChainDB.prototype.getTips = function getTips() {
/**
* Get a coin (unspents only).
* @method
* @private
* @param {Outpoint} prevout
* @returns {Promise} - Returns {@link CoinEntry}.
@ -862,7 +950,6 @@ ChainDB.prototype.readCoin = async function readCoin(prevout) {
/**
* Get a coin (unspents only).
* @method
* @param {Hash} hash
* @param {Number} index
* @returns {Promise} - Returns {@link Coin}.
@ -896,7 +983,6 @@ ChainDB.prototype.hasCoins = async function hasCoins(tx) {
/**
* Get coin viewpoint.
* @method
* @param {TX} tx
* @returns {Promise} - Returns {@link CoinView}.
*/
@ -921,7 +1007,6 @@ ChainDB.prototype.getCoinView = async function getCoinView(tx) {
/**
* Get coin viewpoint (historical).
* @method
* @param {TX} tx
* @returns {Promise} - Returns {@link CoinView}.
*/
@ -946,7 +1031,6 @@ ChainDB.prototype.getSpentView = async function getSpentView(tx) {
/**
* Get coins necessary to be resurrected during a reorg.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link Coin}[].
*/
@ -962,7 +1046,6 @@ ChainDB.prototype.getUndoCoins = async function getUndoCoins(hash) {
/**
* Retrieve a block from the database (not filled with coins).
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link Block}.
*/
@ -978,7 +1061,6 @@ ChainDB.prototype.getBlock = async function getBlock(hash) {
/**
* Retrieve a block from the database (not filled with coins).
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link Block}.
*/
@ -997,7 +1079,6 @@ ChainDB.prototype.getRawBlock = async function getRawBlock(block) {
/**
* Get a historical block coin viewpoint.
* @method
* @param {Block} hash
* @returns {Promise} - Returns {@link CoinView}.
*/
@ -1026,7 +1107,6 @@ ChainDB.prototype.getBlockView = async function getBlockView(block) {
/**
* Get a transaction with metadata.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link TXMeta}.
*/
@ -1045,7 +1125,6 @@ ChainDB.prototype.getMeta = async function getMeta(hash) {
/**
* Retrieve a transaction.
* @method
* @param {Hash} hash
* @returns {Promise} - Returns {@link TX}.
*/
@ -1073,7 +1152,6 @@ ChainDB.prototype.hasTX = async function hasTX(hash) {
/**
* Get all coins pertinent to an address.
* @method
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link Coin}[].
*/
@ -1108,7 +1186,6 @@ ChainDB.prototype.getCoinsByAddress = async function getCoinsByAddress(addrs) {
/**
* Get all transaction hashes to an address.
* @method
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link Hash}[].
*/
@ -1137,7 +1214,6 @@ ChainDB.prototype.getHashesByAddress = async function getHashesByAddress(addrs)
/**
* Get all transactions pertinent to an address.
* @method
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link TX}[].
*/
@ -1154,7 +1230,6 @@ ChainDB.prototype.getTXByAddress = async function getTXByAddress(addrs) {
/**
* Get all transactions pertinent to an address.
* @method
* @param {Address[]} addrs
* @returns {Promise} - Returns {@link TXMeta}[].
*/
@ -1180,7 +1255,6 @@ ChainDB.prototype.getMetaByAddress = async function getMetaByAddress(addrs) {
/**
* Scan the blockchain for transactions containing specified address hashes.
* @method
* @param {Hash} start - Block hash to start at.
* @param {Bloom} filter - Bloom filter containing tx and address hashes.
* @param {Function} iter - Iterator.
@ -1201,7 +1275,7 @@ ChainDB.prototype.scan = async function scan(start, filter, iter) {
if (!entry)
return;
if (!await entry.isMainChain())
if (!await this.isMainChain(entry))
throw new Error('Cannot rescan an alternate chain.');
let total = 0;
@ -1216,7 +1290,7 @@ ChainDB.prototype.scan = async function scan(start, filter, iter) {
if (!this.options.spv && !this.options.prune)
throw new Error('Block not found.');
await iter(entry, txs);
entry = await entry.getNext();
entry = await this.getNext(entry);
continue;
}
@ -1260,7 +1334,7 @@ ChainDB.prototype.scan = async function scan(start, filter, iter) {
await iter(entry, txs);
entry = await entry.getNext();
entry = await this.getNext(entry);
}
this.logger.info('Finished scanning %d blocks.', total);
@ -1271,7 +1345,6 @@ ChainDB.prototype.scan = async function scan(start, filter, iter) {
* connect it as the tip. Note that this method
* does _not_ perform any verification which is
* instead performed in {@link Chain#add}.
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView?} view - Will not connect if null.
@ -1291,7 +1364,6 @@ ChainDB.prototype.save = async function save(entry, block, view) {
/**
* Save an entry without a batch.
* @method
* @private
* @param {ChainEntry} entry
* @param {Block} block
@ -1339,7 +1411,6 @@ ChainDB.prototype._save = async function _save(entry, block, view) {
/**
* Reconnect the block to the chain.
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView} view
@ -1359,7 +1430,6 @@ ChainDB.prototype.reconnect = async function reconnect(entry, block, view) {
/**
* Reconnect block without a batch.
* @method
* @private
* @param {ChainEntry} entry
* @param {Block} block
@ -1394,7 +1464,6 @@ ChainDB.prototype._reconnect = async function _reconnect(entry, block, view) {
/**
* Disconnect block from the chain.
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @returns {Promise}
@ -1419,7 +1488,6 @@ ChainDB.prototype.disconnect = async function disconnect(entry, block) {
/**
* Disconnect block without a batch.
* @private
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @returns {Promise} - Returns {@link CoinView}.
@ -1467,7 +1535,6 @@ ChainDB.prototype.saveUpdates = function saveUpdates() {
/**
* Reset the chain to a height or hash. Useful for replaying
* the blockchain download for SPV.
* @method
* @param {Hash|Number} block - hash/height
* @returns {Promise}
*/
@ -1478,7 +1545,7 @@ ChainDB.prototype.reset = async function reset(block) {
if (!entry)
throw new Error('Block not found.');
if (!await entry.isMainChain())
if (!await this.isMainChain(entry))
throw new Error('Cannot reset on alternate chain.');
if (this.options.prune)
@ -1535,7 +1602,7 @@ ChainDB.prototype.reset = async function reset(block) {
this.cacheHeight.remove(tip.height);
this.cacheHash.remove(tip.hash);
tip = await this.getEntry(tip.prevBlock);
tip = await this.getPrevious(tip);
assert(tip);
}
@ -1544,7 +1611,6 @@ ChainDB.prototype.reset = async function reset(block) {
/**
* Remove all alternate chains.
* @method
* @returns {Promise}
*/
@ -1568,14 +1634,13 @@ ChainDB.prototype.removeChains = async function removeChains() {
/**
* Remove an alternate chain.
* @method
* @private
* @param {Hash} hash - Alternate chain tip.
* @returns {Promise}
*/
ChainDB.prototype._removeChain = async function _removeChain(hash) {
let tip = await this.getEntry(hash);
let tip = await this.getEntryByHash(hash);
if (!tip)
throw new Error('Alternate chain tip not found.');
@ -1583,7 +1648,7 @@ ChainDB.prototype._removeChain = async function _removeChain(hash) {
this.logger.debug('Removing alternate chain: %s.', tip.rhash());
for (;;) {
if (await tip.isMainChain())
if (await this.isMainChain(tip))
break;
assert(!tip.isGenesis());
@ -1598,7 +1663,7 @@ ChainDB.prototype._removeChain = async function _removeChain(hash) {
// on successful write.
this.cacheHash.unpush(tip.hash);
tip = await this.getEntry(tip.prevBlock);
tip = await this.getPrevious(tip);
assert(tip);
}
};
@ -1606,7 +1671,6 @@ ChainDB.prototype._removeChain = async function _removeChain(hash) {
/**
* Save a block (not an entry) to the
* database and potentially connect the inputs.
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView?} view
@ -1632,7 +1696,6 @@ ChainDB.prototype.saveBlock = async function saveBlock(entry, block, view) {
/**
* Remove a block (not an entry) to the database.
* Disconnect inputs.
* @method
* @param {ChainEntry} entry
* @returns {Promise} - Returns {@link Block}.
*/
@ -1676,7 +1739,6 @@ ChainDB.prototype.saveView = function saveView(view) {
/**
* Connect block inputs.
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @param {CoinView} view
@ -1692,7 +1754,7 @@ ChainDB.prototype.connectBlock = async function connectBlock(entry, block, view)
this.pending.connect(block);
// Genesis block's coinbase is unspendable.
if (this.chain.isGenesis(block))
if (entry.isGenesis())
return;
// Update chain state value.
@ -1728,7 +1790,6 @@ ChainDB.prototype.connectBlock = async function connectBlock(entry, block, view)
/**
* Disconnect block inputs.
* @method
* @param {ChainEntry} entry
* @param {Block} block
* @returns {Promise} - Returns {@link CoinView}.
@ -1788,7 +1849,6 @@ ChainDB.prototype.disconnectBlock = async function disconnectBlock(entry, block)
/**
* Prune a block from the chain and
* add current block to the prune queue.
* @method
* @private
* @param {ChainEntry} entry
* @returns {Promise}
@ -2042,19 +2102,15 @@ ChainFlags.fromRaw = function fromRaw(data) {
*/
function ChainState() {
this.tip = encoding.ZERO_HASH;
this.tip = encoding.NULL_HASH;
this.tx = 0;
this.coin = 0;
this.value = 0;
this.committed = false;
}
ChainState.prototype.hash = function hash() {
return this.tip.toString('hex');
};
ChainState.prototype.rhash = function rhash() {
return util.revHex(this.hash());
return util.revHex(this.tip);
};
ChainState.prototype.clone = function clone() {
@ -2085,8 +2141,8 @@ ChainState.prototype.spend = function spend(coin) {
};
ChainState.prototype.commit = function commit(hash) {
if (typeof hash === 'string')
hash = Buffer.from(hash, 'hex');
if (typeof hash !== 'string')
hash = hash.toString('hex');
this.tip = hash;
this.committed = true;
return this.toRaw();
@ -2104,7 +2160,7 @@ ChainState.prototype.toRaw = function toRaw() {
ChainState.fromRaw = function fromRaw(data) {
const state = new ChainState();
const br = new BufferReader(data);
state.tip = br.readHash();
state.tip = br.readHash('hex');
state.tx = br.readU64();
state.coin = br.readU64();
state.value = br.readU64();

View File

@ -27,9 +27,7 @@ const ZERO = new BN(0);
* boot and recalculating the chainworks.
* @alias module:blockchain.ChainEntry
* @constructor
* @param {Chain} chain
* @param {Object} options
* @param {ChainEntry} prev
* @param {Object?} options
* @property {Hash} hash
* @property {Number} version - Transaction version. Note that Bcoin reads
* versions as unsigned even though they are signed at the protocol level.
@ -44,11 +42,10 @@ const ZERO = new BN(0);
* @property {ReversedHash} rhash - Reversed block hash (uint256le).
*/
function ChainEntry(chain, options, prev) {
function ChainEntry(options) {
if (!(this instanceof ChainEntry))
return new ChainEntry(chain, options, prev);
return new ChainEntry(options);
this.chain = chain;
this.hash = encoding.NULL_HASH;
this.version = 1;
this.prevBlock = encoding.NULL_HASH;
@ -60,7 +57,7 @@ function ChainEntry(chain, options, prev) {
this.chainwork = ZERO;
if (options)
this.fromOptions(options, prev);
this.fromOptions(options);
}
/**
@ -70,22 +67,13 @@ function ChainEntry(chain, options, prev) {
ChainEntry.MAX_CHAINWORK = new BN(1).ushln(256);
/**
* Size of set to pick median time from.
* @const {Number}
* @default
*/
ChainEntry.MEDIAN_TIMESPAN = 11;
/**
* Inject properties from options.
* @private
* @param {Object} options
* @param {ChainEntry} prev - Previous entry.
*/
ChainEntry.prototype.fromOptions = function fromOptions(options, prev) {
ChainEntry.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Block data is required.');
assert(typeof options.hash === 'string');
assert(util.isU32(options.version));
@ -107,22 +95,18 @@ ChainEntry.prototype.fromOptions = function fromOptions(options, prev) {
this.height = options.height;
this.chainwork = options.chainwork || ZERO;
if (!this.chainwork)
this.chainwork = this.getChainwork(prev);
return this;
};
/**
* Instantiate chainentry from options.
* @param {Chain} chain
* @param {Object} options
* @param {ChainEntry} prev - Previous entry.
* @returns {ChainEntry}
*/
ChainEntry.fromOptions = function fromOptions(chain, options, prev) {
return new ChainEntry(chain).fromOptions(options, prev);
ChainEntry.fromOptions = function fromOptions(options, prev) {
return new ChainEntry().fromOptions(options, prev);
};
/**
@ -160,178 +144,23 @@ ChainEntry.prototype.getChainwork = function getChainwork(prev) {
*/
ChainEntry.prototype.isGenesis = function isGenesis() {
return this.hash === this.chain.network.genesis.hash;
};
/**
* Test whether the entry is in the main chain.
* @method
* @returns {Promise} - Return Boolean.
*/
ChainEntry.prototype.isMainChain = async function isMainChain() {
if (this.hash === this.chain.tip.hash
|| this.hash === this.chain.network.genesis.hash) {
return true;
}
const entry = this.chain.db.getCache(this.height);
if (entry) {
if (entry.hash === this.hash)
return true;
return false;
}
if (await this.chain.db.getNextHash(this.hash))
return true;
return false;
};
/**
* Get ancestor by `height`.
* @method
* @param {Number} height
* @returns {Promise} - Returns ChainEntry[].
*/
ChainEntry.prototype.getAncestor = async function getAncestor(height) {
if (height < 0)
return null;
assert(height >= 0);
assert(height <= this.height);
if (await this.isMainChain())
return await this.chain.db.getEntry(height);
let entry = this;
while (entry.height !== height) {
entry = await entry.getPrevious();
assert(entry);
}
return entry;
};
/**
* Get previous entry.
* @returns {Promise} - Returns ChainEntry.
*/
ChainEntry.prototype.getPrevious = function getPrevious() {
return this.chain.db.getEntry(this.prevBlock);
};
/**
* Get previous cached entry.
* @returns {ChainEntry|null}
*/
ChainEntry.prototype.getPrevCache = function getPrevCache() {
return this.chain.db.getCache(this.prevBlock);
};
/**
* Get next entry.
* @method
* @returns {Promise} - Returns ChainEntry.
*/
ChainEntry.prototype.getNext = async function getNext() {
const hash = await this.chain.db.getNextHash(this.hash);
if (!hash)
return null;
return await this.chain.db.getEntry(hash);
};
/**
* Get next entry.
* @method
* @returns {Promise} - Returns ChainEntry.
*/
ChainEntry.prototype.getNextEntry = async function getNextEntry() {
const entry = await this.chain.db.getEntry(this.height + 1);
if (!entry)
return null;
// Not on main chain.
if (entry.prevBlock !== this.hash)
return null;
return entry;
};
/**
* Calculate median time past.
* @method
* @param {Number?} time
* @returns {Promise} - Returns Number.
*/
ChainEntry.prototype.getMedianTime = async function getMedianTime(time) {
let timespan = ChainEntry.MEDIAN_TIMESPAN;
const median = [];
// In case we ever want to check
// the MTP of the _current_ block
// (necessary for BIP148).
if (time != null) {
median.push(time);
timespan -= 1;
}
let entry = this;
for (let i = 0; i < timespan && entry; i++) {
median.push(entry.time);
const cache = entry.getPrevCache();
if (cache) {
entry = cache;
continue;
}
entry = await entry.getPrevious();
}
median.sort(cmp);
return median[median.length >>> 1];
};
/**
* Test whether the entry is potentially
* an ancestor of a checkpoint.
* @returns {Boolean}
*/
ChainEntry.prototype.isHistorical = function isHistorical() {
if (this.chain.options.checkpoints) {
if (this.height + 1 <= this.chain.network.lastCheckpoint)
return true;
}
return false;
return this.prevBlock === encoding.NULL_HASH;
};
/**
* Test whether the entry contains an unknown version bit.
* @param {Network} network
* @returns {Boolean}
*/
ChainEntry.prototype.hasUnknown = function hasUnknown() {
ChainEntry.prototype.hasUnknown = function hasUnknown(network) {
const bits = this.version & consensus.VERSION_TOP_MASK;
const topBits = consensus.VERSION_TOP_BITS;
if ((bits >>> 0) !== topBits)
return false;
return (this.version & this.chain.network.unknownBits) !== 0;
return (this.version & network.unknownBits) !== 0;
};
/**
@ -375,14 +204,13 @@ ChainEntry.prototype.fromBlock = function fromBlock(block, prev) {
/**
* Instantiate chainentry from block.
* @param {Chain} chain
* @param {Block|MerkleBlock} block
* @param {ChainEntry} prev - Previous entry.
* @returns {ChainEntry}
*/
ChainEntry.fromBlock = function fromBlock(chain, block, prev) {
return new ChainEntry(chain).fromBlock(block, prev);
ChainEntry.fromBlock = function fromBlock(block, prev) {
return new ChainEntry().fromBlock(block, prev);
};
/**
@ -432,13 +260,12 @@ ChainEntry.prototype.fromRaw = function fromRaw(data) {
/**
* Deserialize the entry.
* @param {Chain} chain
* @param {Buffer} data
* @returns {ChainEntry}
*/
ChainEntry.fromRaw = function fromRaw(chain, data) {
return new ChainEntry(chain).fromRaw(data);
ChainEntry.fromRaw = function fromRaw(data) {
return new ChainEntry().fromRaw(data);
};
/**
@ -493,13 +320,12 @@ ChainEntry.prototype.fromJSON = function fromJSON(json) {
/**
* Instantiate block from jsonified object.
* @param {Chain} chain
* @param {Object} json
* @returns {ChainEntry}
*/
ChainEntry.fromJSON = function fromJSON(chain, json) {
return new ChainEntry(chain).fromJSON(json);
ChainEntry.fromJSON = function fromJSON(json) {
return new ChainEntry().fromJSON(json);
};
/**
@ -541,14 +367,6 @@ ChainEntry.isChainEntry = function isChainEntry(obj) {
return obj instanceof ChainEntry;
};
/*
* Helpers
*/
function cmp(a, b) {
return a - b;
}
/*
* Expose
*/

View File

@ -503,7 +503,7 @@ RPC.prototype.getBlockchainInfo = async function getBlockchainInfo(args, help) {
headers: this.chain.height,
bestblockhash: this.chain.tip.rhash(),
difficulty: toDifficulty(this.chain.tip.bits),
mediantime: await this.chain.tip.getMedianTime(),
mediantime: await this.chain.getMedianTime(this.chain.tip),
verificationprogress: this.chain.getProgress(),
chainwork: this.chain.tip.chainwork.toString('hex', 64),
pruned: this.chain.options.prune,
@ -541,12 +541,12 @@ RPC.prototype.getBlock = async function getBlock(args, help) {
if (!hash)
throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.');
const entry = await this.chain.db.getEntry(hash);
const entry = await this.chain.getEntry(hash);
if (!entry)
throw new RPCError(errs.MISC_ERROR, 'Block not found');
const block = await this.chain.db.getBlock(entry.hash);
const block = await this.chain.getBlock(entry.hash);
if (!block) {
if (this.chain.options.spv)
@ -578,12 +578,12 @@ RPC.prototype.getBlockByHeight = async function getBlockByHeight(args, help) {
if (height === -1)
throw new RPCError(errs.TYPE_ERROR, 'Invalid block height.');
const entry = await this.chain.db.getEntry(height);
const entry = await this.chain.getEntry(height);
if (!entry)
throw new RPCError(errs.MISC_ERROR, 'Block not found');
const block = await this.chain.db.getBlock(entry.hash);
const block = await this.chain.getBlock(entry.hash);
if (!block) {
if (this.chain.options.spv)
@ -611,7 +611,7 @@ RPC.prototype.getBlockHash = async function getBlockHash(args, help) {
if (height == null || height > this.chain.height)
throw new RPCError(errs.INVALID_PARAMETER, 'Block height out of range.');
const hash = await this.chain.db.getHash(height);
const hash = await this.chain.getHash(height);
if (!hash)
throw new RPCError(errs.MISC_ERROR, 'Not found.');
@ -630,7 +630,7 @@ RPC.prototype.getBlockHeader = async function getBlockHeader(args, help) {
if (!hash)
throw new RPCError(errs.MISC_ERROR, 'Invalid block hash.');
const entry = await this.chain.db.getEntry(hash);
const entry = await this.chain.getEntry(hash);
if (!entry)
throw new RPCError(errs.MISC_ERROR, 'Block not found');
@ -645,16 +645,16 @@ RPC.prototype.getChainTips = async function getChainTips(args, help) {
if (help || args.length !== 0)
throw new RPCError(errs.MISC_ERROR, 'getchaintips');
const tips = await this.chain.db.getTips();
const tips = await this.chain.getTips();
const result = [];
for (const hash of tips) {
const entry = await this.chain.db.getEntry(hash);
const entry = await this.chain.getEntry(hash);
assert(entry);
const fork = await this.findFork(entry);
const main = await entry.isMainChain();
const main = await this.chain.isMainChain(entry);
result.push({
height: entry.height,
@ -827,7 +827,7 @@ RPC.prototype.getTXOut = async function getTXOut(args, help) {
}
if (!coin)
coin = await this.chain.db.getCoin(hash, index);
coin = await this.chain.getCoin(hash, index);
if (!coin)
return null;
@ -885,15 +885,15 @@ RPC.prototype.getTXOutProof = async function getTXOutProof(args, help) {
let block = null;
if (hash) {
block = await this.chain.db.getBlock(hash);
block = await this.chain.getBlock(hash);
} else if (this.chain.options.indexTX) {
const tx = await this.chain.db.getMeta(last);
const tx = await this.chain.getMeta(last);
if (tx)
block = await this.chain.db.getBlock(tx.block);
block = await this.chain.getBlock(tx.block);
} else {
const coin = await this.chain.db.getCoin(last, 0);
const coin = await this.chain.getCoin(last, 0);
if (coin)
block = await this.chain.db.getBlock(coin.height);
block = await this.chain.getBlock(coin.height);
}
if (!block)
@ -926,7 +926,7 @@ RPC.prototype.verifyTXOutProof = async function verifyTXOutProof(args, help) {
if (!block.verify())
return [];
const entry = await this.chain.db.getEntry(block.hash('hex'));
const entry = await this.chain.getEntry(block.hash('hex'));
if (!entry)
throw new RPCError(errs.MISC_ERROR, 'Block not found in chain.');
@ -1765,7 +1765,7 @@ RPC.prototype.getRawTransaction = async function getRawTransaction(args, help) {
let entry;
if (meta.block)
entry = await this.chain.db.getEntry(meta.block);
entry = await this.chain.getEntry(meta.block);
const json = this.txToJSON(tx, entry);
json.time = meta.mtime;
@ -2326,7 +2326,7 @@ RPC.prototype.addBlock = async function addBlock(block) {
RPC.prototype._addBlock = async function _addBlock(block) {
this.logger.info('Handling submitted block: %s.', block.rhash());
const prev = await this.chain.db.getEntry(block.prevBlock);
const prev = await this.chain.getEntry(block.prevBlock);
if (prev) {
const state = await this.chain.getDeployments(block.time, prev);
@ -2426,7 +2426,7 @@ RPC.prototype.getHashRate = async function getHashRate(lookup, height) {
let tip = this.chain.tip;
if (height != null)
tip = await this.chain.db.getEntry(height);
tip = await this.chain.getEntry(height);
if (!tip)
return 0;
@ -2445,7 +2445,7 @@ RPC.prototype.getHashRate = async function getHashRate(lookup, height) {
let entry = tip;
for (let i = 0; i < lookup; i++) {
entry = await entry.getPrevious();
entry = await this.chain.getPrevious(entry);
if (!entry)
throw new RPCError(errs.DATABASE_ERROR, 'Not found.');
@ -2488,9 +2488,9 @@ RPC.prototype._mineBlocks = async function _mineBlocks(blocks, addr, tries) {
RPC.prototype.findFork = async function findFork(entry) {
while (entry) {
if (await entry.isMainChain())
if (await this.chain.isMainChain(entry))
return entry;
entry = await entry.getPrevious();
entry = await this.chain.getPrevious(entry);
}
throw new Error('Fork not found.');
};
@ -2598,8 +2598,8 @@ RPC.prototype.scriptToJSON = function scriptToJSON(script, hex) {
};
RPC.prototype.headerToJSON = async function headerToJSON(entry) {
const mtp = await entry.getMedianTime();
const next = await this.chain.db.getNextHash(entry.hash);
const mtp = await this.chain.getMedianTime(entry);
const next = await this.chain.getNextHash(entry.hash);
return {
hash: entry.rhash(),
@ -2621,8 +2621,8 @@ RPC.prototype.headerToJSON = async function headerToJSON(entry) {
};
RPC.prototype.blockToJSON = async function blockToJSON(entry, block, details) {
const mtp = await entry.getMedianTime();
const next = await this.chain.db.getNextHash(entry.hash);
const mtp = await this.chain.getMedianTime(entry);
const next = await this.chain.getNextHash(entry.hash);
const txs = [];
for (const tx of block.txs) {

View File

@ -264,21 +264,21 @@ HTTPServer.prototype.initRouter = function initRouter() {
else
hash = parseInt(hash, 10);
const block = await this.chain.db.getBlock(hash);
const block = await this.chain.getBlock(hash);
if (!block) {
res.send(404);
return;
}
const view = await this.chain.db.getBlockView(block);
const view = await this.chain.getBlockView(block);
if (!view) {
res.send(404);
return;
}
const height = await this.chain.db.getHeight(hash);
const height = await this.chain.getHeight(hash);
res.send(200, block.getJSON(this.network, view, height));
});
@ -441,12 +441,12 @@ HTTPServer.prototype.handleAuth = function handleAuth(socket) {
if (block == null)
throw new Error('Invalid parameter.');
const entry = await this.chain.db.getEntry(block);
const entry = await this.chain.getEntry(block);
if (!entry)
return null;
if (!await entry.isMainChain())
if (!await this.chain.isMainChain(entry))
return null;
return entry.toRaw();

View File

@ -132,7 +132,7 @@ Miner.prototype._createBlock = async function _createBlock(tip, address) {
if (version === -1)
version = await this.chain.computeBlockVersion(tip);
const mtp = await tip.getMedianTime();
const mtp = await this.chain.getMedianTime(tip);
const time = Math.max(this.network.now(), mtp + 1);
const state = await this.chain.getDeployments(time, tip);

View File

@ -1031,7 +1031,7 @@ Pool.prototype.getItem = async function getItem(peer, item) {
if (this.chain.options.prune)
return null;
return await this.chain.db.getBlock(item.hash);
return await this.chain.getBlock(item.hash);
};
/**
@ -1061,7 +1061,7 @@ Pool.prototype.sendBlock = async function sendBlock(peer, item, witness) {
// If we have the same serialization, we
// can write the raw binary to the socket.
if (witness || !this.options.hasWitness()) {
const block = await this.chain.db.getRawBlock(item.hash);
const block = await this.chain.getRawBlock(item.hash);
if (block) {
peer.sendRaw('block', block);
@ -1071,7 +1071,7 @@ Pool.prototype.sendBlock = async function sendBlock(peer, item, witness) {
return false;
}
const block = await this.chain.db.getBlock(item.hash);
const block = await this.chain.getBlock(item.hash);
if (block) {
peer.send(new packets.BlockPacket(block, witness));
@ -1727,7 +1727,7 @@ Pool.prototype.handleBlockInv = async function handleBlockInv(peer, hashes) {
// Attempt to update the peer's best height
// with the last existing hash we know of.
if (exists && this.chain.synced) {
const height = await this.chain.db.getHeight(exists);
const height = await this.chain.getHeight(exists);
if (height !== -1)
peer.bestHeight = height;
}
@ -1849,7 +1849,7 @@ Pool.prototype.handleGetData = async function handleGetData(peer, packet) {
break;
}
case invTypes.CMPCT_BLOCK: {
const height = await this.chain.db.getHeight(item.hash);
const height = await this.chain.getHeight(item.hash);
// Fallback to full block.
if (height < this.chain.tip.height - 10) {
@ -1962,7 +1962,7 @@ Pool.prototype.handleGetBlocks = async function handleGetBlocks(peer, packet) {
let hash = await this.chain.findLocator(packet.locator);
if (hash)
hash = await this.chain.db.getNextHash(hash);
hash = await this.chain.getNextHash(hash);
const blocks = [];
@ -1977,7 +1977,7 @@ Pool.prototype.handleGetBlocks = async function handleGetBlocks(peer, packet) {
break;
}
hash = await this.chain.db.getNextHash(hash);
hash = await this.chain.getNextHash(hash);
}
peer.sendInv(blocks);
@ -2008,14 +2008,14 @@ Pool.prototype.handleGetHeaders = async function handleGetHeaders(peer, packet)
if (packet.locator.length > 0) {
hash = await this.chain.findLocator(packet.locator);
if (hash)
hash = await this.chain.db.getNextHash(hash);
hash = await this.chain.getNextHash(hash);
} else {
hash = packet.stop;
}
let entry;
if (hash)
entry = await this.chain.db.getEntry(hash);
entry = await this.chain.getEntry(hash);
const headers = [];
@ -2028,7 +2028,7 @@ Pool.prototype.handleGetHeaders = async function handleGetHeaders(peer, packet)
if (headers.length === 2000)
break;
entry = await entry.getNext();
entry = await this.chain.getNext(entry);
}
peer.sendHeaders(headers);
@ -2874,7 +2874,7 @@ Pool.prototype.handleGetBlockTxn = async function handleGetBlockTxn(peer, packet
return;
}
const height = await this.chain.db.getHeight(req.hash);
const height = await this.chain.getHeight(req.hash);
if (height < this.chain.tip.height - 15) {
this.logger.debug(

View File

@ -367,7 +367,7 @@ FullNode.prototype.stopSync = function stopSync() {
*/
FullNode.prototype.getBlock = function getBlock(hash) {
return this.chain.db.getBlock(hash);
return this.chain.getBlock(hash);
};
/**
@ -387,7 +387,7 @@ FullNode.prototype.getCoin = async function getCoin(hash, index) {
if (this.mempool.isSpent(hash, index))
return null;
return await this.chain.db.getCoin(hash, index);
return await this.chain.getCoin(hash, index);
};
/**
@ -399,7 +399,7 @@ FullNode.prototype.getCoin = async function getCoin(hash, index) {
FullNode.prototype.getCoinsByAddress = async function getCoinsByAddress(addrs) {
const mempool = this.mempool.getCoinsByAddress(addrs);
const chain = await this.chain.db.getCoinsByAddress(addrs);
const chain = await this.chain.getCoinsByAddress(addrs);
const out = [];
for (const coin of chain) {
@ -426,7 +426,7 @@ FullNode.prototype.getCoinsByAddress = async function getCoinsByAddress(addrs) {
FullNode.prototype.getMetaByAddress = async function getMetaByAddress(addrs) {
const mempool = this.mempool.getMetaByAddress(addrs);
const chain = await this.chain.db.getMetaByAddress(addrs);
const chain = await this.chain.getMetaByAddress(addrs);
return chain.concat(mempool);
};
@ -442,7 +442,7 @@ FullNode.prototype.getMeta = async function getMeta(hash) {
if (meta)
return meta;
return await this.chain.db.getMeta(hash);
return await this.chain.getMeta(hash);
};
/**
@ -499,7 +499,7 @@ FullNode.prototype.hasTX = async function hasTX(hash) {
if (this.mempool.hasEntry(hash))
return true;
return await this.chain.db.hasTX(hash);
return await this.chain.hasTX(hash);
};
/*

View File

@ -102,12 +102,12 @@ NodeClient.prototype.getTip = function getTip() {
*/
NodeClient.prototype.getEntry = async function getEntry(hash) {
const entry = await this.node.chain.db.getEntry(hash);
const entry = await this.node.chain.getEntry(hash);
if (!entry)
return null;
if (!await entry.isMainChain())
if (!await this.node.chain.isMainChain(entry))
return null;
return entry;

View File

@ -158,13 +158,13 @@ describe('Chain', function() {
assert.strictEqual(chain.tip.hash, hash1);
tip1 = await chain.db.getEntry(hash1);
tip2 = await chain.db.getEntry(hash2);
tip1 = await chain.getEntry(hash1);
tip2 = await chain.getEntry(hash2);
assert(tip1);
assert(tip2);
assert(!await tip2.isMainChain());
assert(!await chain.isMainChain(tip2));
}
});
@ -181,7 +181,7 @@ describe('Chain', function() {
it('should handle a reorg', async () => {
assert.strictEqual(chain.height, 210);
const entry = await chain.db.getEntry(tip2.hash);
const entry = await chain.getEntry(tip2.hash);
assert(entry);
assert.strictEqual(chain.height, entry.height);
@ -211,7 +211,7 @@ describe('Chain', function() {
});
it('should check main chain', async () => {
const result = await tip1.isMainChain();
const result = await chain.isMainChain(tip1);
assert(!result);
});
@ -221,12 +221,12 @@ describe('Chain', function() {
assert(await chain.add(block));
const hash = block.hash('hex');
const entry = await chain.db.getEntry(hash);
const entry = await chain.getEntry(hash);
assert(entry);
assert.strictEqual(chain.tip.hash, entry.hash);
const result = await entry.isMainChain();
const result = await chain.isMainChain(entry);
assert(result);
});
@ -264,7 +264,7 @@ describe('Chain', function() {
});
it('should fail to connect coins on an alternate chain', async () => {
const block = await chain.db.getBlock(tip1.hash);
const block = await chain.getBlock(tip1.hash);
const cb = block.txs[0];
const mtx = new MTX();
@ -314,7 +314,7 @@ describe('Chain', function() {
const tx = block.txs[1];
const output = Coin.fromTX(tx, 2, chain.height);
const coin = await chain.db.getCoin(tx.hash('hex'), 2);
const coin = await chain.getCoin(tx.hash('hex'), 2);
assert.bufferEqual(coin.toRaw(), output.toRaw());
});
@ -347,7 +347,7 @@ describe('Chain', function() {
it('should rescan for transactions', async () => {
let total = 0;
await chain.db.scan(0, wallet.filter, async (block, txs) => {
await chain.scan(0, wallet.filter, async (block, txs) => {
total += txs.length;
});
@ -361,7 +361,7 @@ describe('Chain', function() {
assert.strictEqual(chain.height, 214);
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 1);
@ -370,19 +370,19 @@ describe('Chain', function() {
assert(await chain.add(block));
switch (chain.height) {
case 288: {
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 1);
break;
}
case 432: {
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 2);
break;
}
case 576: {
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 3);
break;
@ -402,13 +402,13 @@ describe('Chain', function() {
it('should have activated segwit', async () => {
const deployments = network.deployments;
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.segwit);
assert.strictEqual(state, 3);
});
it('should test csv', async () => {
const tx = (await chain.db.getBlock(chain.height - 100)).txs[0];
const tx = (await chain.getBlock(chain.height - 100)).txs[0];
const csvBlock = await mineCSV(tx);
assert(await chain.add(csvBlock));
@ -439,7 +439,7 @@ describe('Chain', function() {
});
it('should fail csv with bad sequence', async () => {
const csv = (await chain.db.getBlock(chain.height - 100)).txs[0];
const csv = (await chain.getBlock(chain.height - 100)).txs[0];
const spend = new MTX();
spend.addOutput({
@ -468,7 +468,7 @@ describe('Chain', function() {
});
it('should fail csv lock checks', async () => {
const tx = (await chain.db.getBlock(chain.height - 100)).txs[0];
const tx = (await chain.getBlock(chain.height - 100)).txs[0];
const csvBlock = await mineCSV(tx);
assert(await chain.add(csvBlock));
@ -506,7 +506,7 @@ describe('Chain', function() {
});
it('should fail to connect bad MTP', async () => {
const mtp = await chain.tip.getMedianTime();
const mtp = await chain.getMedianTime(chain.tip);
const job = await cpu.createJob();
job.attempt.time = mtp - 1;
assert.strictEqual(await mineBlock(job), 'time-too-old');
@ -613,7 +613,7 @@ describe('Chain', function() {
});
it('should mine a witness tx', async () => {
const prev = await chain.db.getBlock(chain.height - 2000);
const prev = await chain.getBlock(chain.height - 2000);
const cb = prev.txs[0];
const mtx = new MTX();
@ -637,7 +637,7 @@ describe('Chain', function() {
const job = await cpu.createJob();
for (let i = start; i <= end; i++) {
const block = await chain.db.getBlock(i);
const block = await chain.getBlock(i);
const cb = block.txs[0];
const mtx = new MTX();
@ -662,7 +662,7 @@ describe('Chain', function() {
const job = await cpu.createJob();
for (let i = start; i <= end; i++) {
const block = await chain.db.getBlock(i);
const block = await chain.getBlock(i);
const cb = block.txs[0];
const mtx = new MTX();
@ -687,7 +687,7 @@ describe('Chain', function() {
const job = await cpu.createJob();
for (let i = start; i <= end; i++) {
const block = await chain.db.getBlock(i);
const block = await chain.getBlock(i);
const cb = block.txs[0];
const mtx = new MTX();
@ -724,7 +724,7 @@ describe('Chain', function() {
it('should fail to connect premature cb spend', async () => {
const job = await cpu.createJob();
const block = await chain.db.getBlock(chain.height - 98);
const block = await chain.getBlock(chain.height - 98);
const cb = block.txs[0];
const mtx = new MTX();
@ -742,7 +742,7 @@ describe('Chain', function() {
it('should fail to connect vout belowout', async () => {
const job = await cpu.createJob();
const block = await chain.db.getBlock(chain.height - 99);
const block = await chain.getBlock(chain.height - 99);
const cb = block.txs[0];
const mtx = new MTX();
@ -760,7 +760,7 @@ describe('Chain', function() {
it('should fail to connect outtotal toolarge', async () => {
const job = await cpu.createJob();
const block = await chain.db.getBlock(chain.height - 99);
const block = await chain.getBlock(chain.height - 99);
const cb = block.txs[0];
const mtx = new MTX();
@ -839,7 +839,7 @@ describe('Chain', function() {
script.compile();
for (let i = start; i <= end; i++) {
const block = await chain.db.getBlock(i);
const block = await chain.getBlock(i);
const cb = block.txs[0];
if (cb.outputs.length === 2)

View File

@ -117,13 +117,13 @@ describe('Node', function() {
assert.strictEqual(chain.tip.hash, block1.hash('hex'));
tip1 = await chain.db.getEntry(block1.hash('hex'));
tip2 = await chain.db.getEntry(block2.hash('hex'));
tip1 = await chain.getEntry(block1.hash('hex'));
tip2 = await chain.getEntry(block2.hash('hex'));
assert(tip1);
assert(tip2);
assert(!await tip2.isMainChain());
assert(!await chain.isMainChain(tip2));
await co.wait();
}
@ -147,7 +147,7 @@ describe('Node', function() {
assert.strictEqual(wdb.state.height, chain.height);
assert.strictEqual(chain.height, 11);
const entry = await chain.db.getEntry(tip2.hash);
const entry = await chain.getEntry(tip2.hash);
assert(entry);
assert.strictEqual(chain.height, entry.height);
@ -181,7 +181,7 @@ describe('Node', function() {
});
it('should check main chain', async () => {
const result = await tip1.isMainChain();
const result = await chain.isMainChain(tip1);
assert(!result);
});
@ -190,11 +190,11 @@ describe('Node', function() {
await chain.add(block);
const entry = await chain.db.getEntry(block.hash('hex'));
const entry = await chain.getEntry(block.hash('hex'));
assert(entry);
assert.strictEqual(chain.tip.hash, entry.hash);
const result = await entry.isMainChain();
const result = await chain.isMainChain(entry);
assert(result);
});
@ -246,7 +246,7 @@ describe('Node', function() {
const tx = block2.txs[1];
const output = Coin.fromTX(tx, 1, chain.height);
const coin = await chain.db.getCoin(tx.hash('hex'), 1);
const coin = await chain.getCoin(tx.hash('hex'), 1);
assert.bufferEqual(coin.toRaw(), output.toRaw());
});
@ -288,7 +288,7 @@ describe('Node', function() {
it('should rescan for transactions', async () => {
let total = 0;
await chain.db.scan(0, wdb.filter, async (block, txs) => {
await chain.scan(0, wdb.filter, async (block, txs) => {
total += txs.length;
});
@ -298,7 +298,7 @@ describe('Node', function() {
it('should activate csv', async () => {
const deployments = chain.network.deployments;
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 0);
@ -307,19 +307,19 @@ describe('Node', function() {
await chain.add(block);
switch (chain.height) {
case 144: {
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 1);
break;
}
case 288: {
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 2);
break;
}
case 432: {
const prev = await chain.tip.getPrevious();
const prev = await chain.getPrevious(chain.tip);
const state = await chain.getState(prev, deployments.csv);
assert.strictEqual(state, 3);
break;
@ -337,7 +337,7 @@ describe('Node', function() {
});
it('should test csv', async () => {
const tx = (await chain.db.getBlock(chain.height)).txs[0];
const tx = (await chain.getBlock(chain.height)).txs[0];
const csvBlock = await mineCSV(tx);
await chain.add(csvBlock);
@ -368,7 +368,7 @@ describe('Node', function() {
});
it('should fail csv with bad sequence', async () => {
const csv = (await chain.db.getBlock(chain.height)).txs[1];
const csv = (await chain.getBlock(chain.height)).txs[1];
const spend = new MTX();
spend.addOutput({
@ -407,7 +407,7 @@ describe('Node', function() {
});
it('should fail csv lock checks', async () => {
const tx = (await chain.db.getBlock(chain.height)).txs[0];
const tx = (await chain.getBlock(chain.height)).txs[0];
const csvBlock = await mineCSV(tx);
await chain.add(csvBlock);