add tx.isFinal. refactor chain usage.
This commit is contained in:
parent
c6c75d509b
commit
680bf01e98
@ -42,6 +42,8 @@ function Block(data, subtype) {
|
||||
this.network = data.network || false;
|
||||
this.relayedBy = data.relayedBy || '0.0.0.0';
|
||||
|
||||
this._chain = data.chain;
|
||||
|
||||
this.valid = null;
|
||||
this._hash = null;
|
||||
|
||||
@ -226,7 +228,8 @@ Block.prototype.getMerkleRoot = function getMerkleRoot() {
|
||||
// This mimics the behavior of CheckBlockHeader()
|
||||
// and CheckBlock() in bitcoin/src/main.cpp.
|
||||
Block.prototype._verify = function _verify() {
|
||||
var i, unique, hash, merkleRoot;
|
||||
var uniq = {};
|
||||
var i, tx, hash;
|
||||
|
||||
// Check proof of work matches claimed amount
|
||||
if (!utils.testTarget(this.bits, this.hash()))
|
||||
@ -251,55 +254,106 @@ Block.prototype._verify = function _verify() {
|
||||
}
|
||||
|
||||
// First TX must be a coinbase
|
||||
if (!this.txs.length
|
||||
|| this.txs[0].inputs.length !== 1
|
||||
|| +this.txs[0].inputs[0].out.hash !== 0)
|
||||
if (!this.txs.length || !this.txs[0].isCoinbase())
|
||||
return false;
|
||||
|
||||
// The rest of the txs must not be coinbases
|
||||
for (i = 1; i < this.txs.length; i++) {
|
||||
if (this.txs[i].inputs.length === 1
|
||||
&& +this.txs[i].inputs[0].out.hash === 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate tx ids
|
||||
unique = {};
|
||||
// Test all txs
|
||||
for (i = 0; i < this.txs.length; i++) {
|
||||
hash = this.txs[i].hash('hex');
|
||||
if (unique[hash])
|
||||
return false;
|
||||
unique[hash] = true;
|
||||
}
|
||||
tx = this.txs[i];
|
||||
|
||||
// Build MerkleTree
|
||||
merkleRoot = this.getMerkleRoot();
|
||||
// The rest of the txs must not be coinbases
|
||||
if (i > 0 && tx.isCoinbase())
|
||||
return false;
|
||||
|
||||
// Check for duplicate txids
|
||||
hash = tx.hash('hex');
|
||||
if (uniq[hash])
|
||||
return false;
|
||||
uniq[hash] = true;
|
||||
}
|
||||
|
||||
// Check merkle root
|
||||
if (merkleRoot !== this.merkleRoot)
|
||||
if (this.getMerkleRoot() !== this.merkleRoot)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Block.prototype.getHeight = function getHeight(chain) {
|
||||
chain = chain || bcoin.chain.global;
|
||||
Block.prototype.postVerify = function postVerify() {
|
||||
var prev, i, tx, cb;
|
||||
|
||||
if (!chain)
|
||||
return -1;
|
||||
if (this.subtype !== 'block')
|
||||
return true;
|
||||
|
||||
return chain.getHeight(this.hash('hex'));
|
||||
if (!this.chain)
|
||||
return true;
|
||||
|
||||
prev = this.chain.getBlock(this.prevBlock);
|
||||
|
||||
// Ensure it's not an orphan
|
||||
if (!prev)
|
||||
return false;
|
||||
|
||||
// Ensure the timestamp is correct
|
||||
if (this.ts <= prev.getMedianTime())
|
||||
return false;
|
||||
|
||||
// Test all txs
|
||||
for (i = 0; i < this.txs.length; i++) {
|
||||
tx = this.txs[i];
|
||||
|
||||
// TXs must be finalized with regards to seq and locktime
|
||||
if (!tx.isFinal(this, prev))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.bits !== this.chain.target(prev, this))
|
||||
return false;
|
||||
|
||||
if (this.version < 2 && prev.isOutdated(2))
|
||||
return false;
|
||||
|
||||
if (this.version < 3 && prev.isOutdated(3))
|
||||
return false;
|
||||
|
||||
if (this.version < 4 && prev.isOutdated(4))
|
||||
return false;
|
||||
|
||||
// Enforce height in coinbase
|
||||
if (this.version >= 2 && prev.needsUpgrade(2)) {
|
||||
cb = bcoin.script.isCoinbase(this.txs[0].inputs[0].script, this);
|
||||
|
||||
if (!cb)
|
||||
return false;
|
||||
|
||||
if (cb.height !== prev.height + 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
// sig validation (bip66)
|
||||
if (this.version >= 3 && prev.needsUpgrade(3))
|
||||
this.scriptFlags |= 1; // dersig
|
||||
|
||||
// checklocktimeverify (bip65)
|
||||
if (this.version >= 4 && prev.needsUpgrade(4))
|
||||
this.scriptFlags |= 2;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Block.prototype.getNextBlock = function getNextBlock(chain) {
|
||||
Block.prototype.getHeight = function getHeight() {
|
||||
if (!this.chain)
|
||||
return -1;
|
||||
return this.chain.getHeight(this.hash('hex'));
|
||||
};
|
||||
|
||||
Block.prototype.getNextBlock = function getNextBlock() {
|
||||
var next;
|
||||
|
||||
chain = chain || bcoin.chain.global;
|
||||
|
||||
if (!chain)
|
||||
if (!this.chain)
|
||||
return utils.toHex(constants.zeroHash);
|
||||
|
||||
next = chain.getNextBlock(this.hash('hex'));
|
||||
next = this.chain.getNextBlock(this.hash('hex'));
|
||||
|
||||
if (!next)
|
||||
return utils.toHex(constants.zeroHash);
|
||||
@ -361,26 +415,32 @@ Block.prototype.getReward = function getReward() {
|
||||
};
|
||||
};
|
||||
|
||||
Block.prototype.getEntry = function getEntry(chain) {
|
||||
chain = chain || bcoin.chain.global;
|
||||
return chain.getBlock(this);
|
||||
Block.prototype.getEntry = function getEntry() {
|
||||
if (!this.chain)
|
||||
return;
|
||||
return this.chain.getBlock(this);
|
||||
};
|
||||
|
||||
Block.prototype.isOrphan = function isOrphan(chain) {
|
||||
chain = chain || bcoin.chain.global;
|
||||
return chain.hasBlock(this.prevBlock);
|
||||
Block.prototype.isOrphan = function isOrphan() {
|
||||
if (!this.chain)
|
||||
return true;
|
||||
return this.chain.hasBlock(this.prevBlock);
|
||||
};
|
||||
|
||||
Block.prototype.__defineGetter__('chain', function() {
|
||||
return this._chain || bcoin.chain.global;
|
||||
});
|
||||
|
||||
Block.prototype.__defineGetter__('rhash', function() {
|
||||
return utils.revHex(this.hash('hex'));
|
||||
});
|
||||
|
||||
Block.prototype.__defineGetter__('height', function() {
|
||||
return this.getHeight(bcoin.chain.global);
|
||||
return this.getHeight();
|
||||
});
|
||||
|
||||
Block.prototype.__defineGetter__('nextBlock', function() {
|
||||
return this.getNextBlock(bcoin.chain.global);
|
||||
return this.getNextBlock();
|
||||
});
|
||||
|
||||
Block.prototype.__defineGetter__('reward', function() {
|
||||
@ -399,17 +459,18 @@ Block.prototype.__defineGetter__('coinbase', function() {
|
||||
});
|
||||
|
||||
Block.prototype.__defineGetter__('entry', function() {
|
||||
return this.getEntry(bcoin.chain.global);
|
||||
return this.getEntry();
|
||||
});
|
||||
|
||||
Block.prototype.__defineGetter__('orphan', function() {
|
||||
return this.isOrphan(bcoin.chain.global);
|
||||
return this.isOrphan();
|
||||
});
|
||||
|
||||
Block.prototype.inspect = function inspect() {
|
||||
var copy = bcoin.block(this, this.subtype);
|
||||
copy.__proto__ = null;
|
||||
delete copy._raw;
|
||||
delete copy._chain;
|
||||
copy.hash = this.hash('hex');
|
||||
copy.rhash = this.rhash;
|
||||
copy.height = this.height;
|
||||
|
||||
@ -149,7 +149,7 @@ Chain.prototype._addIndex = function _addIndex(entry) {
|
||||
return Chain.codes.unchanged;
|
||||
}
|
||||
|
||||
// Duplcate height
|
||||
// Duplicate height
|
||||
if (this.index.hashes[entry.height] === entry.hash)
|
||||
return Chain.codes.unchanged;
|
||||
|
||||
@ -294,8 +294,16 @@ Chain.prototype.add = function add(block, peer) {
|
||||
// If we have a block at the same height, use chain with higher work
|
||||
if (this.index.hashes[entry.height]) {
|
||||
if (this.tip.chainwork.cmp(entry.chainwork) < 0) {
|
||||
if (!block.postVerify()) {
|
||||
throw new Error;
|
||||
//code = Chain.codes.invalid;
|
||||
//break;
|
||||
}
|
||||
this.resetHeight(entry.height - 1);
|
||||
this._addIndex(entry);
|
||||
code = this._addIndex(entry);
|
||||
assert(code !== Chain.codes.unchanged);
|
||||
if (code !== Chain.codes.okay)
|
||||
break;
|
||||
code = Chain.codes.forked;
|
||||
// Breaking here only works because
|
||||
// we deleted the orphan map in resetHeight.
|
||||
@ -308,7 +316,15 @@ Chain.prototype.add = function add(block, peer) {
|
||||
}
|
||||
|
||||
// Validated known block at this point - add it to index
|
||||
if (!block.postVerify()) {
|
||||
throw new Error;
|
||||
//code = Chain.codes.invalid;
|
||||
//break;
|
||||
}
|
||||
code = this._addIndex(entry);
|
||||
assert(code !== Chain.codes.unchanged);
|
||||
if (code !== Chain.codes.okay)
|
||||
break;
|
||||
this.emit('block', block, peer);
|
||||
this.emit('entry', entry);
|
||||
if (block !== initial)
|
||||
@ -531,26 +547,24 @@ Chain.prototype.height = function height() {
|
||||
};
|
||||
|
||||
Chain.prototype.target = function target(last, block) {
|
||||
var proofOfWorkLimit = utils.toCompact(network.powLimit);
|
||||
var adjustmentInterval = network.powTargetTimespan / network.powTargetSpacing;
|
||||
var newBlockTs, heightFirst, first;
|
||||
|
||||
adjustmentInterval |= 0;
|
||||
var powLimit = utils.toCompact(network.powLimit);
|
||||
var interval = network.powTargetTimespan / network.powTargetSpacing | 0;
|
||||
var first, ts;
|
||||
|
||||
if (!last)
|
||||
last = this.getTip();
|
||||
|
||||
// Do not retarget
|
||||
if ((last.height + 1) % adjustmentInterval) {
|
||||
if ((last.height + 1) % interval) {
|
||||
if (network.powAllowMinDifficultyBlocks) {
|
||||
// Special behavior for testnet:
|
||||
newBlockTs = block ? block.ts : utils.now();
|
||||
if (newBlockTs > last.ts + network.powTargetSpacing * 2)
|
||||
return proofOfWorkLimit;
|
||||
ts = block ? (block.ts || block) : utils.now();
|
||||
if (ts > last.ts + network.powTargetSpacing * 2)
|
||||
return powLimit;
|
||||
|
||||
while (last.prev
|
||||
&& last.height % adjustmentInterval !== 0
|
||||
&& last.bits !== proofOfWorkLimit) {
|
||||
&& last.height % interval !== 0
|
||||
&& last.bits !== powLimit) {
|
||||
last = last.prev;
|
||||
}
|
||||
|
||||
@ -560,23 +574,22 @@ Chain.prototype.target = function target(last, block) {
|
||||
}
|
||||
|
||||
// Back 2 weeks
|
||||
heightFirst = last.height - (adjustmentInterval - 1);
|
||||
first = this.byHeight(heightFirst);
|
||||
first = this.byHeight(last.height - (interval - 1));
|
||||
|
||||
if (!first)
|
||||
return 0;
|
||||
|
||||
return this.retarget(last, first.ts);
|
||||
return this.retarget(last, first);
|
||||
};
|
||||
|
||||
Chain.prototype.retarget = function retarget(last, firstTs) {
|
||||
Chain.prototype.retarget = function retarget(last, first) {
|
||||
var powTargetTimespan = new bn(network.powTargetTimespan);
|
||||
var actualTimespan, powLimit, target;
|
||||
|
||||
if (network.powNoRetargeting)
|
||||
return last.bits;
|
||||
|
||||
actualTimespan = new bn(last.ts).subn(firstTs);
|
||||
actualTimespan = new bn(last.ts).subn(first.ts);
|
||||
if (actualTimespan.cmp(powTargetTimespan.divn(4)) < 0)
|
||||
actualTimespan = powTargetTimespan.divn(4);
|
||||
|
||||
@ -752,6 +765,45 @@ ChainBlock.prototype.getChainwork = function() {
|
||||
return (this.prev ? this.prev.chainwork : new bn(0)).add(this.proof);
|
||||
};
|
||||
|
||||
ChainBlock.prototype.getMedianTime = function() {
|
||||
var entry = this;
|
||||
var median = [];
|
||||
var timeSpan = constants.block.medianTimespan;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < timeSpan && entry; i++, entry = entry.prev)
|
||||
median.push(entry.ts);
|
||||
|
||||
median = median.sort();
|
||||
|
||||
return median[median.length / 2 | 0];
|
||||
};
|
||||
|
||||
ChainBlock.prototype.isOutdated = function(version) {
|
||||
return this.isSuperMajority(version,
|
||||
network.block.majorityRejectBlockOutdated);
|
||||
};
|
||||
|
||||
ChainBlock.prototype.needsUpgrade = function(version) {
|
||||
return this.isSuperMajority(version,
|
||||
network.block.majorityEnforceBlockUpgrade);
|
||||
};
|
||||
|
||||
ChainBlock.prototype.isSuperMajority = function(version, required) {
|
||||
var entry = this;
|
||||
var found = 0;
|
||||
var majorityWindow = network.block.majorityWindow;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < majorityWindow && found < required && entry; i++) {
|
||||
if (entry.version >= version)
|
||||
found++;
|
||||
entry = entry.prev;
|
||||
}
|
||||
|
||||
return found >= required;
|
||||
};
|
||||
|
||||
ChainBlock.prototype.toJSON = function() {
|
||||
// return [
|
||||
// this.hash,
|
||||
|
||||
@ -134,12 +134,17 @@ Miner.prototype.addTX = function addTX(tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pretty important
|
||||
// Ignore if it's already in a block
|
||||
if (tx.height !== -1)
|
||||
return;
|
||||
|
||||
if (!tx.verify())
|
||||
return;
|
||||
|
||||
// Ignore if it's already in a block
|
||||
if (tx.height !== -1)
|
||||
if (tx.isCoinbase())
|
||||
return;
|
||||
|
||||
if (!tx.isFinal(this.block, this.last))
|
||||
return;
|
||||
|
||||
// Deliver me from the block size debate, please
|
||||
@ -161,10 +166,12 @@ Miner.prototype.addTX = function addTX(tx) {
|
||||
};
|
||||
|
||||
Miner.prototype.createBlock = function createBlock(tx) {
|
||||
var target, coinbase, headers, block;
|
||||
var ts, target, coinbase, headers, block;
|
||||
|
||||
// Update target
|
||||
target = this.chain.target(this.last);
|
||||
ts = Math.max(utils.now(), this.last.ts + 1);
|
||||
|
||||
// Find target
|
||||
target = this.chain.target(this.last, ts);
|
||||
|
||||
// Create a coinbase
|
||||
coinbase = bcoin.tx();
|
||||
@ -194,7 +201,7 @@ Miner.prototype.createBlock = function createBlock(tx) {
|
||||
? this.last.hash('hex')
|
||||
: this.last.hash,
|
||||
merkleRoot: utils.toHex(constants.zeroHash.slice()),
|
||||
ts: utils.now(),
|
||||
ts: ts,
|
||||
bits: utils.toCompact(target),
|
||||
nonce: 0
|
||||
};
|
||||
|
||||
@ -149,8 +149,9 @@ Peer.prototype._init = function init() {
|
||||
|
||||
this._req('verack', function(err, payload) {
|
||||
if (err) {
|
||||
self._error(err);
|
||||
self.destroy();
|
||||
return self._error(err);
|
||||
return;
|
||||
}
|
||||
self.ack = true;
|
||||
self.emit('ack');
|
||||
@ -318,7 +319,7 @@ Peer.prototype._res = function _res(cmd, payload) {
|
||||
for (i = 0; i < this._request.queue.length; i++) {
|
||||
entry = this._request.queue[i];
|
||||
|
||||
if (!entry || entry.cmd && entry.cmd !== cmd)
|
||||
if (!entry || (entry.cmd && entry.cmd !== cmd))
|
||||
return false;
|
||||
|
||||
res = entry.cb(null, payload, cmd);
|
||||
|
||||
@ -43,7 +43,9 @@ function Pool(options) {
|
||||
this.size = options.size || 32;
|
||||
this.parallel = options.parallel || 2000;
|
||||
this.redundancy = options.redundancy || 2;
|
||||
this.seeds = network.seeds.slice();
|
||||
this.seeds = options.seeds
|
||||
? options.seeds.slice()
|
||||
: network.seeds.slice();
|
||||
|
||||
this._createConnection = options.createConnection;
|
||||
this._createSocket = options.createSocket;
|
||||
@ -280,6 +282,10 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
relay: this.options.relay
|
||||
});
|
||||
|
||||
peer.once('socket', function() {
|
||||
self.emit('debug', 'Added loader peer: %s', peer.host);
|
||||
});
|
||||
|
||||
this.peers.load = peer;
|
||||
this.peers.all.push(peer);
|
||||
|
||||
@ -494,8 +500,10 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
|
||||
return;
|
||||
|
||||
// Make sure the block is valid
|
||||
if (!block.verify())
|
||||
if (!block.verify()) {
|
||||
this.emit('debug', 'Block verification failed for %s', block.hash('hex'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve orphan chain
|
||||
if (!this.options.headers) {
|
||||
@ -768,8 +776,15 @@ Pool.prototype._removePeer = function _removePeer(peer) {
|
||||
if (i !== -1)
|
||||
this.peers.all.splice(i, 1);
|
||||
|
||||
if (this.peers.load === peer)
|
||||
if (this.peers.load === peer) {
|
||||
this.emit('debug', 'Removed loader peer (%s).', peer.host);
|
||||
this.peers.load = null;
|
||||
// i = this.seeds.indexOf(peer.host);
|
||||
// if (i === -1)
|
||||
// i = this.seeds.indexOf(peer.host + ':' + peer.port);
|
||||
// if (i !== -1)
|
||||
// this.seeds.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
Pool.prototype.watch = function watch(id) {
|
||||
@ -1407,9 +1422,15 @@ Pool.prototype.usableSeed = function usableSeed(addrs, force) {
|
||||
if (!addrs)
|
||||
addrs = this.seeds;
|
||||
|
||||
for (i = 0; i < addrs.length; i++) {
|
||||
if (!this.getPeer(addrs[i]))
|
||||
return addrs[i];
|
||||
addrs = addrs.slice().sort(function() {
|
||||
return Math.random() > 0.50 ? 1 : -1;
|
||||
});
|
||||
|
||||
if (this.peers.loader) {
|
||||
for (i = 0; i < addrs.length; i++) {
|
||||
if (!this.getPeer(addrs[i]))
|
||||
return addrs[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!force)
|
||||
|
||||
@ -155,7 +155,8 @@ exports.hashTypeByVal = Object.keys(exports.hashType).reduce(function(out, type)
|
||||
exports.block = {
|
||||
maxSize: 1000000,
|
||||
maxSigops: 1000000 / 50,
|
||||
maxOrphanTx: 1000000 / 100
|
||||
maxOrphanTx: 1000000 / 100,
|
||||
medianTimeSpan: 11
|
||||
};
|
||||
|
||||
exports.script = {
|
||||
|
||||
@ -140,6 +140,12 @@ main.powTargetSpacing = 10 * 60;
|
||||
main.powAllowMinDifficultyBlocks = false;
|
||||
main.powNoRetargeting = false;
|
||||
|
||||
main.block = {
|
||||
majorityEnforceBlockUpgrade: 750,
|
||||
majorityRejectBlockOutdated: 950,
|
||||
majorityWindow: 1000
|
||||
};
|
||||
|
||||
/**
|
||||
* Testnet (v3)
|
||||
* https://en.bitcoin.it/wiki/Testnet
|
||||
@ -237,3 +243,9 @@ testnet.powTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
||||
testnet.powTargetSpacing = 10 * 60;
|
||||
testnet.powAllowMinDifficultyBlocks = true;
|
||||
testnet.powNoRetargeting = false;
|
||||
|
||||
testnet.block = {
|
||||
majorityEnforceBlockUpgrade: 51,
|
||||
majorityRejectBlockOutdated: 75,
|
||||
majorityWindow: 100
|
||||
};
|
||||
|
||||
@ -37,6 +37,8 @@ function TX(data, block) {
|
||||
this.network = data.network || false;
|
||||
this.relayedBy = data.relayedBy || '0.0.0.0';
|
||||
|
||||
this._chain = data.chain;
|
||||
|
||||
this._lock = this.lock;
|
||||
|
||||
if (data.inputs) {
|
||||
@ -834,25 +836,66 @@ TX.prototype.funds = function funds(side) {
|
||||
return acc;
|
||||
};
|
||||
|
||||
TX.prototype.getHeight = function getHeight(chain) {
|
||||
chain = chain || bcoin.chain.global;
|
||||
|
||||
if (!chain)
|
||||
return -1;
|
||||
|
||||
return this.block ? chain.getHeight(this.block) : -1;
|
||||
// Used for postVerify/ContextualBlockCheck and miner isFinalTx call.
|
||||
// BIP113 will require that time-locked transactions have nLockTime set to
|
||||
// less than the median time of the previous block they're contained in.
|
||||
TX.prototype.isFinal = function isFinal(block, prev) {
|
||||
var height = prev.height + 1;
|
||||
var ts = this.locktimeMedian ? prev.getMedianTime() : block.ts;
|
||||
return this._isFinal(height, ts);
|
||||
};
|
||||
|
||||
TX.prototype.getConfirmations = function getConfirmations(chain) {
|
||||
// Used in AcceptToMemoryPool
|
||||
TX.prototype.isFinalAccept = function isFinalAccept() {
|
||||
var height = this.chain.height() + 1;
|
||||
var ts = this.lockTimeMedian
|
||||
? this.chain.getTip().getMedianTime()
|
||||
: utils.now();
|
||||
return this._isFinalTx(height, ts);
|
||||
};
|
||||
|
||||
// Used in the original bitcoind code for AcceptBlock
|
||||
TX.prototype._isFinalOriginal = function _isFinalOriginal(block) {
|
||||
var ts = block ? block.ts : utils.now();
|
||||
var height = this.chain.height();
|
||||
return this._isFinalTx(height, ts);
|
||||
};
|
||||
|
||||
TX.prototype._isFinal = function _isFinal(height, ts) {
|
||||
var threshold = constants.locktimeThreshold;
|
||||
var i;
|
||||
|
||||
if (!this.chain)
|
||||
return true;
|
||||
|
||||
if (this.lock === 0)
|
||||
return true;
|
||||
|
||||
if (this.lock < (this.lock < threshold ? height : ts))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
if (this.inputs[i].seq !== 0xffffffff)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
TX.prototype.getHeight = function getHeight() {
|
||||
if (!this.chain)
|
||||
return -1;
|
||||
return this.block ? this.chain.getHeight(this.block) : -1;
|
||||
};
|
||||
|
||||
TX.prototype.getConfirmations = function getConfirmations() {
|
||||
var top, height;
|
||||
|
||||
chain = chain || bcoin.chain.global;
|
||||
|
||||
if (!chain)
|
||||
if (!this.chain)
|
||||
return 0;
|
||||
|
||||
top = chain.height();
|
||||
height = this.getHeight(chain);
|
||||
top = this.chain.height();
|
||||
height = this.getHeight();
|
||||
|
||||
if (height === -1)
|
||||
return 0;
|
||||
@ -860,6 +903,10 @@ TX.prototype.getConfirmations = function getConfirmations(chain) {
|
||||
return top - height + 1;
|
||||
};
|
||||
|
||||
TX.prototype.__defineGetter__('chain', function() {
|
||||
return this._chain || bcoin.chain.global;
|
||||
});
|
||||
|
||||
TX.prototype.__defineGetter__('rblock', function() {
|
||||
return this.block
|
||||
? utils.revHex(this.block)
|
||||
@ -879,11 +926,11 @@ TX.prototype.__defineGetter__('value', function() {
|
||||
});
|
||||
|
||||
TX.prototype.__defineGetter__('height', function() {
|
||||
return this.getHeight(bcoin.chain.global);
|
||||
return this.getHeight();
|
||||
});
|
||||
|
||||
TX.prototype.__defineGetter__('confirmations', function() {
|
||||
return this.getConfirmations(bcoin.chain.global);
|
||||
return this.getConfirmations();
|
||||
});
|
||||
|
||||
TX.prototype.inspect = function inspect() {
|
||||
@ -892,6 +939,7 @@ TX.prototype.inspect = function inspect() {
|
||||
if (this.block)
|
||||
copy.block = this.block;
|
||||
delete copy._raw;
|
||||
delete copy._chain;
|
||||
copy.hash = this.hash('hex');
|
||||
copy.rhash = this.rhash;
|
||||
copy.rblock = this.rblock;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user