chain: alias all db methods. remove chainentry spaghetti code.
This commit is contained in:
parent
b81643473e
commit
5f82c0d8c2
@ -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
|
||||
*/
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user