miner. network. serve blocks and mempool txs.
This commit is contained in:
parent
8124b4de44
commit
c5bcd44606
3
bin/node
3
bin/node
@ -9,6 +9,8 @@ var node = bcoin.fullnode({
|
||||
passphrase: 'node',
|
||||
prune: process.argv.indexOf('--prune') !== -1,
|
||||
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
|
||||
listen: process.argv.indexOf('--listen') !== -1,
|
||||
selfish: process.argv.indexOf('--selfish') !== -1,
|
||||
mine: process.argv.indexOf('--mine') !== -1
|
||||
});
|
||||
|
||||
@ -20,7 +22,6 @@ node.open(function(err) {
|
||||
if (err)
|
||||
throw err;
|
||||
|
||||
|
||||
if (node.options.mine)
|
||||
node.miner.start();
|
||||
else
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
var bcoin = require('../bcoin');
|
||||
var utils = require('./utils');
|
||||
var network = bcoin.protocol.network;
|
||||
var BufferWriter = require('./writer');
|
||||
|
||||
/**
|
||||
* AbstractBlock
|
||||
@ -24,7 +23,7 @@ function AbstractBlock(data) {
|
||||
this.ts = data.ts;
|
||||
this.bits = data.bits;
|
||||
this.nonce = data.nonce;
|
||||
this.totalTX = data.totalTX;
|
||||
this.totalTX = data.totalTX || 0;
|
||||
this.height = data.height != null ? data.height : -1;
|
||||
|
||||
this._raw = data._raw || null;
|
||||
@ -42,21 +41,10 @@ AbstractBlock.prototype.hash = function hash(enc) {
|
||||
};
|
||||
|
||||
AbstractBlock.prototype.abbr = function abbr() {
|
||||
var p;
|
||||
|
||||
if (this._raw)
|
||||
return this._raw.slice(0, 80);
|
||||
|
||||
p = new BufferWriter();
|
||||
|
||||
p.write32(this.version);
|
||||
p.writeHash(this.prevBlock);
|
||||
p.writeHash(this.merkleRoot);
|
||||
p.writeU32(this.ts);
|
||||
p.writeU32(this.bits);
|
||||
p.writeU32(this.nonce);
|
||||
|
||||
return p.render();
|
||||
return bcoin.protocol.framer.blockHeaders(this);
|
||||
};
|
||||
|
||||
AbstractBlock.prototype.getSize = function getSize() {
|
||||
|
||||
@ -99,6 +99,47 @@ Block.prototype.hasWitness = function hasWitness() {
|
||||
return false;
|
||||
};
|
||||
|
||||
Block.prototype.addTX = function addTX(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var index;
|
||||
|
||||
if (this.indexOf(hash) !== -1)
|
||||
return;
|
||||
|
||||
index = this.txs.push(tx) - 1;
|
||||
|
||||
tx.setBlock(this, index);
|
||||
};
|
||||
|
||||
Block.prototype.removeTX = function removeTX(hash) {
|
||||
var index = this.indexOf(hash);
|
||||
var tx;
|
||||
|
||||
if (index === -1)
|
||||
return;
|
||||
|
||||
tx = this.txs.splice(index, 1)[0];
|
||||
tx.unsetBlock();
|
||||
};
|
||||
|
||||
Block.prototype.hasTX = function hasTX(hash) {
|
||||
return this.indexOf(hash) !== -1;
|
||||
};
|
||||
|
||||
Block.prototype.indexOf = function indexOf(hash) {
|
||||
var i;
|
||||
|
||||
if (hash instanceof bcoin.tx)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
for (i = 0; i < this.txs.length; i++) {
|
||||
if (this.txs[i].hash('hex') === hash)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
Block.prototype.getSigops = function getSigops(scriptHash, accurate) {
|
||||
var total = 0;
|
||||
var i;
|
||||
|
||||
@ -12,7 +12,6 @@ var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var BufferReader = require('./reader');
|
||||
var VerifyError = utils.VerifyError;
|
||||
|
||||
/**
|
||||
@ -251,18 +250,9 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
utils.debug('Loading %s', url);
|
||||
|
||||
function parseHeader(buf) {
|
||||
var p = new BufferReader(buf);
|
||||
var hash = utils.dsha256(buf.slice(0, 80));
|
||||
|
||||
return {
|
||||
hash: utils.toHex(hash),
|
||||
version: p.readU32(), // Technically signed
|
||||
prevBlock: p.readHash('hex'),
|
||||
merkleRoot: p.readHash('hex'),
|
||||
ts: p.readU32(),
|
||||
bits: p.readU32(),
|
||||
nonce: p.readU32()
|
||||
};
|
||||
var headers = bcoin.protocol.parser.parseBlockHeaders(buf);
|
||||
headers.hash = utils.toHex(utils.dsha256(buf.slice(0, 80)));
|
||||
return headers;
|
||||
}
|
||||
|
||||
function save(entry) {
|
||||
@ -749,7 +739,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype.getHeight = function getHeight(hash) {
|
||||
Chain.prototype._getCachedHeight = function _getCachedHeight(hash) {
|
||||
if (Buffer.isBuffer(hash))
|
||||
hash = utils.toHex(hash);
|
||||
else if (hash.hash)
|
||||
@ -1654,61 +1644,23 @@ Chain.prototype.getCurrentTarget = function getCurrentTarget(callback) {
|
||||
return this.getTargetAsync(this.tip, null, callback);
|
||||
};
|
||||
|
||||
Chain.prototype.getTargetAsync = function getTarget(last, block, callback) {
|
||||
Chain.prototype.getTargetAsync = function getTargetAsync(last, block, callback) {
|
||||
var self = this;
|
||||
var powLimit = utils.toCompact(network.powLimit);
|
||||
var i = 0;
|
||||
var ts;
|
||||
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
// Genesis
|
||||
if (!last)
|
||||
return callback(null, powLimit);
|
||||
|
||||
// Do not retarget
|
||||
if ((last.height + 1) % network.powDiffInterval !== 0) {
|
||||
if (network.powAllowMinDifficultyBlocks) {
|
||||
// Special behavior for testnet:
|
||||
ts = block ? (block.ts || block) : utils.now();
|
||||
if (ts > last.ts + network.powTargetSpacing * 2)
|
||||
return callback(null, powLimit);
|
||||
|
||||
(function next(err, last) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
assert(last);
|
||||
|
||||
if (last.height > 0
|
||||
&& last.height % network.powDiffInterval !== 0
|
||||
&& last.bits === powLimit) {
|
||||
return last.getPrevious(next);
|
||||
}
|
||||
|
||||
return callback(null, last.bits);
|
||||
})(null, last);
|
||||
|
||||
return;
|
||||
}
|
||||
return callback(null, last.bits);
|
||||
if (!network.powAllowMinDifficultyBlocks)
|
||||
return utils.asyncify(callback)(null, this.getTarget(last, block));
|
||||
}
|
||||
|
||||
(function next(err, first) {
|
||||
return last.getAncestors(network.powDiffInterval, function(err, ancestors) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
i++;
|
||||
assert(first);
|
||||
|
||||
if (i >= network.powDiffInterval)
|
||||
return callback(null, self.retarget(last, first));
|
||||
|
||||
first.getPrevious(next);
|
||||
})(null, last);
|
||||
return callback(null, self.getTarget(last, block, ancestors));
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype.getTarget = function getTarget(last, block) {
|
||||
Chain.prototype.getTarget = function getTarget(last, block, ancestors) {
|
||||
var powLimit = utils.toCompact(network.powLimit);
|
||||
var ts, first, i, prev;
|
||||
|
||||
@ -1716,6 +1668,9 @@ Chain.prototype.getTarget = function getTarget(last, block) {
|
||||
if (!last)
|
||||
return powLimit;
|
||||
|
||||
if (!ancestors)
|
||||
ancestors = last.ancestors;
|
||||
|
||||
// Do not retarget
|
||||
if ((last.height + 1) % network.powDiffInterval !== 0) {
|
||||
if (network.powAllowMinDifficultyBlocks) {
|
||||
@ -1725,7 +1680,7 @@ Chain.prototype.getTarget = function getTarget(last, block) {
|
||||
return powLimit;
|
||||
|
||||
i = 1;
|
||||
prev = last.ancestors;
|
||||
prev = ancestors;
|
||||
while (prev[i]
|
||||
&& last.height % network.powDiffInterval !== 0
|
||||
&& last.bits === powLimit) {
|
||||
@ -1738,7 +1693,7 @@ Chain.prototype.getTarget = function getTarget(last, block) {
|
||||
}
|
||||
|
||||
// Back 2 weeks
|
||||
first = last.ancestors[network.powDiffInterval - 1];
|
||||
first = ancestors[network.powDiffInterval - 1];
|
||||
|
||||
assert(first);
|
||||
|
||||
@ -1770,6 +1725,25 @@ Chain.prototype.retarget = function retarget(last, first) {
|
||||
return utils.toCompact(target);
|
||||
};
|
||||
|
||||
Chain.prototype.findLocator = function findLocator(locator, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!locator)
|
||||
return utils.nextTick(callback);
|
||||
|
||||
utils.forEachSerial(locator, function(hash, next) {
|
||||
self.db.has(hash, function(err, result) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (result)
|
||||
return callback(null, hash);
|
||||
|
||||
next();
|
||||
});
|
||||
}, callback);
|
||||
};
|
||||
|
||||
// https://github.com/bitcoin/bitcoin/pull/7648/files
|
||||
Chain.prototype.getState = function getState(prev, id, callback) {
|
||||
var self = this;
|
||||
|
||||
@ -55,6 +55,8 @@ Fullnode.prototype._init = function _init() {
|
||||
// Pool needs access to the chain.
|
||||
this.pool = new bcoin.pool(this, {
|
||||
witness: this.network.witness,
|
||||
listen: this.options.listen,
|
||||
selfish: this.options.selfish,
|
||||
spv: false
|
||||
});
|
||||
|
||||
|
||||
@ -171,7 +171,7 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
||||
return self.removeOrphan(hash, next);
|
||||
|
||||
copy = tx.clone();
|
||||
copy.ts = existing.ps;
|
||||
copy.ts = existing.ts;
|
||||
copy.block = existing.block;
|
||||
copy.height = existing.height;
|
||||
copy.ps = existing.ps;
|
||||
|
||||
@ -62,6 +62,9 @@ MerkleBlock.prototype.getRaw = function getRaw() {
|
||||
};
|
||||
|
||||
MerkleBlock.prototype.hasTX = function hasTX(hash) {
|
||||
if (hash instanceof bcoin.tx)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
return this.txMap[hash] === true;
|
||||
};
|
||||
|
||||
|
||||
@ -41,8 +41,6 @@ function Miner(node, options) {
|
||||
this.running = false;
|
||||
this.timeout = null;
|
||||
|
||||
this.fee = new bn(0);
|
||||
this.last = this.node.chain.tip;
|
||||
this.block = null;
|
||||
this.iterations = 0;
|
||||
this._begin = utils.now();
|
||||
@ -81,7 +79,6 @@ Miner.prototype._init = function _init() {
|
||||
this.chain.on('tip', function(tip) {
|
||||
if (!self.running)
|
||||
return;
|
||||
self.last = tip;
|
||||
self.start();
|
||||
});
|
||||
|
||||
@ -110,10 +107,8 @@ Miner.prototype.start = function start() {
|
||||
var self = this;
|
||||
|
||||
// Wait for `tip`.
|
||||
this.last = this.last || this.chain.tip;
|
||||
if (!this.last) {
|
||||
if (!this.chain.tip) {
|
||||
this.chain.on('tip', function(tip) {
|
||||
self.last = tip;
|
||||
self.start();
|
||||
});
|
||||
return;
|
||||
@ -180,18 +175,14 @@ Miner.prototype.addTX = function addTX(tx) {
|
||||
if (size > constants.blocks.maxSize)
|
||||
return false;
|
||||
|
||||
if (this.block._txMap[tx.hash('hex')])
|
||||
if (this.block.hasTX(tx))
|
||||
return false;
|
||||
|
||||
if (!this.block.witness && tx.hasWitness())
|
||||
return false;
|
||||
|
||||
// Add the tx to our block
|
||||
this.block.txs.push(tx);
|
||||
this.block._txMap[tx.hash('hex')] = true;
|
||||
|
||||
// Calculate our new reward fee
|
||||
this.fee.iadd(tx.getFee());
|
||||
this.block.addTX(tx);
|
||||
|
||||
// Update coinbase value
|
||||
this.updateCoinbase();
|
||||
@ -204,25 +195,18 @@ Miner.prototype.addTX = function addTX(tx) {
|
||||
|
||||
Miner.prototype.createBlock = function createBlock(callback) {
|
||||
var self = this;
|
||||
var ts, target, coinbase, headers, block;
|
||||
var ts = Math.max(utils.now(), this.chain.tip.ts + 1);
|
||||
var coinbase, headers, block;
|
||||
|
||||
// Find target
|
||||
this.last.ensureAncestors(function(err) {
|
||||
if (err) {
|
||||
self.last.free();
|
||||
this.chain.getTargetAsync(this.chain.tip, ts, function(err, target) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Calculate version with versionbits
|
||||
self.chain.computeBlockVersion(self.last, function(err, version) {
|
||||
if (err) {
|
||||
self.last.free();
|
||||
self.chain.computeBlockVersion(self.chain.tip, function(err, version) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
ts = Math.max(utils.now(), self.last.ts + 1);
|
||||
|
||||
target = self.chain.getTarget(self.last, ts);
|
||||
|
||||
// Create a coinbase
|
||||
coinbase = bcoin.mtx();
|
||||
@ -235,7 +219,7 @@ Miner.prototype.createBlock = function createBlock(callback) {
|
||||
coin: null,
|
||||
script: new bcoin.script([
|
||||
// Height (required in v2+ blocks)
|
||||
bcoin.script.array(self.last.height + 1),
|
||||
bcoin.script.array(self.chain.height + 1),
|
||||
// extraNonce - incremented when
|
||||
// the nonce overflows.
|
||||
bcoin.script.array(0),
|
||||
@ -261,20 +245,20 @@ Miner.prototype.createBlock = function createBlock(callback) {
|
||||
// Create our block
|
||||
headers = {
|
||||
version: version,
|
||||
prevBlock: self.last.hash,
|
||||
prevBlock: self.chain.tip.hash,
|
||||
merkleRoot: utils.toHex(constants.zeroHash),
|
||||
ts: ts,
|
||||
bits: target,
|
||||
nonce: 0
|
||||
};
|
||||
|
||||
block = bcoin.block(headers, 'block');
|
||||
block._txMap = {};
|
||||
block = bcoin.block(headers);
|
||||
|
||||
block.txs.push(coinbase);
|
||||
block.addTX(coinbase);
|
||||
|
||||
block.target = utils.fromCompact(target);
|
||||
block.extraNonce = new bn(0, 'le');
|
||||
block.height = self.chain.height + 1;
|
||||
block.target = utils.fromCompact(target).toBuffer('le', 32);
|
||||
block.extraNonce = new bn(0);
|
||||
|
||||
if (self.chain.segwitActive) {
|
||||
// Set up the witness nonce and
|
||||
@ -294,8 +278,6 @@ Miner.prototype.createBlock = function createBlock(callback) {
|
||||
// Create our merkle root.
|
||||
self.updateMerkle(block);
|
||||
|
||||
self.last.free();
|
||||
|
||||
return callback(null, block);
|
||||
});
|
||||
});
|
||||
@ -310,29 +292,20 @@ Miner.prototype.updateCommitment = function updateCommitment(block) {
|
||||
if (!block)
|
||||
block = this.block;
|
||||
|
||||
delete block._txMap[coinbase.hash('hex')];
|
||||
|
||||
hash = block.getCommitmentHash();
|
||||
coinbase.outputs[1].script = bcoin.script.createCommitment(hash);
|
||||
|
||||
block._txMap[coinbase.hash('hex')] = true;
|
||||
};
|
||||
|
||||
Miner.prototype.updateCoinbase = function updateCoinbase(block) {
|
||||
var coinbase = block.txs[0];
|
||||
var reward = bcoin.block.reward(this.last.height + 1);
|
||||
|
||||
assert(coinbase);
|
||||
|
||||
if (!block)
|
||||
block = this.block;
|
||||
|
||||
delete block._txMap[coinbase.hash('hex')];
|
||||
|
||||
coinbase.inputs[0].script[1] = block.extraNonce.toBuffer();
|
||||
coinbase.outputs[0].value = reward.add(this.fee);
|
||||
|
||||
block._txMap[coinbase.hash('hex')] = true;
|
||||
coinbase.outputs[0].value = block.getReward();
|
||||
};
|
||||
|
||||
Miner.prototype.updateMerkle = function updateMerkle(block) {
|
||||
@ -344,7 +317,7 @@ Miner.prototype.updateMerkle = function updateMerkle(block) {
|
||||
if (block.witness)
|
||||
this.updateCommitment(block);
|
||||
|
||||
block.ts = utils.now();
|
||||
block.ts = Math.max(utils.now(), this.chain.tip.ts + 1);
|
||||
block.merkleRoot = block.getMerkleRoot('hex');
|
||||
};
|
||||
|
||||
@ -360,7 +333,7 @@ Miner.prototype.iterate = function iterate() {
|
||||
self.chain.add(self.block, function(err) {
|
||||
if (err) {
|
||||
if (err.type === 'VerifyError')
|
||||
utils.debug('Miner: %s could not be added to chain.', self.block.rhash);
|
||||
utils.debug('%s could not be added to chain.', self.block.rhash);
|
||||
self.emit('error', err);
|
||||
return self.start();
|
||||
}
|
||||
@ -391,25 +364,24 @@ Miner.prototype.sendStatus = function sendStatus() {
|
||||
target: this.block.bits,
|
||||
hashes: this.hashes.toString(10),
|
||||
hashrate: this.rate,
|
||||
height: this.last.height + 1,
|
||||
best: utils.revHex(this.last.hash)
|
||||
height: this.chain.height + 1,
|
||||
best: utils.revHex(this.chain.tip.hash)
|
||||
});
|
||||
};
|
||||
|
||||
Miner.prototype.findNonce = function findNonce() {
|
||||
var data = this.block.abbr();
|
||||
var target = this.block.target.toBuffer('le', 32);
|
||||
var now;
|
||||
|
||||
// Track how long we've been at it.
|
||||
this._begin = utils.now();
|
||||
|
||||
// assert(this.block.ts > this.last.ts);
|
||||
assert(this.block.ts > this.chain.tip.ts);
|
||||
|
||||
// The heart and soul of the miner: match the target.
|
||||
while (this.block.nonce <= 0xffffffff) {
|
||||
// Hash and test against the next target
|
||||
if (utils.rcmp(this.dsha256(data), target) < 0)
|
||||
if (rcmp(this.dsha256(data), this.block.target) < 0)
|
||||
return true;
|
||||
|
||||
// Increment the nonce to get a different hash
|
||||
@ -436,7 +408,7 @@ Miner.prototype.findNonce = function findNonce() {
|
||||
// performance because we do not have to
|
||||
// recalculate the merkle root.
|
||||
now = utils.now();
|
||||
if (now > this.block.ts && now > this.last.ts) {
|
||||
if (now > this.block.ts && now > this.chain.tip.ts) {
|
||||
this.block.ts = now;
|
||||
// Overflow the nonce
|
||||
this.block.nonce = 0;
|
||||
@ -456,6 +428,21 @@ Miner.prototype.findNonce = function findNonce() {
|
||||
return false;
|
||||
};
|
||||
|
||||
function rcmp(a, b) {
|
||||
var i;
|
||||
|
||||
assert(a.length === b.length);
|
||||
|
||||
for (i = a.length - 1; i >= 0; i--) {
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -36,6 +36,7 @@ function Peer(pool, options) {
|
||||
this.parser = new bcoin.protocol.parser();
|
||||
this.framer = new bcoin.protocol.framer();
|
||||
this.chain = this.pool.chain;
|
||||
this.mempool = this.pool.mempool;
|
||||
this.bloom = this.pool.bloom;
|
||||
this.version = null;
|
||||
this.destroyed = false;
|
||||
@ -44,6 +45,7 @@ function Peer(pool, options) {
|
||||
this.ts = this.options.ts || 0;
|
||||
this.sendHeaders = false;
|
||||
this.haveWitness = false;
|
||||
this.hashContinue = null;
|
||||
|
||||
this.challenge = null;
|
||||
this.lastPong = 0;
|
||||
@ -140,7 +142,7 @@ Peer.prototype._init = function init() {
|
||||
|
||||
this.parser.on('error', function(err) {
|
||||
utils.debug(err.stack + '');
|
||||
peer.sendReject(null, 'malformed', 'error parsing message', 100);
|
||||
self.sendReject(null, 'malformed', 'error parsing message', 100);
|
||||
self._error(err);
|
||||
// Something is wrong here.
|
||||
// Ignore this peer.
|
||||
@ -150,11 +152,6 @@ Peer.prototype._init = function init() {
|
||||
this.challenge = utils.nonce();
|
||||
|
||||
this._ping.timer = setInterval(function() {
|
||||
if (self.options.witness && !self.haveWitness) {
|
||||
self._error('Peer does not support segregated witness.');
|
||||
self.setMisbehavior(100);
|
||||
return;
|
||||
}
|
||||
self._write(self.framer.ping({
|
||||
nonce: self.challenge
|
||||
}));
|
||||
@ -171,25 +168,25 @@ Peer.prototype._init = function init() {
|
||||
self.emit('ack');
|
||||
self.ts = utils.now();
|
||||
|
||||
self._write(self.framer.packet('getaddr'));
|
||||
self._write(self.framer.getAddr());
|
||||
|
||||
if (self.options.headers) {
|
||||
if (self.version && self.version.version > 70012)
|
||||
self._write(self.framer.packet('sendheaders'));
|
||||
self._write(self.framer.sendHeaders());
|
||||
}
|
||||
|
||||
if (self.options.witness) {
|
||||
if (self.version && self.version.version >= 70012)
|
||||
self._write(self.framer.packet('havewitness'));
|
||||
self._write(self.framer.haveWitness());
|
||||
}
|
||||
|
||||
if (self.pool.chain.isFull())
|
||||
if (self.chain.isFull())
|
||||
self.getMempool();
|
||||
});
|
||||
|
||||
// Send hello
|
||||
this._write(this.framer.version({
|
||||
height: this.pool.chain.height,
|
||||
height: this.chain.height,
|
||||
relay: this.options.relay
|
||||
}));
|
||||
};
|
||||
@ -286,7 +283,8 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
packetType: packetType,
|
||||
type: type,
|
||||
hash: item.hash(),
|
||||
msg: item
|
||||
value: item.renderNormal(),
|
||||
witnessValue: item.renderWitness()
|
||||
};
|
||||
|
||||
this._broadcast.map[key] = entry;
|
||||
@ -358,11 +356,12 @@ Peer.prototype._error = function error(err) {
|
||||
|
||||
Peer.prototype._req = function _req(cmd, cb) {
|
||||
var self = this;
|
||||
var entry;
|
||||
|
||||
if (this.destroyed)
|
||||
return cb(new Error('Destroyed, sorry'));
|
||||
return utils.asyncify(cb)(new Error('Destroyed, sorry'));
|
||||
|
||||
var entry = {
|
||||
entry = {
|
||||
cmd: cmd,
|
||||
cb: cb,
|
||||
ontimeout: function() {
|
||||
@ -420,78 +419,284 @@ Peer.prototype._onPacket = function onPacket(packet) {
|
||||
var payload = packet.payload;
|
||||
|
||||
if (this.lastBlock && cmd !== 'tx')
|
||||
this._emitMerkle(this.lastBlock);
|
||||
this._emitMerkle();
|
||||
|
||||
if (cmd === 'version')
|
||||
return this._handleVersion(payload);
|
||||
|
||||
if (cmd === 'inv')
|
||||
return this._handleInv(payload);
|
||||
|
||||
if (cmd === 'headers')
|
||||
return this._handleHeaders(payload);
|
||||
|
||||
if (cmd === 'getdata')
|
||||
return this._handleGetData(payload);
|
||||
|
||||
if (cmd === 'addr')
|
||||
return this._handleAddr(payload);
|
||||
|
||||
if (cmd === 'ping')
|
||||
return this._handlePing(payload);
|
||||
|
||||
if (cmd === 'pong')
|
||||
return this._handlePong(payload);
|
||||
|
||||
if (cmd === 'getaddr')
|
||||
return this._handleGetAddr();
|
||||
|
||||
if (cmd === 'reject')
|
||||
return this._handleReject(payload);
|
||||
|
||||
if (cmd === 'alert')
|
||||
return this._handleAlert(payload);
|
||||
|
||||
if (cmd === 'block') {
|
||||
payload = bcoin.compactblock(payload);
|
||||
} else if (cmd === 'merkleblock') {
|
||||
payload = bcoin.merkleblock(payload);
|
||||
this.lastBlock = payload;
|
||||
return;
|
||||
} else if (cmd === 'tx') {
|
||||
payload = bcoin.tx(payload, this.lastBlock);
|
||||
if (this.lastBlock) {
|
||||
if (payload.block) {
|
||||
this.lastBlock.txs.push(payload);
|
||||
return;
|
||||
} else {
|
||||
this._emitMerkle(this.lastBlock);
|
||||
switch (cmd) {
|
||||
case 'version':
|
||||
return this._handleVersion(payload);
|
||||
case 'inv':
|
||||
return this._handleInv(payload);
|
||||
case 'headers':
|
||||
return this._handleHeaders(payload);
|
||||
case 'getdata':
|
||||
return this._handleGetData(payload);
|
||||
case 'addr':
|
||||
return this._handleAddr(payload);
|
||||
case 'ping':
|
||||
return this._handlePing(payload);
|
||||
case 'pong':
|
||||
return this._handlePong(payload);
|
||||
case 'getaddr':
|
||||
return this._handleGetAddr(payload);
|
||||
case 'reject':
|
||||
return this._handleReject(payload);
|
||||
case 'alert':
|
||||
return this._handleAlert(payload);
|
||||
case 'getutxos':
|
||||
return this._handleGetUTXOs(payload);
|
||||
case 'utxos':
|
||||
return this._handleUTXOs(payload);
|
||||
case 'getblocks':
|
||||
return this._handleGetBlocks(payload);
|
||||
case 'getheaders':
|
||||
return this._handleGetHeaders(payload);
|
||||
case 'mempool':
|
||||
return this._handleMempool(payload);
|
||||
case 'block':
|
||||
payload = bcoin.compactblock(payload);
|
||||
this._emit(cmd, payload);
|
||||
break;
|
||||
case 'merkleblock':
|
||||
payload = bcoin.merkleblock(payload);
|
||||
this.lastBlock = payload;
|
||||
break;
|
||||
case 'tx':
|
||||
payload = bcoin.tx(payload, this.lastBlock);
|
||||
if (this.lastBlock) {
|
||||
if (payload.block) {
|
||||
this.lastBlock.txs.push(payload);
|
||||
break;
|
||||
}
|
||||
this._emitMerkle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd === 'sendheaders') {
|
||||
this.sendHeaders = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd === 'havewitness') {
|
||||
this.haveWitness = true;
|
||||
return;
|
||||
this._emit(cmd, payload);
|
||||
break;
|
||||
case 'sendheaders':
|
||||
this.sendHeaders = true;
|
||||
this._res(cmd, payload);
|
||||
break;
|
||||
case 'havewitness':
|
||||
this.haveWitness = true;
|
||||
this._res(cmd, payload);
|
||||
break;
|
||||
case 'verack':
|
||||
this._emit(cmd, payload);
|
||||
break;
|
||||
default:
|
||||
utils.debug('Unknown packet: %s', cmd);
|
||||
this._emit(cmd, payload);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Peer.prototype._emit = function _emit(cmd, payload) {
|
||||
if (this._res(cmd, payload))
|
||||
return;
|
||||
|
||||
this.emit(cmd, payload);
|
||||
};
|
||||
|
||||
Peer.prototype._emitMerkle = function _emitMerkle(payload) {
|
||||
if (!this._res('merkleblock', payload))
|
||||
this.emit('merkleblock', payload);
|
||||
Peer.prototype._emitMerkle = function _emitMerkle() {
|
||||
if (this.lastBlock)
|
||||
this._emit('merkleblock', this.lastBlock);
|
||||
this.lastBlock = null;
|
||||
};
|
||||
|
||||
Peer.prototype._handleUTXOs = function _handleUTXOs(payload) {
|
||||
payload.coins = payload.coins(function(coin) {
|
||||
return new bcoin.coin(coin);
|
||||
});
|
||||
utils.debug('Received %d utxos from %s.', payload.coins.length, this.host);
|
||||
this.emit('utxos', payload);
|
||||
};
|
||||
|
||||
Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) {
|
||||
var self = this;
|
||||
var coins = [];
|
||||
var notfound = [];
|
||||
|
||||
function checkMempool(hash, index, callback) {
|
||||
if (!self.mempool)
|
||||
return callback();
|
||||
|
||||
if (!payload.mempool)
|
||||
return callback();
|
||||
|
||||
self.mempool.getCoin(hash, index, callback);
|
||||
}
|
||||
|
||||
function isSpent(hash, index, callback) {
|
||||
if (!self.mempool)
|
||||
return callback(null, false);
|
||||
|
||||
if (!payload.mempool)
|
||||
return callback(null, false);
|
||||
|
||||
self.mempool.isSpent(hash, index, callback);
|
||||
}
|
||||
|
||||
utils.forEachSerial(payload.prevout, function(prevout, next, i) {
|
||||
var hash = prevout.hash;
|
||||
var index = prevout.index;
|
||||
|
||||
checkMempool(hash, index, function(err, coin) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (coin) {
|
||||
coins.push(coin);
|
||||
return next();
|
||||
}
|
||||
|
||||
isSpent(hash, index, function(err, result) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (result) {
|
||||
notfound.push(i);
|
||||
return next();
|
||||
}
|
||||
|
||||
self.chain.db.getCoin(hash, index, function(err, coin) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!coin) {
|
||||
notfound.push(i);
|
||||
return next();
|
||||
}
|
||||
|
||||
coins.push(coin);
|
||||
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
|
||||
self._write(self.framer.UTXOs({
|
||||
height: self.chain.height,
|
||||
tip: self.chain.tip.hash,
|
||||
notfound: notfound,
|
||||
coins: coins
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
Peer.prototype._handleGetHeaders = function _handleGetHeaders(payload) {
|
||||
var self = this;
|
||||
var headers = [];
|
||||
|
||||
if (this.pool.options.selfish)
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return;
|
||||
|
||||
function collect(err, hash) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
if (!hash)
|
||||
return done();
|
||||
|
||||
self.chain.db.get(hash, function(err, entry) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
if (!entry)
|
||||
return done();
|
||||
|
||||
(function next(err, entry) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
if (!entry)
|
||||
return done();
|
||||
|
||||
headers.push(new bcoin.headers(entry));
|
||||
|
||||
if (headers.length === 2000)
|
||||
return done();
|
||||
|
||||
if (entry.hash === payload.stop)
|
||||
return done();
|
||||
|
||||
entry.getNext(next);
|
||||
})(null, entry);
|
||||
});
|
||||
}
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
self._write(self.framer.headers(headers));
|
||||
}
|
||||
|
||||
if (!payload.locator)
|
||||
return collect(null, payload.stop);
|
||||
|
||||
this.chain.findLocator(payload.locator, function(err, hash) {
|
||||
if (err)
|
||||
return collect(err);
|
||||
|
||||
if (!hash)
|
||||
return collect();
|
||||
|
||||
self.chain.db.getNextHash(hash, collect);
|
||||
});
|
||||
};
|
||||
|
||||
Peer.prototype._handleGetBlocks = function _handleGetBlocks(payload) {
|
||||
var self = this;
|
||||
var blocks = [];
|
||||
|
||||
if (this.pool.options.selfish)
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return;
|
||||
|
||||
function done(err) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
self._write(self.framer.inv(blocks));
|
||||
}
|
||||
|
||||
this.chain.findLocator(payload.locator, function(err, tip) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
if (!tip)
|
||||
return done();
|
||||
|
||||
(function next(hash) {
|
||||
self.chain.db.getNextHash(hash, function(err, hash) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
if (!hash)
|
||||
return done();
|
||||
|
||||
blocks.push({ type: constants.inv.block, hash: hash });
|
||||
|
||||
if (hash === payload.stop)
|
||||
return done();
|
||||
|
||||
if (blocks.length === 500) {
|
||||
self.hashContinue = hash;
|
||||
return done();
|
||||
}
|
||||
|
||||
next(hash);
|
||||
});
|
||||
})(tip);
|
||||
});
|
||||
};
|
||||
|
||||
Peer.prototype._handleVersion = function handleVersion(payload) {
|
||||
if (payload.version < constants.minVersion) {
|
||||
this._error('Peer doesn\'t support required protocol version.');
|
||||
@ -523,13 +728,17 @@ Peer.prototype._handleVersion = function handleVersion(payload) {
|
||||
}
|
||||
}
|
||||
|
||||
// if (this.options.witness) {
|
||||
// if (!payload.witness) {
|
||||
// this._error('Peer does not support segregated witness service.');
|
||||
// this.setMisbehavior(100);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
if (this.options.witness) {
|
||||
if (!payload.witness) {
|
||||
this._req('havewitness', function(err, payload) {
|
||||
if (err) {
|
||||
self._error('Peer does not support segregated witness.');
|
||||
self.setMisbehavior(100);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.witness)
|
||||
this.haveWitness = true;
|
||||
@ -540,32 +749,57 @@ Peer.prototype._handleVersion = function handleVersion(payload) {
|
||||
this.emit('version', payload);
|
||||
};
|
||||
|
||||
Peer.prototype._handleGetData = function handleGetData(items) {
|
||||
items.forEach(function(item) {
|
||||
// Filter out not broadcasted things
|
||||
var hash = utils.toHex(item.hash);
|
||||
var entry = this._broadcast.map[hash];
|
||||
var isWitness = item.type & constants.invWitnessMask;
|
||||
var value;
|
||||
Peer.prototype._handleMempool = function _handleMempool() {
|
||||
var self = this;
|
||||
var items = [];
|
||||
var i;
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
if (!this.mempool)
|
||||
return;
|
||||
|
||||
if (this.pool.options.selfish)
|
||||
return;
|
||||
|
||||
this.mempool.getSnapshot(function(err, hashes) {
|
||||
if (err)
|
||||
return self.emit('error', err);
|
||||
|
||||
for (i = 0; i < hashes.length; i++)
|
||||
items.push({ type: constants.inv.tx, hash: hashes[i] });
|
||||
|
||||
utils.debug('Sending mempool snapshot to %s.', self.host);
|
||||
|
||||
self._write(self.framer.inv(items));
|
||||
});
|
||||
};
|
||||
|
||||
Peer.prototype._handleGetData = function handleGetData(items) {
|
||||
var self = this;
|
||||
var check = [];
|
||||
var notfound = [];
|
||||
var hash, entry, isWitness, value, i, item;
|
||||
|
||||
if (items.length > 50000)
|
||||
return this._error('message getdata size() = %d', items.length);
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
|
||||
hash = utils.toHex(item.hash);
|
||||
entry = this._broadcast.map[hash];
|
||||
isWitness = item.type & constants.invWitnessMask;
|
||||
value = null;
|
||||
|
||||
if (!entry) {
|
||||
check.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((item.type & ~constants.invWitnessMask) !== entry.type) {
|
||||
utils.debug(
|
||||
'Peer %s requested an existing item with the wrong type.',
|
||||
this.host);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWitness) {
|
||||
if (!entry.witnessValue)
|
||||
entry.witnessValue = entry.msg.renderWitness();
|
||||
value = entry.witnessValue;
|
||||
} else {
|
||||
if (!entry.value)
|
||||
entry.value = entry.msg.renderNormal();
|
||||
value = entry.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
utils.debug(
|
||||
@ -575,21 +809,107 @@ Peer.prototype._handleGetData = function handleGetData(items) {
|
||||
utils.revHex(utils.toHex(entry.hash)),
|
||||
isWitness ? 'witness' : 'normal');
|
||||
|
||||
if (entry.value && entry.witnessValue)
|
||||
delete entry.msg;
|
||||
|
||||
this._write(this.framer.packet(entry.packetType, value));
|
||||
if (isWitness)
|
||||
this._write(this.framer.packet(entry.packetType, entry.witnessValue));
|
||||
else
|
||||
this._write(this.framer.packet(entry.packetType, entry.value));
|
||||
|
||||
entry.e.emit('request');
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (this.pool.options.selfish)
|
||||
return;
|
||||
|
||||
utils.forEachSerial(check, function(item, next) {
|
||||
var isWitness = item.type & constants.invWitnessMask;
|
||||
var type = item.type & ~constants.invWitnessMask;
|
||||
var hash = utils.toHex(item.hash);
|
||||
var data;
|
||||
|
||||
if (type === constants.inv.tx) {
|
||||
if (!self.mempool) {
|
||||
notfound.push({ type: type, hash: hash });
|
||||
return next();
|
||||
}
|
||||
return self.mempool.getTX(hash, function(err, tx) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!tx) {
|
||||
notfound.push({ type: type, hash: hash });
|
||||
return next();
|
||||
}
|
||||
|
||||
if (isWitness)
|
||||
data = tx.renderWitness();
|
||||
else
|
||||
data = tx.renderNormal();
|
||||
|
||||
self._write(self.framer.packet('tx', data));
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
if (type === constants.inv.block) {
|
||||
if (self.chain.db.options.spv) {
|
||||
notfound.push({ type: type, hash: hash });
|
||||
return next();
|
||||
}
|
||||
return self.chain.db.getBlock(hash, function(err, block) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!block) {
|
||||
notfound.push({ type: type, hash: hash });
|
||||
return next();
|
||||
}
|
||||
|
||||
if (isWitness)
|
||||
data = block.renderWitness();
|
||||
else
|
||||
data = block.renderNormal();
|
||||
|
||||
self._write(self.framer.packet('block', data));
|
||||
|
||||
if (hash === self.hashContinue) {
|
||||
self._write(self.framer.inv([{
|
||||
type: type,
|
||||
hash: self.chain.tip.hash
|
||||
}]));
|
||||
self.hashContinue = null;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
notfound.push({ type: type, hash: hash });
|
||||
return next();
|
||||
}, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
|
||||
utils.debug(
|
||||
'Served %d items to %s with getdata (notfound=%d).',
|
||||
items.length - notfound.length,
|
||||
notfound.length,
|
||||
self.host);
|
||||
|
||||
if (notfound.length > 0)
|
||||
self._write(self.framer.notFound(notfound));
|
||||
});
|
||||
};
|
||||
|
||||
Peer.prototype._handleAddr = function handleAddr(addrs) {
|
||||
var now = utils.now();
|
||||
var i, addr, ts, host;
|
||||
|
||||
addrs.forEach(function(addr) {
|
||||
var ts = addr.ts;
|
||||
var host = addr.ipv4 !== '0.0.0.0'
|
||||
for (i = 0; i < addrs.length; i++) {
|
||||
addr = addrs[i];
|
||||
|
||||
ts = addr.ts;
|
||||
host = addr.ipv4 !== '0.0.0.0'
|
||||
? addr.ipv4
|
||||
: addr.ipv6;
|
||||
|
||||
@ -608,7 +928,7 @@ Peer.prototype._handleAddr = function handleAddr(addrs) {
|
||||
headers: addr.version >= 31800,
|
||||
spv: addr.bloom && addr.version >= 70011
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
|
||||
utils.debug(
|
||||
'Recieved %d peers (seeds=%d, peers=%d).',
|
||||
@ -635,33 +955,38 @@ Peer.prototype._handlePong = function handlePong(data) {
|
||||
|
||||
Peer.prototype._handleGetAddr = function handleGetAddr() {
|
||||
var hosts = {};
|
||||
var peers;
|
||||
var peers = this.pool.peers.all;
|
||||
var items = [];
|
||||
var i, peer, ip, version;
|
||||
|
||||
peers = this.pool.peers.all.map(function(peer) {
|
||||
var ip, version;
|
||||
if (this.pool.options.selfish)
|
||||
return;
|
||||
|
||||
for (i = 0; i < peers.length; i++) {
|
||||
peer = peers[i];
|
||||
|
||||
if (!peer.socket || !peer.socket.remoteAddress)
|
||||
return;
|
||||
continue;
|
||||
|
||||
ip = peer.socket.remoteAddress;
|
||||
version = utils.isIP(ip);
|
||||
|
||||
if (!version)
|
||||
return;
|
||||
continue;
|
||||
|
||||
if (hosts[ip])
|
||||
return;
|
||||
continue;
|
||||
|
||||
hosts[ip] = true;
|
||||
|
||||
return {
|
||||
items.push({
|
||||
ts: peer.ts,
|
||||
services: peer.version ? peer.version.services : null,
|
||||
ipv4: version === 4 ? ip : null,
|
||||
ipv6: version === 6 ? ip : null,
|
||||
port: peer.socket.remotePort || network.port
|
||||
};
|
||||
}).filter(Boolean);
|
||||
});
|
||||
}
|
||||
|
||||
return this._write(this.framer.addr(peers));
|
||||
};
|
||||
@ -734,7 +1059,7 @@ Peer.prototype.getHeaders = function getHeaders(hashes, stop) {
|
||||
this.host);
|
||||
|
||||
utils.debug('Height: %s, Hash: %s, Stop: %s',
|
||||
this.pool.chain.getHeight(hashes[0]),
|
||||
this.chain._getCachedHeight(hashes[0]),
|
||||
hashes ? utils.revHex(hashes[0]) : 0,
|
||||
stop ? utils.revHex(stop) : 0);
|
||||
|
||||
@ -747,7 +1072,7 @@ Peer.prototype.getBlocks = function getBlocks(hashes, stop) {
|
||||
this.host);
|
||||
|
||||
utils.debug('Height: %s, Hash: %s, Stop: %s',
|
||||
this.pool.chain.getHeight(hashes[0]),
|
||||
this.chain._getCachedHeight(hashes[0]),
|
||||
hashes ? utils.revHex(hashes[0]) : 0,
|
||||
stop ? utils.revHex(stop) : 0);
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ var utils = require('../utils');
|
||||
var assert = utils.assert;
|
||||
var BufferWriter = require('../writer');
|
||||
var DUMMY = new Buffer([]);
|
||||
var bn = require('bn.js');
|
||||
|
||||
/**
|
||||
* Framer
|
||||
@ -56,8 +57,7 @@ Framer.prototype.header = function header(cmd, payload) {
|
||||
Framer.prototype.packet = function packet(cmd, payload) {
|
||||
var header;
|
||||
|
||||
if (!payload)
|
||||
payload = DUMMY;
|
||||
assert(payload, 'No payload.');
|
||||
|
||||
header = this.header(cmd, payload);
|
||||
|
||||
@ -77,6 +77,50 @@ Framer.prototype.verack = function verack() {
|
||||
return this.packet('verack', Framer.verack());
|
||||
};
|
||||
|
||||
Framer.prototype.mempool = function mempool() {
|
||||
return this.packet('mempool', Framer.mempool());
|
||||
};
|
||||
|
||||
Framer.prototype.getUTXOs = function getUTXOs(data) {
|
||||
return this.packet('getutxos', Framer.getUTXOs(data));
|
||||
};
|
||||
|
||||
Framer.prototype.UTXOs = function UTXOs(data) {
|
||||
return this.packet('utxos', Framer.UTXOs(data));
|
||||
};
|
||||
|
||||
Framer.prototype.getAddr = function getAddr() {
|
||||
return this.packet('getaddr', Framer.getAddr());
|
||||
};
|
||||
|
||||
Framer.prototype.submitOrder = function submitOrder() {
|
||||
return this.packet('submitorder', Framer.submitOrder());
|
||||
};
|
||||
|
||||
Framer.prototype.checkOrder = function checkOrder() {
|
||||
return this.packet('checkorder', Framer.checkOrder());
|
||||
};
|
||||
|
||||
Framer.prototype.reply = function reply() {
|
||||
return this.packet('reply', Framer.reply());
|
||||
};
|
||||
|
||||
Framer.prototype.sendHeaders = function sendHeaders() {
|
||||
return this.packet('sendheaders', Framer.sendHeaders());
|
||||
};
|
||||
|
||||
Framer.prototype.haveWitness = function haveWitness() {
|
||||
return this.packet('havewitness', Framer.haveWitness());
|
||||
};
|
||||
|
||||
Framer.prototype.filterAdd = function filterAdd(data) {
|
||||
return this.packet('filteradd', Framer.filterAdd(data));
|
||||
};
|
||||
|
||||
Framer.prototype.filterClear = function filterClear() {
|
||||
return this.packet('filterclear', Framer.filterClear());
|
||||
};
|
||||
|
||||
Framer.prototype.inv = function inv(items) {
|
||||
return this.packet('inv', Framer.inv(items));
|
||||
};
|
||||
@ -101,10 +145,6 @@ Framer.prototype.filterLoad = function filterLoad(bloom, update) {
|
||||
return this.packet('filterload', Framer.filterLoad(bloom, update));
|
||||
};
|
||||
|
||||
Framer.prototype.filterClear = function filterClear() {
|
||||
return this.packet('filterclear', Framer.filterClear());
|
||||
};
|
||||
|
||||
Framer.prototype.getHeaders = function getHeaders(hashes, stop) {
|
||||
return this.packet('getheaders', Framer.getHeaders(hashes, stop));
|
||||
};
|
||||
@ -118,11 +158,11 @@ Framer.prototype.utxo = function _coin(coin) {
|
||||
};
|
||||
|
||||
Framer.prototype.tx = function tx(tx) {
|
||||
return this.packet('tx', Framer.tx(tx));
|
||||
return this.packet('tx', Framer.renderTX(tx, false));
|
||||
};
|
||||
|
||||
Framer.prototype.witnessTX = function witnessTX(tx) {
|
||||
return this.packet('tx', Framer.witnessTX(tx));
|
||||
return this.packet('tx', Framer.renderTX(tx, true));
|
||||
};
|
||||
|
||||
Framer.prototype.block = function _block(block) {
|
||||
@ -149,10 +189,6 @@ Framer.prototype.addr = function addr(peers) {
|
||||
return this.packet('addr', Framer.addr(peers));
|
||||
};
|
||||
|
||||
Framer.prototype.mempool = function mempool() {
|
||||
return this.packet('mempool', Framer.mempool());
|
||||
};
|
||||
|
||||
Framer.address = function address(data, full, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
@ -230,7 +266,7 @@ Framer.version = function version(options, writer) {
|
||||
};
|
||||
|
||||
Framer.verack = function verack() {
|
||||
return new Buffer([]);
|
||||
return DUMMY;
|
||||
};
|
||||
|
||||
Framer._inv = function _inv(items, writer) {
|
||||
@ -295,33 +331,42 @@ Framer.filterLoad = function filterLoad(bloom, update, writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.filterClear = function filterClear() {
|
||||
return new Buffer([]);
|
||||
Framer.getHeaders = function getHeaders(locator, stop, writer) {
|
||||
// NOTE: getheaders can have an empty locator.
|
||||
return Framer._getBlocks(locator || [], stop, writer);
|
||||
};
|
||||
|
||||
Framer.getHeaders = function getHeaders(hashes, stop, writer) {
|
||||
// NOTE: getheaders can have a null hash
|
||||
if (!hashes)
|
||||
hashes = [];
|
||||
|
||||
return Framer._getBlocks(hashes, stop, writer);
|
||||
Framer.getBlocks = function getBlocks(locator, stop, writer) {
|
||||
return Framer._getBlocks(locator, stop, writer);
|
||||
};
|
||||
|
||||
Framer.getBlocks = function getBlocks(hashes, stop, writer) {
|
||||
return Framer._getBlocks(hashes, stop, writer);
|
||||
};
|
||||
Framer._getBlocks = function _getBlocks(locator, stop, writer) {
|
||||
var p, i, version;
|
||||
|
||||
Framer._getBlocks = function _getBlocks(hashes, stop, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i;
|
||||
if (locator.locator) {
|
||||
writer = stop;
|
||||
stop = locator.stop;
|
||||
version = locator.version;
|
||||
locator = locator.locator;
|
||||
}
|
||||
|
||||
p.writeU32(constants.version);
|
||||
p.writeVarint(hashes.length);
|
||||
if (!version)
|
||||
version = constants.version;
|
||||
|
||||
for (i = 0; i < hashes.length; i++)
|
||||
p.writeHash(hashes[i]);
|
||||
if (!stop)
|
||||
stop = constants.zeroHash;
|
||||
|
||||
p.writeHash(stop || constants.zeroHash);
|
||||
assert(locator, 'getblocks requires a locator');
|
||||
|
||||
p = new BufferWriter(writer);
|
||||
|
||||
p.writeU32(version);
|
||||
p.writeVarint(locator.length);
|
||||
|
||||
for (i = 0; i < locator.length; i++)
|
||||
p.writeHash(locator[i]);
|
||||
|
||||
p.writeHash(stop);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
@ -430,7 +475,7 @@ Framer.output = function _output(output, writer) {
|
||||
Framer.witnessTX = function _witnessTX(tx, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var witnessSize = 0;
|
||||
var i;
|
||||
var i, start;
|
||||
|
||||
p.write32(tx.version);
|
||||
p.writeU8(0);
|
||||
@ -447,7 +492,7 @@ Framer.witnessTX = function _witnessTX(tx, writer) {
|
||||
Framer.output(tx.outputs[i], p);
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
var start = p.written;
|
||||
start = p.written;
|
||||
Framer.witness(tx.inputs[i].witness, p);
|
||||
witnessSize += p.written - start;
|
||||
}
|
||||
@ -588,7 +633,30 @@ Framer.merkleBlock = function _merkleBlock(block, writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.headers = function _headers(block, writer) {
|
||||
Framer.headers = function _headers(headers, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i, header;
|
||||
|
||||
p.writeVarint(headers.length);
|
||||
|
||||
for (i = 0; i < headers.length; i++) {
|
||||
header = headers[i];
|
||||
p.write32(header.version);
|
||||
p.writeHash(header.prevBlock);
|
||||
p.writeHash(header.merkleRoot);
|
||||
p.writeU32(header.ts);
|
||||
p.writeU32(header.bits);
|
||||
p.writeU32(header.nonce);
|
||||
p.writeVarint(header.totalTX);
|
||||
}
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.blockHeaders = function blockHeaders(block, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.write32(block.version);
|
||||
@ -597,7 +665,6 @@ Framer.headers = function _headers(block, writer) {
|
||||
p.writeU32(block.ts);
|
||||
p.writeU32(block.bits);
|
||||
p.writeU32(block.nonce);
|
||||
p.writeVarint(block.totalTX);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
@ -647,10 +714,6 @@ Framer.addr = function addr(peers, writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.mempool = function mempool() {
|
||||
return new Buffer([]);
|
||||
};
|
||||
|
||||
Framer.alert = function alert(data, writer) {
|
||||
var p, i, payload;
|
||||
|
||||
@ -694,6 +757,134 @@ Framer.alert = function alert(data, writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.mempool = function mempool() {
|
||||
return DUMMY;
|
||||
};
|
||||
|
||||
Framer.getAddr = function getAddr() {
|
||||
return DUMMY;
|
||||
};
|
||||
|
||||
Framer.getUTXOs = function getUTXOs(data, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i, prevout;
|
||||
|
||||
p.writeU8(data.mempool ? 1 : 0);
|
||||
p.writeVarint(data.prevout.length);
|
||||
|
||||
for (i = 0; i < data.prevout.length; i++) {
|
||||
prevout = data.prevout[i];
|
||||
p.writeHash(prevout.hash);
|
||||
p.writeU32(prevout.index);
|
||||
}
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.UTXOs = function UTXOs(data, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
var i, j, coin, height, index, map;
|
||||
|
||||
if (!data.map) {
|
||||
assert(data.notfound);
|
||||
map = new bn(0);
|
||||
j = -1;
|
||||
for (i = 0; i < data.notfound.length; i++) {
|
||||
index = data.notfound[i];
|
||||
while (++j < index) {
|
||||
map.iushln(1);
|
||||
map.iuorn(1);
|
||||
}
|
||||
map.iushln(1);
|
||||
map.iuorn(0);
|
||||
}
|
||||
map = map.toBuffer('be');
|
||||
} else {
|
||||
map = data.map;
|
||||
}
|
||||
|
||||
p.writeU32(data.height);
|
||||
p.writeHash(data.tip);
|
||||
p.writeVarBytes(map);
|
||||
p.writeVarInt(data.coins.length);
|
||||
|
||||
for (i = 0; i < data.coins.length; i++) {
|
||||
coin = data.coins[i];
|
||||
height = coin.height;
|
||||
|
||||
if (height === -1)
|
||||
height = 0x7fffffff;
|
||||
|
||||
p.writeU32(coin.version);
|
||||
p.writeU32(height);
|
||||
Framer.output(coin, p);
|
||||
}
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.submitOrder = function submitOrder(order, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.writeHash(order.hash);
|
||||
Framer.renderTX(order.tx, true, p);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.checkOrder = function checkOrder(order, writer) {
|
||||
return Framer.submitOrder(order, writer);
|
||||
};
|
||||
|
||||
Framer.reply = function reply(data, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.writeHash(data.hash);
|
||||
p.writeU32(data.code || 0);
|
||||
|
||||
if (data.publicKey)
|
||||
p.writeVarBytes(data.publicKey);
|
||||
else
|
||||
p.writeVarInt(0);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.sendHeaders = function sendHeaders() {
|
||||
return DUMMY;
|
||||
};
|
||||
|
||||
Framer.haveWitness = function haveWitness() {
|
||||
return DUMMY;
|
||||
};
|
||||
|
||||
Framer.filterAdd = function filterAdd(data, writer) {
|
||||
var p = new BufferWriter(writer);
|
||||
|
||||
p.writeVarBytes(data.data || data);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.filterClear = function filterClear() {
|
||||
return DUMMY;
|
||||
};
|
||||
|
||||
// Total size and size of witness
|
||||
Framer.block._sizes = function blockSizes(block) {
|
||||
var writer = new BufferWriter();
|
||||
|
||||
@ -119,44 +119,198 @@ Parser.prototype.parseHeader = function parseHeader(h) {
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseMempool = function parseMempool(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.parseSubmitOrder = function parseSubmitOrder(p) {
|
||||
var hash, tx;
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
return {
|
||||
hash: p.readHash(),
|
||||
tx: Parser.parseTX(p),
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseCheckOrder = function parseCheckOrder(p) {
|
||||
return Parser.parseSubmitOrder(p);
|
||||
};
|
||||
|
||||
Parser.parseReply = function parseReply(p) {
|
||||
var hash, code, publicKey;
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
return {
|
||||
hash: p.readHash(),
|
||||
code: p.readU32(),
|
||||
publicKey: p.readVarBytes(),
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseSendHeaders = function parseSendHeaders(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.parseHaveWitness = function parseHaveWitness(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.parseGetAddr = function parseGetAddr(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.parseFilterLoad = function parseFilterLoad(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.parseFilterAdd = function parseFilterAdd(p) {
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
return {
|
||||
data: p.readVarBytes(),
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseFilterClear = function parseFilterClear(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.prototype.parsePayload = function parsePayload(cmd, p) {
|
||||
if (cmd === 'version')
|
||||
return Parser.parseVersion(p);
|
||||
switch (cmd) {
|
||||
case 'version':
|
||||
return Parser.parseVersion(p);
|
||||
case 'verack':
|
||||
return Parser.parseVerack(p);
|
||||
case 'mempool':
|
||||
return Parser.parseMempool(p);
|
||||
case 'getaddr':
|
||||
return Parser.parseGetAddr(p);
|
||||
case 'submitorder':
|
||||
return Parser.parseSubmitOrder(p);
|
||||
case 'checkorder':
|
||||
return Parser.parseCheckOrder(p);
|
||||
case 'reply':
|
||||
return Parser.parseReply(p);
|
||||
case 'sendheaders':
|
||||
return Parser.parseSendHeaders(p);
|
||||
case 'havewitness':
|
||||
return Parser.parseHaveWitness(p);
|
||||
case 'filterload':
|
||||
return Parser.parseFilterLoad(p);
|
||||
case 'filteradd':
|
||||
return Parser.parseFilterAdd(p);
|
||||
case 'filterclear':
|
||||
return Parser.parseFilterClear(p);
|
||||
case 'inv':
|
||||
return Parser.parseInv(p);
|
||||
case 'getdata':
|
||||
return Parser.parseGetData(p);
|
||||
case 'notfound':
|
||||
return Parser.parseNotFound(p);
|
||||
case 'getheaders':
|
||||
return Parser.parseGetHeaders(p);
|
||||
case 'getblocks':
|
||||
return Parser.parseGetBlocks(p);
|
||||
case 'merkleblock':
|
||||
return Parser.parseMerkleBlock(p);
|
||||
case 'headers':
|
||||
return Parser.parseHeaders(p);
|
||||
case 'block':
|
||||
return Parser.parseBlockCompact(p);
|
||||
case 'tx':
|
||||
return Parser.parseTX(p);
|
||||
case 'reject':
|
||||
return Parser.parseReject(p);
|
||||
case 'addr':
|
||||
return Parser.parseAddr(p);
|
||||
case 'ping':
|
||||
return Parser.parsePing(p);
|
||||
case 'pong':
|
||||
return Parser.parsePong(p);
|
||||
case 'alert':
|
||||
return Parser.parseAlert(p);
|
||||
case 'getutxos':
|
||||
return Parser.parseGetUTXOs(p);
|
||||
case 'utxos':
|
||||
return Parser.parseUTXOs(p);
|
||||
default:
|
||||
utils.debug('Unknown packet: %s', cmd);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
if (cmd === 'getdata' || cmd === 'inv' || cmd === 'notfound')
|
||||
return Parser.parseInvList(p);
|
||||
Parser.parseGetUTXOs = function parseGetUTXOs(p) {
|
||||
var mempool, prevout, count, i;
|
||||
|
||||
if (cmd === 'merkleblock')
|
||||
return Parser.parseMerkleBlock(p);
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
|
||||
if (cmd === 'headers')
|
||||
return Parser.parseHeaders(p);
|
||||
mempool = p.readU8() === 1;
|
||||
prevout = [];
|
||||
count = p.readVarint();
|
||||
|
||||
if (cmd === 'block')
|
||||
return Parser.parseBlockCompact(p);
|
||||
for (i = 0; i < count; i++) {
|
||||
prevout.push({
|
||||
hash: p.readHash('hex'),
|
||||
index: p.readU32()
|
||||
});
|
||||
}
|
||||
|
||||
if (cmd === 'tx')
|
||||
return Parser.parseTX(p);
|
||||
return {
|
||||
mempool: mempool,
|
||||
prevout: prevout,
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
if (cmd === 'reject')
|
||||
return Parser.parseReject(p);
|
||||
Parser.parseUTXOs = function parseUTXOs(p) {
|
||||
var chainHeight, tip, map, count, coins;
|
||||
var coin, version, height, i, notfound, ch, j;
|
||||
|
||||
if (cmd === 'addr')
|
||||
return Parser.parseAddr(p);
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
|
||||
if (cmd === 'ping')
|
||||
return Parser.parsePing(p);
|
||||
chainHeight = p.readU32();
|
||||
tip = p.readHash('hex');
|
||||
map = p.readVarBytes();
|
||||
count = p.readVarint();
|
||||
coins = [];
|
||||
notfound = [];
|
||||
|
||||
if (cmd === 'pong')
|
||||
return Parser.parsePong(p);
|
||||
for (i = 0; i < map.length; i++) {
|
||||
ch = map[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((ch & 1) === 0)
|
||||
notfound.push(i + j);
|
||||
ch >>>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd === 'alert')
|
||||
return Parser.parseAlert(p);
|
||||
for (i = 0; i < count; i++) {
|
||||
version = p.readU32();
|
||||
height = p.readU32();
|
||||
|
||||
if (cmd === 'utxo')
|
||||
return Parser.parseUTXO(p);
|
||||
if (height === 0x7fffffff)
|
||||
height = -1;
|
||||
|
||||
return p;
|
||||
coin = Parser.parseOutput(p);
|
||||
coin.version = version;
|
||||
coin.height = height;
|
||||
coins.push(coin);
|
||||
}
|
||||
|
||||
return {
|
||||
height: chainHeight,
|
||||
tip: tip,
|
||||
map: map,
|
||||
coins: coins,
|
||||
notfound: notfound,
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parsePing = function parsePing(p) {
|
||||
@ -223,7 +377,58 @@ Parser.parseVersion = function parseVersion(p) {
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseInvList = function parseInvList(p) {
|
||||
Parser.parseVerack = function parseVerack(p) {
|
||||
return {};
|
||||
};
|
||||
|
||||
Parser.parseNotFound = function parseNotFound(p) {
|
||||
return Parser.parseInv(p);
|
||||
};
|
||||
|
||||
Parser.parseGetData = function parseGetData(p) {
|
||||
return Parser.parseInv(p);
|
||||
};
|
||||
|
||||
Parser._parseGetBlocks = function _parseGetBlocks(p) {
|
||||
var version, count, locator, i, stop;
|
||||
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
|
||||
version = p.readU32();
|
||||
count = p.readVarint();
|
||||
locator = [];
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
locator.push(p.readHash('hex'));
|
||||
|
||||
stop = p.readHash('hex');
|
||||
|
||||
if (stop === constants.nullHash)
|
||||
stop = null;
|
||||
|
||||
return {
|
||||
version: version,
|
||||
locator: locator,
|
||||
stop: stop,
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseGetBlocks = function parseGetBlocks(p) {
|
||||
var data = Parser._parseGetBlocks(p);
|
||||
assert(data.locator.length > 0, 'getblocks requires a locator.');
|
||||
return data;
|
||||
};
|
||||
|
||||
Parser.parseGetHeaders = function parseGetHeaders(p) {
|
||||
var data = Parser._parseGetBlocks(p);
|
||||
if (data.locator.length === 0)
|
||||
data.locator = null;
|
||||
return data;
|
||||
};
|
||||
|
||||
Parser.parseInv = function parseInv(p) {
|
||||
var items = [];
|
||||
var i, count;
|
||||
|
||||
@ -308,6 +513,22 @@ Parser.parseHeaders = function parseHeaders(p) {
|
||||
return headers;
|
||||
};
|
||||
|
||||
Parser.parseBlockHeaders = function parseBlockHeaders(p) {
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
|
||||
return {
|
||||
version: p.readU32(), // Technically signed
|
||||
prevBlock: p.readHash('hex'),
|
||||
merkleRoot: p.readHash('hex'),
|
||||
ts: p.readU32(),
|
||||
bits: p.readU32(),
|
||||
nonce: p.readU32(),
|
||||
totalTX: p.readVarint(),
|
||||
_size: p.end()
|
||||
}
|
||||
};
|
||||
|
||||
Parser.parseBlock = function parseBlock(p) {
|
||||
var txs = [];
|
||||
var witnessSize = 0;
|
||||
|
||||
@ -587,6 +587,11 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op === opcodes.OP_1NEGATE) {
|
||||
stack.push(STACK_NEGATE);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case opcodes.OP_NOP:
|
||||
case opcodes.OP_NOP1:
|
||||
@ -601,10 +606,6 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
||||
throw new ScriptError('Upgradable NOP used.', op, ip);
|
||||
break;
|
||||
}
|
||||
case opcodes.OP_1NEGATE: {
|
||||
stack.push(STACK_NEGATE);
|
||||
break;
|
||||
}
|
||||
case opcodes.OP_IF:
|
||||
case opcodes.OP_NOTIF: {
|
||||
if (stack.length < 1)
|
||||
@ -1047,13 +1048,13 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
||||
if (stack.length === 0)
|
||||
throw new ScriptError('Stack too small.', op, ip);
|
||||
|
||||
// NOTE: Bitcoind accepts 5 byte locktimes.
|
||||
// 4 byte locktimes become useless in 2106.
|
||||
locktime = Script.num(stack.top(-1), flags, 5).toNumber();
|
||||
locktime = Script.num(stack.top(-1), flags, 5);
|
||||
|
||||
if (locktime < 0)
|
||||
if (locktime.cmpn(0) < 0)
|
||||
throw new ScriptError('Negative locktime.', op, ip);
|
||||
|
||||
locktime = locktime.uand(utils.U32).toNumber();
|
||||
|
||||
if (!Script.checkLocktime(locktime, tx, index))
|
||||
throw new ScriptError('Locktime verification failed.', op, ip);
|
||||
|
||||
@ -1073,14 +1074,13 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version
|
||||
if (stack.length === 0)
|
||||
throw new ScriptError('Stack too small.', op, ip);
|
||||
|
||||
// NOTE: Bitcoind accepts 5 byte locktimes.
|
||||
// 4 byte locktimes become useless in 2106
|
||||
// (will people still be using bcoin then?).
|
||||
locktime = Script.num(stack.top(-1), flags, 4).toNumber();
|
||||
locktime = Script.num(stack.top(-1), flags, 5);
|
||||
|
||||
if (locktime < 0)
|
||||
if (locktime.cmpn(0) < 0)
|
||||
throw new ScriptError('Negative sequence.', op, ip);
|
||||
|
||||
locktime = locktime.uand(utils.U32).toNumber();
|
||||
|
||||
if ((locktime & constants.sequenceLocktimeDisableFlag) !== 0)
|
||||
break;
|
||||
|
||||
@ -1153,14 +1153,6 @@ Script.checkSequence = function checkSequence(sequence, tx, i) {
|
||||
Script.bool = function bool(value) {
|
||||
var i;
|
||||
|
||||
// Should never happen:
|
||||
// if (typeof value === 'boolean')
|
||||
// return value;
|
||||
|
||||
// Should never happen:
|
||||
// if (utils.isFinite(value))
|
||||
// return value !== 0;
|
||||
|
||||
if (bn.isBN(value))
|
||||
return value.cmpn(0) !== 0;
|
||||
|
||||
@ -1210,10 +1202,10 @@ Script.num = function num(value, flags, size) {
|
||||
// negative flag.
|
||||
if (value[value.length - 1] & 0x80) {
|
||||
if (utils.isNegZero(value, 'le')) {
|
||||
value = new bn(0, 'le');
|
||||
value = new bn(0);
|
||||
} else {
|
||||
value = new bn(value, 'le');
|
||||
value = value.notn(value.bitLength()).addn(1).neg();
|
||||
value = value.notn(size * 8).addn(1).neg();
|
||||
}
|
||||
} else {
|
||||
value = new bn(value, 'le');
|
||||
@ -1237,7 +1229,7 @@ Script.array = function(value) {
|
||||
if (value.cmpn(0) === 0)
|
||||
value = new bn(0);
|
||||
else
|
||||
value = value.neg().notn(value.bitLength()).subn(1);
|
||||
value = value.neg().notn(value.byteLength() * 8).subn(1);
|
||||
}
|
||||
|
||||
if (value.cmpn(0) === 0)
|
||||
@ -1349,6 +1341,7 @@ Script.createMultisig = function createMultisig(keys, m, n) {
|
||||
};
|
||||
|
||||
Script.createScripthash = function createScripthash(hash) {
|
||||
assert(hash.length === 20);
|
||||
return new Script([
|
||||
opcodes.OP_HASH160,
|
||||
hash,
|
||||
@ -1357,6 +1350,8 @@ Script.createScripthash = function createScripthash(hash) {
|
||||
};
|
||||
|
||||
Script.createNulldata = function createNulldata(flags) {
|
||||
assert(Buffer.isBuffer(flags));
|
||||
assert(flags.length <= constants.script.maxOpReturn, 'Nulldata too large.');
|
||||
return new Script([
|
||||
opcodes.OP_RETURN,
|
||||
flags
|
||||
@ -1366,11 +1361,12 @@ Script.createNulldata = function createNulldata(flags) {
|
||||
Script.createWitnessProgram = function createWitnessProgram(version, data) {
|
||||
assert(typeof version === 'number' && version >= 0 && version <= 16);
|
||||
assert(Buffer.isBuffer(data));
|
||||
assert(data.length === 20 || data.length === 32);
|
||||
assert(data.length >= 2 && data.length <= 32);
|
||||
return new Script([version === 0 ? 0 : version + 0x50, data]);
|
||||
};
|
||||
|
||||
Script.createCommitment = function createCommitment(hash) {
|
||||
assert(hash.length === 32);
|
||||
return new Script([
|
||||
opcodes.OP_RETURN,
|
||||
Buffer.concat([new Buffer([0xaa, 0x21, 0xa9, 0xed]), hash])
|
||||
@ -1859,8 +1855,6 @@ Script.createOutputScript = function(options) {
|
||||
flags = options.flags;
|
||||
if (typeof flags === 'string')
|
||||
flags = new Buffer(flags, 'utf8');
|
||||
assert(Buffer.isBuffer(flags));
|
||||
assert(flags.length <= constants.script.maxOpReturn, 'Nulldata too large.');
|
||||
return Script.createNulldata(flags);
|
||||
}
|
||||
|
||||
@ -1875,9 +1869,6 @@ Script.createOutputScript = function(options) {
|
||||
} else if (options.keys) {
|
||||
m = options.m;
|
||||
n = options.n || options.keys.length;
|
||||
assert(m >= 1 && m <= n, 'm must be between 1 and n');
|
||||
assert(n >= 1 && n <= (options.scriptHash ? 15 : 3),
|
||||
'n must be between 1 and 15');
|
||||
script = Script.createMultisig(options.keys, m, n);
|
||||
} else if (Buffer.isBuffer(options.scriptHash)) {
|
||||
if (options.version != null) {
|
||||
|
||||
@ -45,6 +45,8 @@ SPVNode.prototype._init = function _init() {
|
||||
// Pool needs access to the chain.
|
||||
this.pool = new bcoin.pool(this, {
|
||||
witness: this.network.witness,
|
||||
selfish: true,
|
||||
listen: false,
|
||||
spv: true
|
||||
});
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ function TX(data, block, index) {
|
||||
|
||||
if (block && this.ts === 0) {
|
||||
if (block.type === 'merkleblock') {
|
||||
if (block.hasTX(this.hash('hex')))
|
||||
if (block.hasTX(this))
|
||||
this.setBlock(block, index);
|
||||
} else {
|
||||
this.setBlock(block, index);
|
||||
@ -118,6 +118,15 @@ TX.prototype.setBlock = function setBlock(block, index) {
|
||||
this.block = block.hash('hex');
|
||||
this.height = block.height;
|
||||
this.index = index;
|
||||
this.ps = 0;
|
||||
};
|
||||
|
||||
TX.prototype.unsetBlock = function unsetBlock() {
|
||||
this.ts = 0;
|
||||
this.block = null;
|
||||
this.height = -1;
|
||||
this.index = -1;
|
||||
this.ps = utils.now();
|
||||
};
|
||||
|
||||
TX.prototype.hash = function hash(enc) {
|
||||
@ -702,7 +711,8 @@ TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
|
||||
|
||||
if (prev && prev.isWitnessScripthash()) {
|
||||
prev = input.witness.getRedeem();
|
||||
cost += prev.getSigops(true);
|
||||
if (prev)
|
||||
cost += prev.getSigops(true);
|
||||
} else {
|
||||
cost += 0;
|
||||
}
|
||||
|
||||
@ -1444,21 +1444,6 @@ utils.cmp = function cmp(a, b) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
utils.rcmp = function rcmp(a, b) {
|
||||
var i;
|
||||
|
||||
assert(a.length === b.length);
|
||||
|
||||
for (i = a.length - 1; i >= 0; i--) {
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// memcmp in constant time (can only return true or false)
|
||||
// https://cryptocoding.net/index.php/Coding_rules
|
||||
// $ man 3 memcmp (see NetBSD's consttime_memequal)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user