style consistency.

This commit is contained in:
Christopher Jeffrey 2015-12-18 22:37:02 -08:00
parent 9fe8afa7d3
commit 5ece45091b
16 changed files with 1072 additions and 661 deletions

View File

@ -9,10 +9,6 @@ synchronized balance and send and receive payments without keeping track of a
BCoin is implemented in *pure* javascript, and is browserify-able (this means
compiling a binding to an ECDSA library is not even required for node.js).
**NOTE**: BCoin is also in the process of supporting the original (pre-bip37)
satoshi protocol, which will also optionally give the user the ability download
the entire blockchain.
## Prerequisites
```

View File

@ -3,6 +3,8 @@ var utils = bcoin.utils;
var constants = bcoin.protocol.constants;
function Block(data, subtype) {
var self = this;
if (!(this instanceof Block))
return new Block(data, subtype);
@ -28,7 +30,6 @@ function Block(data, subtype) {
this.invalid = false;
if (this.subtype === 'block') {
var self = this;
this.txs = data.txs || [];
this.txs = this.txs.map(function(tx) {
tx._network = self._network;
@ -49,7 +50,6 @@ function Block(data, subtype) {
// Verify partial merkle tree and fill `ts` array
this._verifyMerkle();
}
module.exports = Block;
Block.prototype.hash = function hash(enc) {
// Hash it
@ -91,33 +91,30 @@ Block.prototype.hasTX = function hasTX(hash) {
Block.prototype._verifyMerkle = function verifyMerkle() {
var height = 0;
if (this.subtype === 'block')
return;
// Count leafs
for (var i = this.totalTX; i > 0; i >>= 1)
height++;
if (this.totalTX > (1 << (height - 1)))
height++;
var tx = [];
var i = 0;
var j = 0;
var hashes = this.hashes;
var flags = this.flags;
var i, root;
var root = visit(1);
if (!root || root !== this.merkleRoot) {
this.invalid = true;
if (this.subtype === 'block')
return;
}
this.tx = tx;
// Count leaves
for (i = this.totalTX; i > 0; i >>= 1)
height++;
if (this.totalTX > (1 << (height - 1)))
height++;
function visit(depth) {
var flag, left, right;
if (i === flags.length * 8 || j === hashes.length)
return null;
var flag = (flags[i >> 3] >>> (i & 7)) & 1;
flag = (flags[i >> 3] >>> (i & 7)) & 1;
i++;
if (flag === 0 || depth === height) {
@ -127,34 +124,49 @@ Block.prototype._verifyMerkle = function verifyMerkle() {
}
// Go deeper
var left = visit(depth + 1);
left = visit(depth + 1);
if (!left)
return null;
var right = visit(depth + 1);
right = visit(depth + 1);
if (right === left)
return null;
if (!right)
right = left;
return utils.toHex(utils.dsha256(left + right, 'hex'));
}
root = visit(1);
if (!root || root !== this.merkleRoot) {
this.invalid = true;
return;
}
this.tx = tx;
};
Block.prototype.getMerkleRoot = function getMerkleRoot() {
var merkleTree = [];
var i, j, size, i2, hash;
for (var i = 0; i < this.txs.length; i++) {
for (i = 0; i < this.txs.length; i++) {
merkleTree.push(this.txs[i].hash('hex'));
}
var j = 0;
for (var size = this.txs.length; size > 1; size = ((size + 1) / 2) | 0) {
for (var i = 0; i < size; i += 2) {
var i2 = Math.min(i + 1, size - 1);
j = 0;
size = this.txs.length;
for (; size > 1; size = ((size + 1) / 2) | 0) {
for (i = 0; i < size; i += 2) {
i2 = Math.min(i + 1, size - 1);
if (i2 === i + 1 && i2 + 1 === size
&& merkleTree[j + i] === merkleTree[j + i2]) {
return utils.toHex(constants.zeroHash);
}
var hash = utils.dsha256(merkleTree[j + i] + merkleTree[j + i2], 'hex');
hash = utils.dsha256(merkleTree[j + i] + merkleTree[j + i2], 'hex');
merkleTree.push(utils.toHex(hash));
}
j += size;
@ -169,6 +181,8 @@ Block.prototype.getMerkleRoot = function getMerkleRoot() {
// This mimics the behavior of CheckBlockHeader()
// and CheckBlock() in bitcoin/src/main.cpp.
Block.prototype._checkBlock = function checkBlock() {
var i, unique, hash, merkleRoot;
// Check proof of work matches claimed amount
if (!utils.testTarget(this.bits, this.hash()))
return false;
@ -184,29 +198,29 @@ Block.prototype._checkBlock = function checkBlock() {
}
// 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].inputs.length !== 1
|| +this.txs[0].inputs[0].out.hash !== 0)
return false;
// The rest of the txs must not be coinbases
for (var i = 1; i < this.txs.length; i++) {
if (this.txs[i].inputs.length === 1 &&
+this.txs[i].inputs[0].out.hash === 0)
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
var unique = {};
for (var i = 0; i < this.txs.length; i++) {
var hash = this.txs[i].hash('hex');
unique = {};
for (i = 0; i < this.txs.length; i++) {
hash = this.txs[i].hash('hex');
if (unique[hash])
return false;
unique[hash] = true;
}
// Build MerkleTree
var merkleRoot = this.getMerkleRoot();
merkleRoot = this.getMerkleRoot();
// Check merkle root
if (merkleRoot !== this.merkleRoot)
@ -228,22 +242,26 @@ Block.prototype.toJSON = function toJSON() {
};
Block.fromJSON = function fromJSON(json) {
var raw, parser, data, block;
utils.assert.equal(json.v, 1);
utils.assert.equal(json.type, 'block');
var raw = utils.toArray(json.block, 'hex');
raw = utils.toArray(json.block, 'hex');
var parser = new bcoin.protocol.parser();
parser = new bcoin.protocol.parser();
var data = json.subtype === 'merkleblock' ?
data = json.subtype === 'merkleblock' ?
parser.parseMerkleBlock(raw) :
parser.parseBlock(raw);
data._network = json._network;
var block = new Block(data, json.subtype);
block = new Block(data, json.subtype);
block._hash = json.hash;
return block;
};
module.exports = Block;

View File

@ -12,7 +12,6 @@ function Bloom(size, n, tweak) {
this.reset();
}
module.exports = Bloom;
Bloom.prototype.hash = function hash(val, n) {
return Bloom.hash(val, sum32(mul32(n, 0xfba4c795), this.tweak)) % this.size;
@ -24,24 +23,28 @@ Bloom.prototype.reset = function reset() {
};
Bloom.prototype.add = function add(val, enc) {
var i, bit, pos, shift;
val = utils.toArray(val, enc);
for (var i = 0; i < this.n; i++) {
var bit = this.hash(val, i);
var pos = 1 << (bit & 0x1f);
var shift = bit >> 5;
for (i = 0; i < this.n; i++) {
bit = this.hash(val, i);
pos = 1 << (bit & 0x1f);
shift = bit >> 5;
this.filter[shift] |= pos;
}
};
Bloom.prototype.test = function test(val, enc) {
var i, bit, pos, shift;
val = utils.toArray(val, enc);
for (var i = 0; i < this.n; i++) {
var bit = this.hash(val, i);
var pos = 1 << (bit & 0x1f);
var shift = bit >> 5;
for (i = 0; i < this.n; i++) {
bit = this.hash(val, i);
pos = 1 << (bit & 0x1f);
shift = bit >> 5;
if ((this.filter[shift] & pos) === 0)
return false;
@ -53,8 +56,10 @@ Bloom.prototype.test = function test(val, enc) {
Bloom.prototype.toArray = function toArray() {
var bytes = Math.ceil(this.size / 8);
var res = new Array(this.filter.length * 4);
for (var i = 0; i < this.filter.length; i++) {
var w = this.filter[i];
var i, w;
for (i = 0; i < this.filter.length; i++) {
w = this.filter[i];
res[i * 4] = w & 0xff;
res[i * 4 + 1] = (w >> 8) & 0xff;
res[i * 4 + 2] = (w >> 16) & 0xff;
@ -69,22 +74,26 @@ function mul32(a, b) {
var blo = b & 0xffff;
var ahi = a >>> 16;
var bhi = b >>> 16;
var r;
var lo = alo * blo;
var hi = (ahi * blo + bhi * alo) & 0xffff;
hi += lo >>> 16;
lo &= 0xffff;
var r = (hi << 16) | lo;
r = (hi << 16) | lo;
if (r < 0)
r += 0x100000000;
return r;
}
function sum32(a, b) {
var r = (a + b) & 0xffffffff;
if (r < 0)
r += 0x100000000;
return r;
}
@ -103,11 +112,14 @@ function hash(data, seed) {
var n = 0xe6546b64;
var hash = seed;
for (var i = 0; i + 4 <= data.length; i += 4) {
var w = data[i] |
(data[i + 1] << 8) |
(data[i + 2] << 16) |
(data[i + 3] << 24);
var i, w, r, j;
for (i = 0; i + 4 <= data.length; i += 4) {
w = data[i]
| (data[i + 1] << 8)
| (data[i + 2] << 16)
| (data[i + 3] << 24);
w = mul32(w, c1);
w = rotl32(w, r1);
@ -120,8 +132,8 @@ function hash(data, seed) {
}
if (i !== data.length) {
var r = 0;
for (var j = data.length - 1; j >= i; j--)
r = 0;
for (j = data.length - 1; j >= i; j--)
r = (r << 8) | data[j];
r = mul32(r, c1);
@ -145,4 +157,7 @@ function hash(data, seed) {
return hash;
}
Bloom.hash = hash;
module.exports = Bloom;

View File

@ -8,6 +8,8 @@ var utils = bcoin.utils;
var assert = utils.assert;
function Chain(options) {
var preload = network.preload;
if (!(this instanceof Chain))
return new Chain(options);
@ -38,8 +40,6 @@ function Chain(options) {
};
this.request = new utils.RequestCache();
var preload = network.preload;
// Start from the genesis block
// if we're a full node.
if (this.options.fullNode) {
@ -60,8 +60,8 @@ function Chain(options) {
this.loading = false;
this._init();
}
inherits(Chain, EventEmitter);
module.exports = Chain;
function compareTs(a, b) {
return a -b;
@ -69,6 +69,7 @@ function compareTs(a, b) {
Chain.prototype._init = function _init() {
var self = this;
var s;
if (!this.storage)
return;
@ -79,7 +80,7 @@ Chain.prototype._init = function _init() {
this.loading = true;
var s = this.storage.createReadStream({
s = this.storage.createReadStream({
start: this.prefix,
end: this.prefix + 'z'
});
@ -103,18 +104,19 @@ Chain.prototype._init = function _init() {
Chain.prototype._getRange = function _getRange(hash, ts, futureOnly) {
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
var start = Math.min(Math.max(0, pos), this.index.ts.length - 1);
var curr, wnd, end;
while (start > 0 && this.index.ts[start] > ts)
start--;
var curr = this.index.ts[start];
var wnd = 2 * 3600;
curr = this.index.ts[start];
wnd = 2 * 3600;
if (!futureOnly)
while (start > 0 && this.index.ts[start] + wnd > curr)
start--;
var end = Math.min(Math.max(0, pos), this.index.ts.length - 1);
end = Math.min(Math.max(0, pos), this.index.ts.length - 1);
while (end < this.index.ts.length - 1 && this.index.ts[end] - wnd < ts)
end++;
@ -127,13 +129,15 @@ Chain.prototype._probeIndex = function _probeIndex(hash, ts) {
var start = 0;
var end = this.index.ts.length;
var range, i;
if (ts) {
var range = this._getRange(hash, ts);
range = this._getRange(hash, ts);
start = range.start;
end = range.end;
}
for (var i = start; i <= end; i++)
for (i = start; i <= end; i++)
if (this.index.hashes[i] === hash)
return { i: i, height: this.index.heights[i], ts: this.index.ts[i] };
@ -141,19 +145,22 @@ Chain.prototype._probeIndex = function _probeIndex(hash, ts) {
};
Chain.prototype._addIndex = function _addIndex(hash, ts, height) {
var self = this;
if (this._probeIndex(hash, ts))
return new Error('Already added.');
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
var checkpoint, obj;
// Avoid duplicates
if (this.index.hashes[pos] === hash ||
this.index.hashes[pos - 1] === hash ||
this.index.hashes[pos + 1] === hash) {
if (this.index.hashes[pos] === hash
|| this.index.hashes[pos - 1] === hash
|| this.index.hashes[pos + 1] === hash) {
return new Error('Duplicate height.');
}
var checkpoint = network.checkpoints[height];
checkpoint = network.checkpoints[height];
if (checkpoint) {
this.emit('checkpoint', height, hash, checkpoint);
if (hash !== checkpoint) {
@ -171,8 +178,8 @@ Chain.prototype._addIndex = function _addIndex(hash, ts, height) {
if (!this.storage)
return;
var self = this;
var obj = { ts: ts, height: height };
obj = { ts: ts, height: height };
this.storage.put(this.prefix + hash, obj, function(err) {
if (err)
self.emit('error', err);
@ -195,14 +202,14 @@ Chain.prototype.resetHeight = function resetHeight(height) {
if (index < 0)
throw new Error('Cannot reset to height of ' + height);
this.block.list = [];
this.block.list.length = 0;
this.block.bloom.reset();
this.orphan.map = {};
this.orphan.bmap = {};
this.orphan.count = 0;
this.index.ts = this.index.ts.slice(0, index + 1);
this.index.hashes = this.index.hashes.slice(0, index + 1);
this.index.heights = this.index.heights.slice(0, index + 1);
this.index.ts.length = index + 1;
this.index.hashes.length = index + 1;
this.index.heights.length = index + 1;
this.index.bloom.reset();
this.index.hashes.forEach(function(hash) {
self.index.bloom.add(hash, 'hex');
@ -211,13 +218,15 @@ Chain.prototype.resetHeight = function resetHeight(height) {
};
Chain.prototype._killFork = function _killFork(probe) {
var self = this;
var delta = 2 * 3600;
var upper = probe.ts + delta;
var lower = probe.ts - delta;
var index, i, len, hash;
// Search duplicate heights down
var index = -1;
for (var i = probe.i - 1; i > 0 && this.index.ts[i] > lower; i--) {
index = -1;
for (i = probe.i - 1; i > 0 && this.index.ts[i] > lower; i--) {
if (probe.height === this.index.heights[i]) {
index = i;
break;
@ -226,8 +235,8 @@ Chain.prototype._killFork = function _killFork(probe) {
// And up
if (index === -1) {
var len = this.index.ts.length;
for (var i = probe.i + 1; i < len && this.index.ts[i] < upper; i++) {
len = this.index.ts.length;
for (i = probe.i + 1; i < len && this.index.ts[i] < upper; i++) {
if (probe.height === this.index.heights[i]) {
index = i;
break;
@ -238,7 +247,7 @@ Chain.prototype._killFork = function _killFork(probe) {
if (index === -1)
return false;
var hash = this.index.hashes[index];
hash = this.index.hashes[index];
this.index.hashes.splice(index, 1);
this.index.ts.splice(index, 1);
this.index.heights.splice(index, 1);
@ -247,7 +256,6 @@ Chain.prototype._killFork = function _killFork(probe) {
if (!this.storage)
return true;
var self = this;
this.storage.del(this.prefix + hash, function(err) {
if (err)
self.emit('error', err);
@ -267,6 +275,8 @@ Chain.prototype.add = function add(block) {
var res = false;
var err = null;
var initial = block;
var hash, prev, prevProbe, range, hashes;
do {
// No need to revalidate orphans
if (!res && !block.verify()) {
@ -274,8 +284,8 @@ Chain.prototype.add = function add(block) {
break;
}
var hash = block.hash('hex');
var prev = block.prevBlock;
hash = block.hash('hex');
prev = block.prevBlock;
// If the block is already known to be an orphan
if (this.orphan.map[prev]) {
@ -283,7 +293,7 @@ Chain.prototype.add = function add(block) {
break;
}
var prevProbe = this._probeIndex(prev, block.ts);
prevProbe = this._probeIndex(prev, block.ts);
// Remove forked nodes from storage, if shorter chain is detected
if (this._killFork(prevProbe)) {
@ -297,8 +307,9 @@ Chain.prototype.add = function add(block) {
this.orphan.map[prev] = block;
this.orphan.bmap[hash] = block;
var range = this._getRange(hash, block.ts, true);
var hashes = this.index.hashes.slice(range.start, range.end + 1);
range = this._getRange(hash, block.ts, true);
hashes = this.index.hashes.slice(range.start, range.end + 1);
this.emit('missing', prev, hashes, block);
break;
}
@ -334,6 +345,8 @@ Chain.prototype.add = function add(block) {
};
Chain.prototype._compress = function compress() {
var i;
// Keep at least 1000 blocks and at most 2000 by default
if (this.block.list.length < this.cacheLimit)
return;
@ -342,7 +355,7 @@ Chain.prototype._compress = function compress() {
this.block.list = this.block.list.slice(-(this.cacheLimit / 2 | 0));
this.block.bloom.reset();
for (var i = 0; i < this.block.list.length; i++)
for (i = 0; i < this.block.list.length; i++)
this._bloomBlock(this.block.list[i]);
};
@ -351,21 +364,25 @@ Chain.prototype._bloomBlock = function _bloomBlock(block) {
};
Chain.prototype.has = function has(hash, noProbe, cb) {
var i;
if (typeof noProbe === 'function') {
cb = noProbe;
noProbe = false;
}
if (this.loading) {
this.once('load', function() {
this.has(hash, noProbe, cb);
});
return;
}
cb = utils.asyncify(cb);
if (this.block.bloom.test(hash, 'hex')) {
if (this.strict) {
for (var i = 0; i < this.block.list.length; i++)
for (i = 0; i < this.block.list.length; i++)
if (this.block.list[i].hash('hex') === hash)
return cb(true);
} else {
@ -406,6 +423,8 @@ Chain.prototype.hasOrphan = function hasOrphan(hash) {
};
Chain.prototype.get = function get(hash, force, cb) {
var i, block;
if (typeof force === 'function') {
cb = force;
force = false;
@ -413,11 +432,11 @@ Chain.prototype.get = function get(hash, force, cb) {
// Cached block found
if (!force && this.block.bloom.test(hash, 'hex')) {
for (var i = 0; i < this.block.list.length; i++) {
for (i = 0; i < this.block.list.length; i++) {
if (this.block.list[i].hash('hex') === hash) {
// NOTE: we return right after the statement - so `block` should be
// valid at the time of nextTick call
var block = this.block.list[i];
block = this.block.list[i];
bcoin.utils.nextTick(function() {
cb(block);
});
@ -437,6 +456,7 @@ Chain.prototype.isFull = function isFull() {
return false;
var delta = (+new Date() / 1000) - this.index.ts[this.index.ts.length - 1];
return delta < 40 * 60;
};
@ -447,34 +467,42 @@ Chain.prototype.fillPercent = function fillPercent() {
};
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
var ts, hashes, heights, zip, i, count;
if (this.loading) {
this.once('load', function() {
this.hashesInRange(start, end, cb);
});
return;
}
cb = utils.asyncify(cb);
var ts = this.index.ts;
ts = this.index.ts;
start = utils.binaryInsert(ts, start, compareTs, true);
if (start > 0 && ts[start - 1] >= start)
start--;
end = utils.binaryInsert(ts, end, compareTs, true);
// Zip hashes and heights together and sort them by height
var hashes = this.index.hashes.slice(start, end);
var heights = this.index.heights.slice(start, end);
var zip = [];
for (var i = 0; i < hashes.length; i++)
hashes = this.index.hashes.slice(start, end);
heights = this.index.heights.slice(start, end);
zip = [];
for (i = 0; i < hashes.length; i++)
zip.push({ hash: hashes[i], height: heights[i] });
zip = zip.sort(function(a, b) {
return a.height - b.height;
});
var hashes = zip.map(function(a) {
hashes = zip.map(function(a) {
return a.hash;
});
var count = zip[zip.length - 1].height - zip[0].height + 1;
count = zip[zip.length - 1].height - zip[0].height + 1;
return cb(hashes, count);
};
@ -538,14 +566,13 @@ Chain.prototype.locatorHashes = function(start) {
Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {
var self = this;
var root = hash;
if (Array.isArray(hash))
hash = utils.toHex(hash);
else if (hash.hash)
hash = hash.hash('hex');
var root = hash;
while (this.orphan.bmap[hash]) {
root = hash;
hash = this.orphan.bmap[hash].prevBlock;
@ -580,11 +607,17 @@ Chain.prototype.toJSON = function toJSON() {
var lastTs = 0;
var lastHeight = -1000;
for (var i = 0; i < this.index.ts.length - keep; i++) {
var ts = this.index.ts[i];
var delta = ts < 1356984000 ? delta1 :
ts < 1388520000 ? delta2 : delta3;
var hdelta = this.index.heights[i] - lastHeight;
var i, ts, delta, hdelta;
for (i = 0; i < this.index.ts.length - keep; i++) {
ts = this.index.ts[i];
delta = ts < 1356984000
? delta1
: ts < 1388520000 ? delta2 : delta3;
hdelta = this.index.heights[i] - lastHeight;
if (ts - lastTs < delta && hdelta < 250)
continue;
@ -606,13 +639,18 @@ Chain.prototype.toJSON = function toJSON() {
};
Chain.prototype.fromJSON = function fromJSON(json) {
var i;
assert.equal(json.v, 1);
assert.equal(json.type, 'chain');
if (json.network)
assert.equal(json.network, network.type);
this.index.hashes = json.hashes.slice();
this.index.ts = json.ts.slice();
this.index.heights = json.heights.slice();
if (this.index.bloom)
this.index.bloom.reset();
else
@ -621,7 +659,8 @@ Chain.prototype.fromJSON = function fromJSON(json) {
if (this.index.hashes.length === 0)
this.add(new bcoin.block(network.genesis, 'block'));
for (var i = 0; i < this.index.hashes.length; i++) {
for (i = 0; i < this.index.hashes.length; i++)
this.index.bloom.add(this.index.hashes[i], 'hex');
}
};
module.exports = Chain;

View File

@ -96,13 +96,14 @@ HDSeed._entropy = function(size) {
HDSeed._mnemonic = function(entropy) {
var bin = '';
for (var i = 0; i < entropy.length; i++) {
bin = bin + ('00000000' + entropy[i].toString(2)).slice(-8);
}
var mnemonic = [];
var i, wi;
for (i = 0; i < entropy.length; i++)
bin = bin + ('00000000' + entropy[i].toString(2)).slice(-8);
for (i = 0; i < bin.length / 11; i++) {
var wi = parseInt(bin.slice(i * 11, (i + 1) * 11), 2);
wi = parseInt(bin.slice(i * 11, (i + 1) * 11), 2);
mnemonic.push(english[wi]);
}
@ -231,6 +232,7 @@ HDPriv.prototype._unbuild = function(xkey) {
var raw = utils.fromBase58(xkey);
var data = {};
var off = 0;
var hash;
data.version = utils.readU32BE(raw, off);
off += 4;
@ -248,7 +250,7 @@ HDPriv.prototype._unbuild = function(xkey) {
data.checksum = utils.readU32BE(raw, off);
off += 4;
var hash = utils.dsha256(raw.slice(0, -4));
hash = utils.dsha256(raw.slice(0, -4));
if (data.checksum !== utils.readU32BE(hash, 0))
throw new Error('checksum mismatch');
@ -258,6 +260,7 @@ HDPriv.prototype._unbuild = function(xkey) {
HDPriv.prototype._build = function(data) {
var sequence = [];
var off = 0;
var checksum, xprivkey, pair, privateKey, publicKey, size, fingerPrint;
utils.writeU32BE(sequence, data.version, off);
off += 4;
@ -273,18 +276,18 @@ HDPriv.prototype._build = function(data) {
off += 1;
utils.copy(data.privateKey, sequence, off, true);
off += data.privateKey.length;
var checksum = utils.dsha256(sequence).slice(0, 4);
checksum = utils.dsha256(sequence).slice(0, 4);
utils.copy(checksum, sequence, off, true);
off += 4;
var xprivkey = utils.toBase58(sequence);
xprivkey = utils.toBase58(sequence);
var pair = bcoin.ecdsa.keyPair({ priv: data.privateKey });
var privateKey = pair.getPrivate().toArray();
var publicKey = pair.getPublic(true, 'array');
pair = bcoin.ecdsa.keyPair({ priv: data.privateKey });
privateKey = pair.getPrivate().toArray();
publicKey = pair.getPublic(true, 'array');
var size = PARENT_FINGER_PRINT_SIZE;
var fingerPrint = utils.ripesha(publicKey).slice(0, size);
size = PARENT_FINGER_PRINT_SIZE;
fingerPrint = utils.ripesha(publicKey).slice(0, size);
this.version = data.version;
this.depth = data.depth;
@ -307,22 +310,24 @@ HDPriv.prototype.derive = function(index, hard) {
if (typeof index === 'string')
return this.deriveString(index);
var index_ = [];
var data, hash, leftPart, chainCode, privateKey;
hard = index >= HARDENED ? true : hard;
if (index < HARDENED && hard === true)
index += HARDENED;
var index_ = [];
utils.writeU32BE(index_, index, 0);
var data = hard
data = hard
? [0].concat(this.privateKey).concat(index_)
: data = [].concat(this.publicKey).concat(index_);
var hash = sha512hmac(data, this.chainCode);
var leftPart = new bn(hash.slice(0, 32));
var chainCode = hash.slice(32, 64);
hash = sha512hmac(data, this.chainCode);
leftPart = new bn(hash.slice(0, 32));
chainCode = hash.slice(32, 64);
var privateKey = leftPart.add(new bn(this.privateKey)).mod(ec.curve.n).toArray();
privateKey = leftPart.add(new bn(this.privateKey)).mod(ec.curve.n).toArray();
return new HDPriv({
master: this.master,
@ -340,6 +345,7 @@ HDPriv._getIndexes = function(path) {
var steps = path.split('/');
var root = steps.shift();
var indexes = [];
var i, step, hard, index;
if (~PATH_ROOTS.indexOf(path))
return indexes;
@ -347,9 +353,9 @@ HDPriv._getIndexes = function(path) {
if (!~PATH_ROOTS.indexOf(root))
return null;
for (var i = 0; i < steps.length; i++) {
var step = steps[i];
var hard = step[step.length - 1] === '\'';
for (i = 0; i < steps.length; i++) {
step = steps[i];
hard = step[step.length - 1] === '\'';
if (hard)
step = step.slice(0, -1);
@ -357,7 +363,7 @@ HDPriv._getIndexes = function(path) {
if (!step || step[0] === '-')
return null;
var index = +step;
index = +step;
if (hard)
index += HARDENED;
@ -399,18 +405,17 @@ HDPriv.prototype.deriveString = function(path) {
*/
function HDPub(options) {
var data;
if (!(this instanceof HDPub))
return new HDPub(options);
var data;
if (typeof options === 'string' && options.indexOf('xpub') === 0)
options = { xkey: options };
if (options.xkey)
data = this._unbuild(options.xkey);
else
data = options;
data = options.xkey
? this._unbuild(options.xkey)
: options;
data = this._normalize(data, network.prefixes.xpubkey);
@ -425,6 +430,7 @@ HDPub.prototype._unbuild = function(xkey) {
var raw = utils.fromBase58(xkey);
var data = {};
var off = 0;
var hash;
data.version = utils.readU32BE(raw, off);
off += 4;
@ -441,7 +447,7 @@ HDPub.prototype._unbuild = function(xkey) {
data.checksum = utils.readU32BE(raw, off);
off += 4;
var hash = utils.dsha256(raw.slice(0, -4));
hash = utils.dsha256(raw.slice(0, -4));
if (data.checksum !== utils.readU32BE(hash, 0))
throw new Error('checksum mismatch');
@ -451,6 +457,7 @@ HDPub.prototype._unbuild = function(xkey) {
HDPub.prototype._build = function(data) {
var sequence = [];
var off = 0;
var checksum, xpubkey, publicKey, size, fingerPrint;
utils.writeU32BE(sequence, data.version, off);
off += 4;
@ -464,7 +471,7 @@ HDPub.prototype._build = function(data) {
off += data.chainCode.length;
utils.copy(data.publicKey, sequence, off, true);
off += data.publicKey.length;
var checksum = utils.dsha256(sequence).slice(0, 4);
checksum = utils.dsha256(sequence).slice(0, 4);
utils.copy(checksum, sequence, off, true);
off += 4;
@ -473,11 +480,11 @@ HDPub.prototype._build = function(data) {
else if (utils.toHex(checksum) !== utils.toHex(data.checksum))
throw new Error('checksum mismatch');
var xpubkey = utils.toBase58(sequence);
xpubkey = utils.toBase58(sequence);
var publicKey = data.publicKey;
var size = PARENT_FINGER_PRINT_SIZE;
var fingerPrint = utils.ripesha(publicKey).slice(0, size);
publicKey = data.publicKey;
size = PARENT_FINGER_PRINT_SIZE;
fingerPrint = utils.ripesha(publicKey).slice(0, size);
this.version = data.version;
this.depth = data.depth;
@ -495,6 +502,9 @@ HDPub.prototype._build = function(data) {
};
HDPub.prototype.derive = function(index, hard) {
var index_ = [];
var data, hash, leftPart, chainCode, pair, pubkeyPoint, publicKey;
if (typeof index === 'string')
return this.deriveString(index);
@ -504,17 +514,16 @@ HDPub.prototype.derive = function(index, hard) {
if (index < 0)
throw new Error('invalid path');
var index_ = [];
utils.writeU32BE(index_, index, 0);
var data = [].concat(this.publicKey).concat(index_);
var hash = sha512hmac(data, this.chainCode);
var leftPart = new bn(hash.slice(0, 32));
var chainCode = hash.slice(32, 64);
data = [].concat(this.publicKey).concat(index_);
hash = sha512hmac(data, this.chainCode);
leftPart = new bn(hash.slice(0, 32));
chainCode = hash.slice(32, 64);
var pair = bcoin.ecdsa.keyPair({ pub: this.publicKey });
var pubkeyPoint = ec.curve.g.mul(leftPart).add(pair.pub);
var publicKey = bcoin.ecdsa.keyFromPublic(pubkeyPoint).getPublic(true, 'array');
pair = bcoin.ecdsa.keyPair({ pub: this.publicKey });
pubkeyPoint = ec.curve.g.mul(leftPart).add(pair.pub);
publicKey = bcoin.ecdsa.keyFromPublic(pubkeyPoint).getPublic(true, 'array');
return new HDPub({
// version: network.prefixes.xpubkey,
@ -610,8 +619,8 @@ function sha512hmac(data, salt) {
function randomBytes(size) {
if (isBrowser) {
var a = Uint8Array(size);
(window.crypto || window.msCrypto).getRandomValues(a);
var buf = new Array(size);
(window.crypto || window.msCrypto).getRandomValues(a);
utils.copy(a, buf, 0);
return buf;
}
@ -629,6 +638,7 @@ function pbkdf2(key, salt, iterations, dkLen) {
'use strict';
var hLen = 64;
if (dkLen > (Math.pow(2, 32) - 1) * hLen)
throw Error('Requested key length too long');
@ -652,9 +662,11 @@ function pbkdf2(key, salt, iterations, dkLen) {
var l = Math.ceil(dkLen / hLen);
var r = dkLen - (l - 1) * hLen;
var i, j, k, destPos, len;
utils.copy(salt.slice(0, salt.length), block1, 0);
for (var i = 1; i <= l; i++) {
for (i = 1; i <= l; i++) {
block1[salt.length + 0] = (i >> 24 & 0xff);
block1[salt.length + 1] = (i >> 16 & 0xff);
block1[salt.length + 2] = (i >> 8 & 0xff);
@ -664,15 +676,15 @@ function pbkdf2(key, salt, iterations, dkLen) {
utils.copy(U.slice(0, hLen), T, 0);
for (var j = 1; j < iterations; j++) {
for (j = 1; j < iterations; j++) {
U = sha512hmac(U, key);
for (var k = 0; k < hLen; k++)
for (k = 0; k < hLen; k++)
T[k] ^= U[k];
}
var destPos = (i - 1) * hLen;
var len = (i === l ? r : hLen);
destPos = (i - 1) * hLen;
len = (i === l ? r : hLen);
utils.copy(T.slice(0, len), DK, 0);
}

View File

@ -13,6 +13,8 @@ try {
}
function Peer(pool, createSocket, options) {
var self = this;
if (!(this instanceof Peer))
return new Peer(pool, createSocket, options);
@ -31,7 +33,6 @@ function Peer(pool, createSocket, options) {
this.ts = this.options.ts || 0;
if (this.options.backoff) {
var self = this;
setTimeout(function() {
self.socket = createSocket(self);
self.emit('socket');
@ -65,26 +66,32 @@ function Peer(pool, createSocket, options) {
else
this.once('socket', this._init);
}
inherits(Peer, EventEmitter);
module.exports = Peer;
Peer.prototype._init = function init() {
var self = this;
this.socket.once('connect', function() {
self.ts = Date.now() / 1000 | 0;
});
this.socket.once('error', function(err) {
self._error(err);
});
this.socket.once('close', function() {
self._error('socket hangup');
});
this.socket.on('data', function(chunk) {
self.parser.feed(chunk);
});
this.parser.on('packet', function(packet) {
self._onPacket(packet);
});
this.parser.on('error', function(err) {
self._error(err);
});
@ -134,13 +141,16 @@ Peer.prototype.startSync = function startSync() {
};
Peer.prototype.broadcast = function broadcast(items) {
var self = this;
var result;
if (this.destroyed)
return;
if (!Array.isArray(items))
items = [ items ];
var self = this;
var result = items.map(function(item) {
result = items.map(function(item) {
var key = item.hash('hex');
var old = this._broadcast.map[key];
if (old) {
@ -195,8 +205,11 @@ Peer.prototype.updateWatch = function updateWatch() {
};
Peer.prototype.destroy = function destroy() {
var i;
if (this.destroyed)
return;
if (!this.socket)
return this.once('socket', this.destroy);
@ -214,7 +227,7 @@ Peer.prototype.destroy = function destroy() {
clearInterval(this._ping.timer);
this._ping.timer = null;
for (var i = 0; i < this._request.queue.length; i++)
for (i = 0; i < this._request.queue.length; i++)
clearTimeout(this._request.queue[i].timer);
};
@ -223,11 +236,13 @@ Peer.prototype.destroy = function destroy() {
Peer.prototype._write = function write(chunk) {
if (this.destroyed)
return;
if (!this.socket) {
return this.once('socket', function() {
this._write(chunk);
});
}
if (NodeBuffer)
this.socket.write(new NodeBuffer(chunk));
else
@ -237,15 +252,17 @@ Peer.prototype._write = function write(chunk) {
Peer.prototype._error = function error(err) {
if (this.destroyed)
return;
this.destroy();
this.emit('error', typeof err === 'string' ? new Error(err) : err);
};
Peer.prototype._req = function _req(cmd, cb) {
var self = this;
if (this.destroyed)
return cb(new Error('Destroyed, sorry'));
var self = this;
var entry = {
cmd: cmd,
cb: cb,
@ -258,19 +275,24 @@ Peer.prototype._req = function _req(cmd, cb) {
},
timer: null
};
entry.timer = setTimeout(entry.ontimeout, this._request.timeout);
this._request.queue.push(entry);
return entry;
};
Peer.prototype._res = function _res(cmd, payload) {
for (var i = 0; i < this._request.queue.length; i++) {
var entry = this._request.queue[i];
var i, entry, res;
for (i = 0; i < this._request.queue.length; i++) {
entry = this._request.queue[i];
if (!entry || entry.cmd && entry.cmd !== cmd)
return false;
var res = entry.cb(null, payload, cmd);
res = entry.cb(null, payload, cmd);
if (res === this._request.cont) {
assert(!entry.cmd);
@ -388,6 +410,7 @@ Peer.prototype._handleGetAddr = function handleGetAddr() {
this.pool.peers.block,
this.pool.peers.load
).filter(Boolean);
var addrs;
// NOTE: For IPv6 BTC uses:
// '0000:0000:0000:0000:0000:xxxx:xxxx:ffff'
@ -417,7 +440,7 @@ Peer.prototype._handleGetAddr = function handleGetAddr() {
};
});
var addrs = peers.map(function(peer) {
addrs = peers.map(function(peer) {
if (peer.ver === 6) {
while (peer.ipv6.split(':').length < 8)
peer.ipv6 = '0000:' + peer.ipv6;
@ -450,16 +473,18 @@ Peer.prototype._handleInv = function handleInv(items) {
return item.hash;
});
var req, i, block, hash;
if (blocks.length === 1)
this.bestBlock = utils.toHex(blocks[0]);
this.emit('blocks', blocks);
if (this.pool.options.fullNode) {
var req = [];
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
var hash = utils.toHex(block);
req = [];
for (i = 0; i < blocks.length; i++) {
block = blocks[i];
hash = utils.toHex(block);
if (this.chain.hasOrphan(hash)) {
this.loadBlocks(this.chain.locatorHashes(), this.chain.getOrphanRoot(hash));
continue;
@ -507,3 +532,5 @@ Peer.prototype.loadHeaders = function loadHeaders(hashes, stop) {
Peer.prototype.loadBlocks = function loadBlocks(hashes, stop) {
this._write(this.framer.getBlocks(hashes, stop));
};
module.exports = Peer;

View File

@ -29,10 +29,12 @@ function Pool(options) {
this.size = options.size || 32;
this.parallel = options.parallel || 2000;
this.redundancy = options.redundancy || 2;
this.backoff = {
delta: options.backoffDelta || 500,
max: options.backoffMax || 5000
};
this.load = {
timeout: options.loadTimeout || 3000,
interval: options.loadInterval || 5000,
@ -44,8 +46,10 @@ function Pool(options) {
hwm: options.hwm || this.parallel * 8,
hiReached: false
};
this.maxRetries = options.maxRetries || 42;
this.requestTimeout = options.requestTimeout || 10000;
this.chain = new bcoin.chain({
storage: this.storage,
// Since regular blocks contain transactions and full merkle
@ -54,10 +58,14 @@ function Pool(options) {
fullNode: this.options.fullNode,
startHeight: this.options.startHeight
});
this.watchMap = {};
this.bloom = new bcoin.bloom(8 * 1024,
10,
(Math.random() * 0xffffffff) | 0);
this.bloom = new bcoin.bloom(
8 * 1024,
10,
(Math.random() * 0xffffffff) | 0
);
this.bestHeight = 0;
this.bestBlock = null;
@ -72,14 +80,17 @@ function Pool(options) {
// Peers that are loading block ids
load: null
};
this.block = {
lastHash: null
};
this.request = {
map: {},
active: 0,
queue: []
};
this.validate = {
// 5 days scan delta for obtaining TXs
delta: 5 * 24 * 3600,
@ -117,15 +128,18 @@ function Pool(options) {
});
}
}
inherits(Pool, EventEmitter);
module.exports = Pool;
Pool.prototype._init = function _init() {
var self = this;
var i;
this._addLoader();
for (var i = 0; i < this.size; i++)
for (i = 0; i < this.size; i++)
this._addPeer(0);
this._load();
this.chain.on('missing', function(hash, preload, parent) {
@ -141,19 +155,22 @@ Pool.prototype._init = function _init() {
};
Pool.prototype._addLoader = function _addLoader() {
var self = this;
var peer, interval, timer;
if (this.destroyed)
return;
if (this.peers.load !== null)
return;
var peer = new bcoin.peer(this, this.createSocket, {
peer = new bcoin.peer(this, this.createSocket, {
backoff: 750 * Math.random(),
startHeight: this.options.startHeight,
relay: this.options.relay
});
this.peers.load = peer;
var self = this;
peer.on('error', function(err) {
self.emit('error', err, peer);
});
@ -168,7 +185,7 @@ Pool.prototype._addLoader = function _addLoader() {
self._addLoader();
}
var interval = setInterval(function() {
interval = setInterval(function() {
self._load();
}, this.load.interval);
@ -178,7 +195,8 @@ Pool.prototype._addLoader = function _addLoader() {
clearTimeout(timer);
});
if (this.options.fullNode) return;
if (this.options.fullNode)
return;
function destroy() {
// Chain is full and up-to-date
@ -191,7 +209,8 @@ Pool.prototype._addLoader = function _addLoader() {
peer.destroy();
}
var timer = setTimeout(destroy, this.load.timeout);
timer = setTimeout(destroy, this.load.timeout);
// Split blocks and request them using multiple peers
peer.on('blocks', function(hashes) {
@ -236,7 +255,11 @@ Pool.prototype.isFull = function isFull() {
};
Pool.prototype._loadRange = function _loadRange(hashes, force) {
if (this.options.fullNode) return;
var now = +new Date();
var last;
if (this.options.fullNode)
return;
if (!hashes)
return;
@ -245,29 +268,33 @@ Pool.prototype._loadRange = function _loadRange(hashes, force) {
return;
// Limit number of requests
var now = +new Date();
if (!force && now - this.load.lastRange < this.load.rangeWindow)
return;
this.load.lastRange = now;
if (!this.peers.load)
this._addLoader();
var last = hashes[hashes.length - 1];
last = hashes[hashes.length - 1];
hashes.slice(0, -1).forEach(function(hash) {
this.peers.load.loadBlocks([ hash ], last);
}, this);
};
Pool.prototype._load = function _load() {
if (this.options.fullNode) return;
var self = this;
if (this.options.fullNode)
return;
if (this.request.queue.length >= this.load.hwm) {
this.load.hiReached = true;
return false;
}
this.load.hiReached = false;
var self = this;
// Load more blocks, starting from last hash
if (this.block.lastHash)
@ -286,12 +313,16 @@ Pool.prototype._load = function _load() {
};
Pool.prototype._addPeer = function _addPeer(backoff) {
var self = this;
var peer;
if (this.destroyed)
return;
if (this.peers.block.length + this.peers.pending.length >= this.size)
return;
var peer = new bcoin.peer(this, this.createSocket, {
peer = new bcoin.peer(this, this.createSocket, {
backoff: backoff,
startHeight: this.options.startHeight,
relay: this.options.relay
@ -302,7 +333,6 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
peer._retry = 0;
// Create new peer on failure
var self = this;
peer.on('error', function(err) {
self.emit('error', err, peer);
});
@ -315,10 +345,12 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
});
peer.once('ack', function() {
var i;
if (self.destroyed)
return;
var i = self.peers.pending.indexOf(peer);
i = self.peers.pending.indexOf(peer);
if (i !== -1) {
self.peers.pending.splice(i, 1);
self.peers.block.push(peer);
@ -349,6 +381,9 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
});
} else {
peer.on('block', function(block) {
var hashes = self.chain.index.hashes;
var hash, len, orphan, err;
if (self.syncPeer !== peer)
return;
@ -356,11 +391,11 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
self._response(block);
var hash = block.hash('hex');
var len = self.chain.index.hashes.length;
var orphan = self.chain.hasOrphan(block);
hash = block.hash('hex');
len = hashes.length;
orphan = self.chain.hasOrphan(block);
var err = self.chain.add(block);
err = self.chain.add(block);
if (err)
self.emit('chain-error', err, peer);
@ -373,12 +408,10 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
return;
}
if (self.chain.index.hashes.length === len)
if (hashes.length === len)
return;
var top = self.chain.index.hashes[self.chain.index.hashes.length - 1];
self.needSync = top !== self.bestBlock;
self.needSync = hashes[hashes.length - 1] !== self.bestBlock;
self.emit('chain-progress', self.chain.fillPercent(), peer);
self.emit('block', block, peer);
@ -386,9 +419,9 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
}
this.chain.on('fork', function(height, hash, checkpoint) {
if (!self.syncPeer)
return;
var peer = self.syncPeer;
if (!peer)
return;
delete self.syncPeer;
peer.destroy();
self.startSync();
@ -450,6 +483,7 @@ Pool.prototype.bestPeer = function bestPeer() {
this.peers.block.forEach(function(peer) {
if (!peer.version || !peer.socket)
return;
if (!best || peer.version.height > best.version.height)
best = peer;
});
@ -495,13 +529,15 @@ Pool.prototype._removePeer = function _removePeer(peer) {
};
Pool.prototype.watch = function watch(id) {
var hid, i;
if (id instanceof bcoin.wallet) {
this.watchWallet(id);
return;
}
if (id) {
var hid = utils.toHex(id);
hid = utils.toHex(id);
if (this.watchMap[hid])
this.watchMap[hid]++;
else
@ -515,11 +551,14 @@ Pool.prototype.watch = function watch(id) {
if (this.peers.load)
this.peers.load.updateWatch();
for (var i = 0; i < this.peers.block.length; i++)
for (i = 0; i < this.peers.block.length; i++)
this.peers.block[i].updateWatch();
};
Pool.prototype.unwatch = function unwatch(id) {
var i;
if (!this.bloom.test(id, 'hex'))
return;
@ -538,17 +577,21 @@ Pool.prototype.unwatch = function unwatch(id) {
// Resend it to peers
if (this.peers.load)
this.peers.load.updateWatch();
for (var i = 0; i < this.peers.block.length; i++)
for (i = 0; i < this.peers.block.length; i++)
this.peers.block[i].updateWatch();
};
Pool.prototype.addWallet = function addWallet(w, defaultTs) {
var self = this;
var e;
if (this.wallets.indexOf(w) !== -1)
return false;
this.watchWallet(w);
var self = this;
var e = new EventEmitter();
e = new EventEmitter();
if (w.loaded)
search(w.lastTs);
else
@ -606,16 +649,19 @@ Pool.prototype.unwatchWallet = function unwatchWallet(w) {
};
Pool.prototype.search = function search(id, range, e) {
var self = this;
e = e || new EventEmitter();
// Optional id argument
if (id !== null &&
typeof id === 'object' &&
!Array.isArray(id) ||
typeof id === 'number') {
if (id !== null
&& typeof id === 'object'
&& !Array.isArray(id)
|| typeof id === 'number') {
range = id;
id = null;
}
if (typeof id === 'string')
id = utils.toArray(id, 'hex');
@ -633,7 +679,6 @@ Pool.prototype.search = function search(id, range, e) {
if (!range.start)
range.start = +new Date() / 1000 - 432000;
var self = this;
this.chain.hashesInRange(range.start, range.end, function(hashes, count) {
var waiting = count;
@ -691,6 +736,8 @@ Pool.prototype.search = function search(id, range, e) {
};
Pool.prototype._request = function _request(type, hash, options, cb) {
var self = this;
// Optional `force`
if (typeof options === 'function') {
cb = options;
@ -703,8 +750,6 @@ Pool.prototype._request = function _request(type, hash, options, cb) {
if (this.request.map[hash])
return this.request.map[hash].addCallback(cb);
var self = this;
// Block should be not in chain, or be requested
if (!options.force && type === 'block')
return this.chain.has(hash, true, next);
@ -732,8 +777,11 @@ Pool.prototype._response = function _response(entity) {
};
Pool.prototype._scheduleRequests = function _scheduleRequests() {
var self = this;
if (this.destroyed)
return;
if (this.request.active > this.parallel / 2)
return;
@ -749,7 +797,6 @@ Pool.prototype._scheduleRequests = function _scheduleRequests() {
if (this.load.timer !== null)
return;
var self = this;
this.load.timer = setTimeout(function() {
self.load.timer = null;
self._doRequests();
@ -757,6 +804,9 @@ Pool.prototype._scheduleRequests = function _scheduleRequests() {
};
Pool.prototype._doRequests = function _doRequests() {
var queue, above, items, below;
var red, count, split, i, off, req, j;
if (this.request.active >= this.parallel)
return;
@ -764,11 +814,11 @@ Pool.prototype._doRequests = function _doRequests() {
if (this.peers.block.length === 0)
return;
var queue = this.request.queue;
var above = queue.length >= this.load.lwm;
var items = queue.slice(0, this.parallel - this.request.active);
queue = this.request.queue;
above = queue.length >= this.load.lwm;
items = queue.slice(0, this.parallel - this.request.active);
this.request.queue = queue.slice(items.length);
var below = this.request.queue.length < this.load.lwm;
below = this.request.queue.length < this.load.lwm;
// Watermark boundary crossed, load more blocks
if (above && below && this.load.hiReached)
@ -779,19 +829,20 @@ Pool.prototype._doRequests = function _doRequests() {
}
// Split list between peers
var red = this.redundancy;
var count = this.peers.block.length;
var split = Math.ceil(items.length * red / count);
for (var i = 0, off = 0; i < count; i += red, off += split) {
var req = items.slice(off, off + split).map(mapReq, this);
for (var j = 0; j < red && i + j < count; j++) {
red = this.redundancy;
count = this.peers.block.length;
split = Math.ceil(items.length * red / count);
for (i = 0, off = 0; i < count; i += red, off += split) {
req = items.slice(off, off + split).map(mapReq, this);
for (j = 0; j < red && i + j < count; j++)
this.peers.block[i + j].getData(req);
}
}
};
Pool.prototype.getTX = function getTX(hash, range, cb) {
var self = this;
var cbs, tx, finished, req, delta;
hash = utils.toHex(hash);
if (typeof range === 'function') {
@ -802,19 +853,20 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
// Do not perform duplicate searches
if (this.validate.map[hash])
return this.validate.map[hash].push(cb);
var cbs = [ cb ];
cbs = [ cb ];
this.validate.map[hash] = cbs;
// Add request without queueing it to get notification at the time of load
var tx = null;
var finished = false;
var req = this._request('tx', hash, { noQueue: true }, function(t) {
tx = null;
finished = false;
req = this._request('tx', hash, { noQueue: true }, function(t) {
finished = true;
tx = t;
});
// Do incremental search until the TX is found
var delta = this.validate.delta;
delta = this.validate.delta;
// Start from the existing range if given
if (range)
@ -822,9 +874,6 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
else
range = { start: (+new Date() / 1000) - delta, end: 0 };
var self = this;
doSearch();
function doSearch() {
var e = self.search(hash, range);
e.on('end', function(empty) {
@ -849,12 +898,14 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
doSearch();
});
}
doSearch();
};
Pool.prototype.sendTX = function sendTX(tx) {
var self = this;
var e = new EventEmitter();
var self = this;
var entry = {
tx: tx,
e: e,
@ -864,6 +915,7 @@ Pool.prototype.sendTX = function sendTX(tx) {
self.tx.list.splice(i, 1);
}, this.tx.timeout)
};
this.tx.list.push(entry);
this.peers.block.forEach(function(peer) {
@ -880,6 +932,7 @@ Pool.prototype.sendTX = function sendTX(tx) {
Pool.prototype.destroy = function destroy() {
if (this.destroyed)
return;
this.destroyed = true;
if (this.peers.load)
@ -888,18 +941,23 @@ Pool.prototype.destroy = function destroy() {
this.request.queue.slice().forEach(function(item) {
item.finish(null);
});
this.tx.list.forEach(function(tx) {
clearTimeout(tx.timer);
tx.timer = null;
});
this.peers.pending.slice().forEach(function(peer) {
peer.destroy();
});
this.peers.block.slice().forEach(function(peer) {
peer.destroy();
});
if (this.load.timer)
clearTimeout(this.load.timer);
this.load.timer = null;
};
@ -920,6 +978,8 @@ Pool.prototype.fromJSON = function fromJSON(json) {
};
function LoadRequest(pool, type, hash, cb) {
var self = this;
this.pool = pool;
this.type = type;
this.hash = hash;
@ -930,7 +990,6 @@ function LoadRequest(pool, type, hash, cb) {
this.active = false;
this.noQueue = false;
var self = this;
this.onclose = function onclose() {
if (self.pool.destroyed)
self.clear();
@ -941,8 +1000,9 @@ function LoadRequest(pool, type, hash, cb) {
LoadRequest.prototype.start = function start(peer) {
var self = this;
assert(!this.active);
var reqType;
assert(!this.active);
this.active = true;
this.pool.request.active++;
@ -956,7 +1016,6 @@ LoadRequest.prototype.start = function start(peer) {
this.peer = peer;
this.peer.once('close', this.onclose);
var reqType;
if (this.type === 'block')
reqType = 'filtered';
else if (this.type === 'tx')
@ -991,9 +1050,11 @@ LoadRequest.prototype.clear = function clear() {
};
LoadRequest.prototype.retry = function retry() {
var peer = this.peer;
// Put block into the queue, ensure that the queue is always sorted by ts
utils.binaryInsert(this.pool.request.queue, this, LoadRequest.compare);
var peer = this.peer;
this.clear();
// Kill peer, if it misbehaves
@ -1005,12 +1066,14 @@ LoadRequest.prototype.retry = function retry() {
};
LoadRequest.prototype.finish = function finish(entity) {
var index;
if (this.active) {
this.clear();
} else {
// It could be that request was never sent to the node, remove it from
// queue and forget about it
var index = this.pool.request.queue.indexOf(this);
index = this.pool.request.queue.indexOf(this);
assert(index !== -1 || this.noQueue);
if (!this.noQueue)
this.pool.request.queue.splice(index, 1);
@ -1031,3 +1094,5 @@ LoadRequest.prototype.addCallback = function addCallback(cb) {
if (cb)
this.cbs.push(cb);
};
module.exports = Pool;

View File

@ -1,6 +1,8 @@
var bcoin = require('../../bcoin');
var utils = bcoin.utils;
var i;
exports.minVersion = 70001;
exports.version = 70002;
@ -119,10 +121,11 @@ exports.opcodes = {
};
exports.opcodes['-1'] = 0x50 + -1;
for (var i = 1; i <= 16; i++)
for (i = 1; i <= 16; i++)
exports.opcodes[i] = 0x50 + i;
for (var i = 0; i <= 7; i++)
for (i = 0; i <= 7; i++)
exports.opcodes['nop' + (i + 3)] = 0xb2 + i;
exports.opcodesByVal = new Array(256);

View File

@ -19,20 +19,20 @@ function Framer(options) {
this.agent = utils.toArray(options.agent || '/bcoin:' + version + '/');
this.agent = this.agent.slice(0, 0xfc);
}
module.exports = Framer;
Framer.prototype.header = function header(cmd, payload) {
var h = new Array(24);
var len, i;
assert(cmd.length < 12);
assert(payload.length <= 0xffffffff);
var h = new Array(24);
// Magic value
writeU32(h, network.magic, 0);
// Command
var len = writeAscii(h, cmd, 4);
for (var i = 4 + len; i < 4 + 12; i++)
len = writeAscii(h, cmd, 4);
for (i = 4 + len; i < 4 + 12; i++)
h[i] = 0;
// Payload length
@ -64,6 +64,7 @@ Framer.prototype._addr = function addr(buf, off) {
Framer.prototype.version = function version(packet) {
var p = new Array(86 + this.agent.length);
var off = 0;
var ts, i;
if (!packet)
packet = {};
@ -76,7 +77,7 @@ Framer.prototype.version = function version(packet) {
off += writeU32(p, 0, off);
// Timestamp
var ts = ((+new Date()) / 1000) | 0;
ts = ((+new Date()) / 1000) | 0;
off += writeU32(p, ts, off);
off += writeU32(p, 0, off);
@ -94,7 +95,7 @@ Framer.prototype.version = function version(packet) {
p[off++] = 0;
} else {
off += varint(p, this.agent.length, off);
for (var i = 0; i < this.agent.length; i++)
for (i = 0; i < this.agent.length; i++)
p[off++] = this.agent[i];
}
@ -139,14 +140,16 @@ function varint(arr, value, off) {
Framer.prototype._inv = function _inv(command, items) {
var res = [];
var off = varint(res, items.length, 0);
var i, hash;
assert(items.length <= 50000);
for (var i = 0; i < items.length; i++) {
for (i = 0; i < items.length; i++) {
// Type
off += writeU32(res, constants.inv[items[i].type], off);
// Hash
var hash = items[i].hash;
hash = items[i].hash;
if (typeof hash === 'string')
hash = utils.toArray(hash, 'hex');
assert.equal(hash.length, 32);
@ -181,10 +184,10 @@ Framer.prototype.pong = function pong(nonce) {
Framer.prototype.filterLoad = function filterLoad(bloom, update) {
var filter = bloom.toArray();
var before = [];
varint(before, filter.length, 0);
var after = new Array(9);
varint(before, filter.length, 0);
// Number of hash functions
writeU32(after, bloom.n, 0);
@ -194,8 +197,7 @@ Framer.prototype.filterLoad = function filterLoad(bloom, update) {
// nFlags
after[8] = constants.filterFlags[update];
var r = this.packet('filterload', before.concat(filter, after));
return r;
return this.packet('filterload', before.concat(filter, after));
};
Framer.prototype.filterClear = function filterClear() {
@ -212,28 +214,36 @@ Framer.prototype.getBlocks = function getBlocks(hashes, stop) {
Framer.prototype._getBlocks = function _getBlocks(cmd, hashes, stop) {
var p = [];
var off, i, hash, len;
writeU32(p, constants.version, 0);
var off = 4 + varint(p, hashes.length, 4);
off = 4 + varint(p, hashes.length, 4);
p.length = off + 32 * (hashes.length + 1);
for (var i = 0; i < hashes.length; i++) {
var hash = hashes[i];
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
if (typeof hash === 'string')
hash = utils.toArray(hash, 'hex');
var len = utils.copy(hash, p, off);
len = utils.copy(hash, p, off);
for (; len < 32; len++)
p[off + len] = 0;
off += len;
}
if (stop) {
stop = utils.toArray(stop, 'hex');
var len = utils.copy(stop, p, off);
len = utils.copy(stop, p, off);
} else {
var len = 0;
len = 0;
}
for (; len < 32; len++)
p[off + len] = 0;
assert.equal(off + len, p.length);
return this.packet(cmd, p);
@ -241,16 +251,18 @@ Framer.prototype._getBlocks = function _getBlocks(cmd, hashes, stop) {
Framer.tx = function tx(tx) {
var p = [];
var off = writeU32(p, tx.version, 0);
var off, i, input, s, output, value, j;
off = writeU32(p, tx.version, 0);
off += varint(p, tx.inputs.length, off);
for (var i = 0; i < tx.inputs.length; i++) {
var input = tx.inputs[i];
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
off += utils.copy(utils.toArray(input.out.hash, 'hex'), p, off, true);
off += writeU32(p, input.out.index, off);
var s = bcoin.script.encode(input.script);
s = bcoin.script.encode(input.script);
off += varint(p, s.length, off);
off += utils.copy(s, p, off, true);
@ -258,17 +270,19 @@ Framer.tx = function tx(tx) {
}
off += varint(p, tx.outputs.length, off);
for (var i = 0; i < tx.outputs.length; i++) {
var output = tx.outputs[i];
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
// Put LE value
var value = output.value.toArray().slice().reverse();
value = output.value.toArray().slice().reverse();
assert(value.length <= 8);
off += utils.copy(value, p, off, true);
for (var j = value.length; j < 8; j++, off++)
for (j = value.length; j < 8; j++, off++)
p[off] = 0;
var s = bcoin.script.encode(output.script);
s = bcoin.script.encode(output.script);
off += varint(p, s.length, off);
off += utils.copy(s, p, off, true);
}
@ -356,15 +370,15 @@ Framer.prototype.merkleBlock = function merkleBlock(block) {
Framer.prototype.addr = function addr(peers) {
var p = [];
var i = 0;
var off = 0;
var peer;
var start = (Date.now() / 1000 | 0) - process.uptime();
var i;
// count
off += varint(p, peers.length, off);
for (; i < peers.length; i++) {
for (i = 0; i < peers.length; i++) {
peer = peers[i];
// timestamp
@ -387,3 +401,5 @@ Framer.prototype.addr = function addr(peers) {
return this.packet('addr', p);
};
module.exports = Framer;

View File

@ -6,6 +6,7 @@ var utils = bcoin.utils;
*/
var network = exports;
var main, testnet;
network.set = function(type) {
var net = network[type];
@ -16,7 +17,7 @@ network.set = function(type) {
* Main
*/
var main = network.main = {};
main = network.main = {};
main.prefixes = {
pubkey: 0,
@ -103,7 +104,7 @@ main.preload = require('./preload');
* https://en.bitcoin.it/wiki/Testnet
*/
var testnet = network.testnet = {};
testnet = network.testnet = {};
testnet.type = 'testnet';

View File

@ -22,20 +22,28 @@ function Parser() {
this.waiting = 24;
this.packet = null;
}
inherits(Parser, EventEmitter);
module.exports = Parser;
Parser.prototype._error = function _error(str) {
this.emit('error', new Error(str));
};
Parser.prototype.feed = function feed(data) {
var chunk, i, off, len;
this.pendingTotal += data.length;
this.pending.push(data);
while (this.pendingTotal >= this.waiting) {
// Concat chunks
var chunk = new Array(this.waiting);
for (var i = 0, off = 0, len = 0; off < chunk.length; i++) {
chunk = new Array(this.waiting);
i = 0;
off = 0;
len = 0;
for (; off < chunk.length; i++) {
len = utils.copy(this.pending[0], chunk, off);
if (len === this.pending[0].length)
this.pending.shift();
@ -45,6 +53,7 @@ Parser.prototype.feed = function feed(data) {
this.pending[0] = this.pending[0].slice(len);
off += len;
}
assert.equal(off, chunk.length);
// Slice buffers
@ -56,32 +65,39 @@ Parser.prototype.feed = function feed(data) {
Parser.prototype.parse = function parse(chunk) {
if (this.packet === null) {
this.packet = this.parseHeader(chunk) || {};
} else {
this.packet.payload = chunk;
if (readU32(utils.checksum(this.packet.payload)) !== this.packet.checksum)
return this._error('Invalid checksum');
this.packet.payload = this.parsePayload(this.packet.cmd,
this.packet.payload);
if (this.packet.payload)
this.emit('packet', this.packet);
this.waiting = 24;
this.packet = null;
return;
}
this.packet.payload = chunk;
if (readU32(utils.checksum(this.packet.payload)) !== this.packet.checksum)
return this._error('Invalid checksum');
this.packet.payload = this.parsePayload(this.packet.cmd,
this.packet.payload);
if (this.packet.payload)
this.emit('packet', this.packet);
this.waiting = 24;
this.packet = null;
};
Parser.prototype.parseHeader = function parseHeader(h) {
var magic = readU32(h, 0);
var i, magic, cmd;
magic = readU32(h, 0);
if (magic !== network.magic) {
return this._error('Invalid magic value: ' + magic.toString(16));
}
// Count length of the cmd
for (var i = 0; h[i + 4] !== 0 && i < 12; i++);
for (i = 0; h[i + 4] !== 0 && i < 12; i++);
if (i === 12)
return this._error('Not NULL-terminated cmd');
var cmd = utils.stringify(h.slice(4, 4 + i));
cmd = utils.stringify(h.slice(4, 4 + i));
this.waiting = readU32(h, 16);
return {
@ -113,30 +129,32 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) {
};
Parser.prototype.parseVersion = function parseVersion(p) {
var v, services, ts, nonce, result, off, agent, height, relay;
if (p.length < 85)
return this._error('version packet is too small');
var v = readU32(p, 0);
var services = readU64(p, 4);
v = readU32(p, 0);
services = readU64(p, 4);
// Timestamp
var ts = readU64(p, 12);
ts = readU64(p, 12);
// Nonce, very dramatic
var nonce = { lo: readU32(p, 72), hi: readU32(p, 76) };
nonce = { lo: readU32(p, 72), hi: readU32(p, 76) };
// User agent length
var result = readIntv(p, 80);
var off = result.off;
var agent = p.slice(off, off + result.r);
result = readIntv(p, 80);
off = result.off;
agent = p.slice(off, off + result.r);
off += result.r;
// Start height
var height = readU32(p, off);
height = readU32(p, off);
off += 4;
// Relay
var relay = p.length > off ? p[off] === 1 : true;
relay = p.length > off ? p[off] === 1 : true;
return {
v: v,
@ -150,10 +168,11 @@ Parser.prototype.parseVersion = function parseVersion(p) {
};
function readIntv(p, off) {
var r, bytes;
if (!off)
off = 0;
var r, bytes;
if (p[off] < 0xfd) {
r = p[off];
bytes = 1;
@ -172,45 +191,54 @@ function readIntv(p, off) {
}
Parser.prototype.parseInvList = function parseInvList(p) {
var count = readIntv(p, 0);
var items = [];
var off = 0;
var i, count;
count = readIntv(p, 0);
p = p.slice(count.off);
count = count.r;
if (p.length < count * 36)
return this._error('Invalid getdata size');
var items = [];
for (var i = 0, off = 0; i < count; i++, off += 36) {
for (i = 0; i < count; i++, off += 36) {
items.push({
type: constants.invByVal[readU32(p, off)],
hash: p.slice(off + 4, off + 36)
});
}
return items;
};
Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) {
var i, hashCount, off, hashes, flagCount, flags;
if (p.length < 86)
return this._error('Invalid merkleblock size');
var hashCount = readIntv(p, 84);
var off = hashCount.off;
hashCount = readIntv(p, 84);
off = hashCount.off;
hashCount = hashCount.r;
if (off + 32 * hashCount + 1 > p.length)
return this._error('Invalid hash count');
var hashes = new Array(hashCount);
for (var i = 0; i < hashCount; i++)
hashes = new Array(hashCount);
for (i = 0; i < hashCount; i++)
hashes[i] = p.slice(off + i * 32, off + (i + 1) * 32);
off = off + 32 * hashCount;
var flagCount = readIntv(p, off);
flagCount = readIntv(p, off);
off = flagCount.off;
flagCount = flagCount.r;
if (off + flagCount > p.length)
return this._error('Invalid flag count');
var flags = p.slice(off, off + flagCount);
flags = p.slice(off, off + flagCount);
return {
version: readU32(p, 0),
@ -228,19 +256,20 @@ Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) {
};
Parser.prototype.parseHeaders = function parseHeaders(p) {
var headers = [];
var i, result, off, count, header, start, r;
if (p.length < 81)
return this._error('Invalid headers size');
var result = readIntv(p, 0);
var off = result.off;
var count = result.r;
var headers = [];
result = readIntv(p, 0);
off = result.off;
count = result.r;
if (p.length >= off + 81) {
for (var i = 0; i < count && off + 81 < p.length; i++) {
var header = {};
var start = off;
for (i = 0; i < count && off + 81 < p.length; i++) {
header = {};
start = off;
header.version = readU32(p, off);
off += 4;
header.prevBlock = p.slice(off, off + 32);
@ -253,7 +282,7 @@ Parser.prototype.parseHeaders = function parseHeaders(p) {
off += 4;
header.nonce = readU32(p, off);
off += 4;
var r = readIntv(p, off);
r = readIntv(p, off);
header.totalTX = r.r;
off = r.off;
header._raw = p.slice(start, start + 80);
@ -265,17 +294,19 @@ Parser.prototype.parseHeaders = function parseHeaders(p) {
};
Parser.prototype.parseBlock = function parseBlock(p) {
var txs = [];
var i, result, off, totalTX, tx;
if (p.length < 81)
return this._error('Invalid block size');
var result = readIntv(p, 80);
var off = result.off;
var totalTX = result.r;
var txs = [];
result = readIntv(p, 80);
off = result.off;
totalTX = result.r;
if (p.length >= off + 10) {
for (var i = 0; i < totalTX; i++) {
var tx = this.parseTX(p.slice(off));
for (i = 0; i < totalTX; i++) {
tx = this.parseTX(p.slice(off));
off += tx._off;
txs.push(tx);
}
@ -296,12 +327,15 @@ Parser.prototype.parseBlock = function parseBlock(p) {
};
Parser.prototype.parseTXIn = function parseTXIn(p) {
var scriptLen, off;
if (p.length < 41)
return this._error('Invalid tx_in size');
var scriptLen = readIntv(p, 36);
var off = scriptLen.off;
scriptLen = readIntv(p, 36);
off = scriptLen.off;
scriptLen = scriptLen.r;
if (off + scriptLen + 4 > p.length)
return this._error('Invalid tx_in script length');
@ -317,12 +351,15 @@ Parser.prototype.parseTXIn = function parseTXIn(p) {
};
Parser.prototype.parseTXOut = function parseTXOut(p) {
var scriptLen, off;
if (p.length < 9)
return this._error('Invalid tx_out size');
var scriptLen = readIntv(p, 8);
var off = scriptLen.off;
scriptLen = readIntv(p, 8);
off = scriptLen.off;
scriptLen = scriptLen.r;
if (off + scriptLen > p.length)
return this._error('Invalid tx_out script length');
@ -334,22 +371,30 @@ Parser.prototype.parseTXOut = function parseTXOut(p) {
};
Parser.prototype.parseTX = function parseTX(p) {
var inCount, off, txIn, tx;
var outCount, off, txOut;
var i;
if (p.length < 10)
return this._error('Invalid tx size');
var inCount = readIntv(p, 4);
var off = inCount.off;
inCount = readIntv(p, 4);
off = inCount.off;
inCount = inCount.r;
if (inCount < 0)
return this._error('Invalid tx_in count (negative)');
if (off + 41 * inCount + 5 > p.length)
return this._error('Invalid tx_in count (too big)');
var txIn = new Array(inCount);
for (var i = 0; i < inCount; i++) {
var tx = this.parseTXIn(p.slice(off));
txIn = new Array(inCount);
for (i = 0; i < inCount; i++) {
tx = this.parseTXIn(p.slice(off));
if (!tx)
return;
txIn[i] = tx;
off += tx.size;
@ -357,19 +402,21 @@ Parser.prototype.parseTX = function parseTX(p) {
return this._error('Invalid tx_in offset');
}
var outCount = readIntv(p, off);
var off = outCount.off;
outCount = readIntv(p, off);
off = outCount.off;
outCount = outCount.r;
if (outCount < 0)
return this._error('Invalid tx_out count (negative)');
if (off + 9 * outCount + 4 > p.length)
return this._error('Invalid tx_out count (too big)');
var txOut = new Array(outCount);
for (var i = 0; i < outCount; i++) {
var tx = this.parseTXOut(p.slice(off));
txOut = new Array(outCount);
for (i = 0; i < outCount; i++) {
tx = this.parseTXOut(p.slice(off));
if (!tx)
return;
txOut[i] = tx;
off += tx.size;
@ -389,32 +436,36 @@ Parser.prototype.parseTX = function parseTX(p) {
};
Parser.prototype.parseReject = function parseReject(p) {
var messageLen, off, message, ccode, reasonLen, reason, data;
if (p.length < 3)
return this._error('Invalid reject size');
var messageLen = readIntv(p, 0);
var off = messageLen.off;
messageLen = readIntv(p, 0);
off = messageLen.off;
messageLen = messageLen.r;
if (off + messageLen + 2 > p.length)
return this._error('Invalid reject message');
var message = utils.stringify(p.slice(off, off + messageLen));
message = utils.stringify(p.slice(off, off + messageLen));
off += messageLen;
var ccode = p[off];
ccode = p[off];
off++;
var reasonLen = readIntv(p, off);
reasonLen = readIntv(p, off);
off = reasonLen.off;
reasonLen = reasonLen.r;
if (off + reasonLen > p.length)
return this._error('Invalid reject reason');
var reason = utils.stringify(p.slice(off, off + reasonLen));
reason = utils.stringify(p.slice(off, off + reasonLen));
off += reasonLen;
var data = p.slice(off, off + 32);
data = p.slice(off, off + 32);
return {
message: message,
@ -429,34 +480,35 @@ Parser.prototype.parseAddr = function parseAddr(p) {
return this._error('Invalid addr size');
var addrs = [];
var i, len, off, count, ts, service, ipv6, ipv4, port;
// count
var len = readIntv(p, 0);
var off = len.off;
var count = len.r;
len = readIntv(p, 0);
off = len.off;
count = len.r;
p = p.slice(off);
for (var i = 0; i < count; i++) {
for (i = 0; i < count; i++) {
// timestamp - LE
var ts = utils.readU32(p, 0);
ts = utils.readU32(p, 0);
// NODE_NETWORK service - LE
var service = utils.readU64(p, 4);
service = utils.readU64(p, 4);
// ipv6 - BE
var ipv6 = utils.toHex(p.slice(12, 24));
ipv6 = utils.toHex(p.slice(12, 24));
ipv6 = '::' + ipv6.replace(/(.{4})/g, '$1:').slice(0, -1);
// ipv4 - BE
var ipv4 = utils.readU32BE(p, 24);
ipv4 = ((ipv4 >> 24) & 0xff) + '.' +
((ipv4 >> 16) & 0xff) + '.' +
((ipv4 >> 8) & 0xff) + '.' +
((ipv4 >> 0) & 0xff);
ipv4 = utils.readU32BE(p, 24);
ipv4 = ((ipv4 >> 24) & 0xff)
+ '.' + ((ipv4 >> 16) & 0xff)
+ '.' + ((ipv4 >> 8) & 0xff)
+ '.' + ((ipv4 >> 0) & 0xff);
// port - BE
var port = utils.readU16BE(p, 28);
port = utils.readU16BE(p, 28);
addrs.push({
ts: ts,
@ -471,3 +523,5 @@ Parser.prototype.parseAddr = function parseAddr(p) {
return addrs;
};
module.exports = Parser;

View File

@ -8,9 +8,13 @@ var script = exports;
script.decode = function decode(s) {
if (!s)
return [];
var opcodes = [];
for (var i = 0; i < s.length;) {
var b = s[i++];
var i = 0;
var b, opcode, len;
while (i < s.length) {
b = s[i++];
// Next `b` bytes should be pushed to stack
if (b >= 0x01 && b <= 0x4b) {
@ -35,7 +39,7 @@ script.decode = function decode(s) {
continue;
}
var opcode = constants.opcodesByVal[b];
opcode = constants.opcodesByVal[b];
if (i >= s.length) {
opcodes.push(opcode || b);
@ -43,7 +47,7 @@ script.decode = function decode(s) {
}
if (opcode === 'pushdata1') {
var len = s[i];
len = s[i];
i += 1;
opcodes.push(s.slice(i, i + len));
i += len;
@ -52,7 +56,7 @@ script.decode = function decode(s) {
len: len
});
} else if (opcode === 'pushdata2') {
var len = utils.readU16(s, i);
len = utils.readU16(s, i);
i += 2;
opcodes.push(s.slice(i, i + len));
i += len;
@ -61,7 +65,7 @@ script.decode = function decode(s) {
len: len
});
} else if (opcode === 'pushdata4') {
var len = utils.readU32(s, i);
len = utils.readU32(s, i);
i += 4;
opcodes.push(s.slice(i, i + len));
i += len;
@ -85,8 +89,11 @@ script.encode = function encode(s) {
var opcodes = constants.opcodes;
var res = [];
for (var i = 0; i < s.length; i++) {
var instr = s[i];
var i = 0;
var instr;
for (i = 0; i < s.length; i++) {
instr = s[i];
// Push value to stack
if (Array.isArray(instr)) {
@ -134,27 +141,30 @@ script.encode = function encode(s) {
};
script.subscript = function subscript(s, lastSep) {
var i, res;
if (!s)
return [];
if (lastSep == null) {
lastSep = -1;
for (var i = 0; i < s.length; i++) {
for (i = 0; i < s.length; i++) {
if (s[i] === 'codesep')
lastSep = i;
else if (s[i] === 'checksig' ||
s[i] === 'checksigverify' ||
s[i] === 'checkmultisig' ||
s[i] === 'checkmultisigverify') {
else if (s[i] === 'checksig'
|| s[i] === 'checksigverify'
|| s[i] === 'checkmultisig'
|| s[i] === 'checkmultisigverify') {
break;
}
}
}
var res = [];
for (var i = lastSep + 1; i < s.length; i++)
res = [];
for (i = lastSep + 1; i < s.length; i++) {
if (s[i] !== 'codesep')
res.push(s[i]);
}
return res;
};
@ -179,8 +189,10 @@ script.verify = function verify(hash, sig, pub) {
script._next = function(to, s, pc) {
var depth = 0;
var o;
while (s[pc]) {
var o = s[pc];
o = s[pc];
if (o === 'if_' || o === 'notif')
depth++;
else if (o === 'else_')
@ -195,6 +207,7 @@ script._next = function(to, s, pc) {
depth++;
pc++;
}
return -1;
};
@ -205,11 +218,22 @@ script.execute = function execute(s, stack, tx, index, recurse) {
return false;
var lastSep = -1;
var pc = 0;
var o, val;
var if_, else_, endif;
var v, v1, v2, v3, v4;
var n, n1, n2, n3;
var res;
var pub, sig, type, subscript, hash;
var keys, i, key, m;
var succ;
var lock, threshold;
var evalScript;
stack.alt = stack.alt || [];
for (var pc = 0; pc < s.length; pc++) {
var o = s[pc];
for (pc = 0; pc < s.length; pc++) {
o = s[pc];
if (Array.isArray(o)) {
if (o.length > constants.script.maxPush)
@ -237,16 +261,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
}
case 'if_':
case 'notif': {
var val = false;
val = false;
if (stack.length < 1)
return false;
var v = stack.pop();
v = stack.pop();
val = new bn(v).cmpn(0) !== 0;
if (o === 'notif')
val = !val;
var if_ = pc;
var else_ = script._next('else_', s, pc);
var endif = script._next('endif', s, pc);
if_ = pc;
else_ = script._next('else_', s, pc);
endif = script._next('endif', s, pc);
// Splice out the statement blocks we don't need
if (val) {
if (endif === -1)
@ -339,10 +363,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'roll': {
if (stack.length < 2)
return false;
var n = new bn(stack.pop()).toNumber();
n = new bn(stack.pop()).toNumber();
if (n < 0 || n >= stack.length)
return false;
var v = stack[-n - 1];
v = stack[-n - 1];
if (o === 'roll')
stack.splice(stack.length - n - 1, 1);
stack.push(v);
@ -351,9 +375,9 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'rot': {
if (stack.length < 3)
return false;
var v3 = stack[stack.length - 3];
var v2 = stack[stack.length - 2];
var v1 = stack[stack.length - 1];
v3 = stack[stack.length - 3];
v2 = stack[stack.length - 2];
v1 = stack[stack.length - 1];
stack[stack.length - 3] = v2;
stack[stack.length - 2] = v3;
v2 = stack[stack.length - 2];
@ -364,8 +388,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'swap': {
if (stack.length < 2)
return false;
var v2 = stack[stack.length - 2];
var v1 = stack[stack.length - 1];
v2 = stack[stack.length - 2];
v1 = stack[stack.length - 1];
stack[stack.length - 2] = v1;
stack[stack.length - 1] = v2;
break;
@ -386,8 +410,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'dup2': {
if (stack.length < 2)
return false;
var v1 = stack[stack.length - 1];
var v2 = stack[stack.length - 2];
v1 = stack[stack.length - 1];
v2 = stack[stack.length - 2];
stack.push(v1);
stack.push(v2);
break;
@ -395,9 +419,9 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'dup3': {
if (stack.length < 3)
return false;
var v1 = stack[stack.length - 1];
var v2 = stack[stack.length - 2];
var v3 = stack[stack.length - 3];
v1 = stack[stack.length - 1];
v2 = stack[stack.length - 2];
v3 = stack[stack.length - 3];
stack.push(v1);
stack.push(v2);
stack.push(v3);
@ -406,8 +430,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'over2': {
if (stack.length < 4)
return false;
var v1 = stack[stack.length - 4];
var v2 = stack[stack.length - 3];
v1 = stack[stack.length - 4];
v2 = stack[stack.length - 3];
stack.push(v1);
stack.push(v2);
break;
@ -415,8 +439,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'rot2': {
if (stack.length < 6)
return false;
var v1 = stack[stack.length - 6];
var v2 = stack[stack.length - 5];
v1 = stack[stack.length - 6];
v2 = stack[stack.length - 5];
stack.splice(stack.length - 6, 2);
stack.push(v1);
stack.push(v2);
@ -425,10 +449,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'swap2': {
if (stack.length < 4)
return false;
var v4 = stack[stack.length - 4];
var v3 = stack[stack.length - 3];
var v2 = stack[stack.length - 2];
var v1 = stack[stack.length - 1];
v4 = stack[stack.length - 4];
v3 = stack[stack.length - 3];
v2 = stack[stack.length - 2];
v1 = stack[stack.length - 1];
stack[stack.length - 4] = v2;
stack[stack.length - 2] = v4;
stack[stack.length - 3] = v1;
@ -449,7 +473,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'noteq0': {
if (stack.length < 1)
return false;
var n = new bn(stack.pop());
n = new bn(stack.pop());
switch (o) {
case 'add1':
n.iadd(1);
@ -505,9 +529,9 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'max':
if (stack.length < 2)
return false;
var n2 = new bn(stack.pop());
var n1 = new bn(stack.pop());
var n = new bn(0);
n2 = new bn(stack.pop());
n1 = new bn(stack.pop());
n = new bn(0);
switch (o) {
case 'add':
n = n1.add(b2);
@ -551,7 +575,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
default:
return false;
}
var res = n.cmpn(0) !== 0;
res = n.cmpn(0) !== 0;
if (o === 'numeqverify') {
if (!res)
return false;
@ -563,10 +587,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'within':
if (stack.length < 3)
return false;
var n3 = new bn(stack.pop());
var n2 = new bn(stack.pop());
var n1 = new bn(stack.pop());
var val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
n3 = new bn(stack.pop());
n2 = new bn(stack.pop());
n1 = new bn(stack.pop());
val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
stack.push(val.cmpn(0) !== 0 ? [ 1 ] : []);
break;
}
@ -611,7 +635,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
case 'eq': {
if (stack.length < 2)
return false;
var res = utils.isEqual(stack.pop(), stack.pop());
res = utils.isEqual(stack.pop(), stack.pop());
if (o === 'eqverify') {
if (!res)
return false;
@ -625,19 +649,19 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (!tx || stack.length < 2)
return false;
var pub = stack.pop();
var sig = stack.pop();
var type = sig[sig.length - 1];
pub = stack.pop();
sig = stack.pop();
type = sig[sig.length - 1];
if (!constants.rhashType[type & 0x1f])
return false;
if (!script.isValidSig(sig))
return false;
var subscript = script.subscript(s, lastSep);
var hash = tx.subscriptHash(index, subscript, type);
subscript = script.subscript(s, lastSep);
hash = tx.subscriptHash(index, subscript, type);
var res = script.verify(hash, sig.slice(0, -1), pub);
res = script.verify(hash, sig.slice(0, -1), pub);
if (o === 'checksigverify') {
if (!res)
return false;
@ -652,7 +676,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (!tx || stack.length < 3)
return false;
var n = stack.pop();
n = stack.pop();
if (n.length !== 1 || !(1 <= n[0] && n[0] <= 15))
return false;
n = n[0] || 0;
@ -660,16 +684,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (stack.length < n + 1)
return false;
var keys = [];
for (var i = 0; i < n; i++) {
var key = stack.pop();
keys = [];
for (i = 0; i < n; i++) {
key = stack.pop();
if (!(33 <= key.length && key.length <= 65))
return false;
keys.push(key);
}
var m = stack.pop();
m = stack.pop();
if (m.length !== 1 || !(1 <= m[0] && m[0] <= n))
return false;
m = m[0] || 0;
@ -677,23 +701,23 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (stack.length < m + 1)
return false;
var subscript = script.subscript(s, lastSep);
subscript = script.subscript(s, lastSep);
// Get signatures
var succ = 0;
for (var i = 0; i < m; i++) {
var sig = stack.pop();
var type = sig[sig.length - 1];
succ = 0;
for (i = 0; i < m; i++) {
sig = stack.pop();
type = sig[sig.length - 1];
if (!constants.rhashType[type & 0x1f])
return false;
if (!script.isValidSig(sig))
return false;
var hash = tx.subscriptHash(index, subscript, type);
hash = tx.subscriptHash(index, subscript, type);
// Strict order:
var res = script.verify(hash, sig.slice(0, -1), keys.pop());
res = script.verify(hash, sig.slice(0, -1), keys.pop());
if (res)
succ++;
}
@ -701,7 +725,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
// Extra value
stack.pop();
var res = succ >= m;
res = succ >= m;
if (o === 'checkmultisigverify') {
if (!res)
return false;
@ -716,15 +740,15 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (!tx || stack.length === 0)
return false;
var lock = new bn(stack[stack.length - 1]).toNumber();
lock = new bn(stack[stack.length - 1]).toNumber();
if (lock < 0)
return false;
var threshold = constants.locktimeThreshold;
threshold = constants.locktimeThreshold;
if (!(
(tx.lock < threshold && lock < threshold) ||
(tx.lock >= threshold && lock >= threshold)
(tx.lock < threshold && lock < threshold)
|| (tx.lock >= threshold && lock >= threshold)
)) {
return false;
}
@ -747,15 +771,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (recurse++ > 2)
return false;
var evalScript = stack.pop();
evalScript = stack.pop();
if (!Array.isArray(evalScript))
return false;
evalScript = script.decode(evalScript);
var res = evalScript.some(function(op) {
res = evalScript.some(function(op) {
return op === 'codesep';
});
if (res)
return false;
@ -780,11 +805,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
script.exec = function(input, output, tx, i, recurse) {
var stack = [];
var res;
script.execute(input, stack, tx, i, recurse);
var res = script.execute(output, stack, tx, i, recurse);
res = script.execute(output, stack, tx, i, recurse);
// if (!res || stack.length === 0 || new bn(stack.pop()).cmp(0) !== 0)
if (!res || stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
return false;
return true;
};
@ -847,8 +877,8 @@ script.isPubkey = function isPubkey(s, key) {
if (key)
return utils.isEqual(s[0], key);
else
return s[0];
return s[0];
};
script.isPubkeyhash = function isPubkeyhash(s, hash) {
@ -858,21 +888,24 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) {
if (s.length !== 5)
return false;
var match = s[0] === 'dup' &&
s[1] === 'hash160' &&
Array.isArray(s[2]) &&
s[3] === 'eqverify' &&
s[4] === 'checksig';
var match = s[0] === 'dup'
&& s[1] === 'hash160'
&& Array.isArray(s[2])
&& s[3] === 'eqverify'
&& s[4] === 'checksig';
if (!match)
return false;
if (hash)
return utils.isEqual(s[2], hash);
else
return s[2];
return s[2];
};
script.isMultisig = function isMultisig(s, pubs) {
var m, n, keys, isArray, total;
if (script.lockTime(s))
s = s.slice(3);
@ -883,7 +916,7 @@ script.isMultisig = function isMultisig(s, pubs) {
if (pubs && !Array.isArray(pubs[0]))
pubs = [pubs];
var m = s[0];
m = s[0];
if (typeof m === 'number' && m >= 1 && m <= 15)
m = [m];
if (!Array.isArray(m) || m.length !== 1)
@ -893,7 +926,7 @@ script.isMultisig = function isMultisig(s, pubs) {
if (s[s.length - 1] !== 'checkmultisig')
return false;
var n = s[s.length - 2];
n = s[s.length - 2];
if (typeof n === 'number' && n >= 1 && n <= 15)
n = [n];
if (!Array.isArray(n) || n.length !== 1)
@ -903,17 +936,19 @@ script.isMultisig = function isMultisig(s, pubs) {
if (n + 3 !== s.length)
return false;
var keys = s.slice(1, 1 + n);
var isArray = keys.every(function(k) {
keys = s.slice(1, 1 + n);
isArray = keys.every(function(k) {
return Array.isArray(k);
});
if (!isArray)
return false;
if (!pubs)
return keys;
var total = keys.filter(function(k) {
total = keys.filter(function(k) {
return pubs.some(function(pub) {
return utils.isEqual(k, pub);
});
@ -929,10 +964,10 @@ script.isScripthash = function isScripthash(s, hash) {
if (s.length !== 3)
return false;
var res = s[0] === 'hash160' &&
Array.isArray(s[1]) &&
s[1].length === 20 &&
s[2] === 'eq';
var res = s[0] === 'hash160'
&& Array.isArray(s[1])
&& s[1].length === 20
&& s[2] === 'eq';
if (!res)
return false;
@ -947,9 +982,9 @@ script.isColored = function isColored(s) {
if (s.length !== 2)
return false;
return s[0] === 'ret' &&
Array.isArray(s[1]) &&
s[1].length <= 40;
return s[0] === 'ret'
&& Array.isArray(s[1])
&& s[1].length <= 40;
};
script.colored = function colored(s) {
@ -986,11 +1021,11 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
if (s.length !== 2 || !Array.isArray(s[0]) || !Array.isArray(s[1]))
return false;
// var res = script.isValidSig(s[0]) &&
// 33 <= s[1].length && s[1].length <= 65;
// var res = script.isValidSig(s[0])
// && 33 <= s[1].length && s[1].length <= 65;
var res = 9 <= s[0].length && s[0].length <= 73 &&
33 <= s[1].length && s[1].length <= 65;
var res = 9 <= s[0].length && s[0].length <= 73
&& 33 <= s[1].length && s[1].length <= 65;
if (!res)
return false;
@ -1002,21 +1037,23 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
};
script.isMultisigInput = function isMultisigInput(s, pubs, tx, i) {
var i, res, o;
if (s.length < 3)
return false;
if (!Array.isArray(s[0]) || s[0].length !== 0)
return false;
for (var i = 1; i < s.length; i++) {
// var res = Array.isArray(s[i]) && script.isValidSig(s[i]);
var res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
for (i = 1; i < s.length; i++) {
// res = Array.isArray(s[i]) && script.isValidSig(s[i]);
res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
if (!res)
return false;
}
if (pubs && pubs.length >= 2) {
var o = script.multisig(pubs, 2, pubs.length);
o = script.multisig(pubs, 2, pubs.length);
return script.exec(s, o, tx, i);
}
@ -1024,27 +1061,29 @@ script.isMultisigInput = function isMultisigInput(s, pubs, tx, i) {
};
script.isScripthashInput = function isScripthashInput(s, redeem) {
var i, res, r, keys;
if (s.length < 4)
return false;
if (!Array.isArray(s[0]) || s[0].length !== 0)
return false;
for (var i = 1; i < s.length - 1; i++) {
// var res = Array.isArray(s[i]) && script.isValidSig(s[i]);
var res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
for (i = 1; i < s.length - 1; i++) {
// res = Array.isArray(s[i]) && script.isValidSig(s[i]);
res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
if (!res)
return false;
}
var r = Array.isArray(s[s.length - 1]) && s[s.length - 1];
r = Array.isArray(s[s.length - 1]) && s[s.length - 1];
if (r[r.length - 1] !== constants.opcodes.checkmultisig)
return false;
if (redeem)
return utils.isEqual(redeem, r);
var keys = script.decode(r).slice(1, -2);
keys = script.decode(r).slice(1, -2);
return keys;
};
@ -1061,6 +1100,8 @@ script.isScripthashInput = function isScripthashInput(s, redeem) {
* This function is consensus-critical since BIP66.
*/
script.isValidSig = function(sig) {
var lenR, lenS;
// Empty signature. Not strictly DER encoded, but allowed to provide a
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
if (sig.length === 0)
@ -1093,14 +1134,14 @@ script.isValidSig = function(sig) {
return false;
// Extract the length of the R element.
var lenR = sig[3];
lenR = sig[3];
// Make sure the length of the S element is still inside the signature.
if (5 + lenR >= sig.length)
return false;
// Extract the length of the S element.
var lenS = sig[5 + lenR];
lenS = sig[5 + lenR];
// Verify that the length of the signature matches the sum of the length
// of the elements.
@ -1146,14 +1187,15 @@ script.isValidSig = function(sig) {
script.format = function(input, output) {
var scripts = [];
var prev, redeem;
if (input) {
scripts.push(input.script);
if (input.out.tx && input.out.tx.outputs[input.out.index]) {
var prev = input.out.tx.outputs[input.out.index].script;
prev = input.out.tx.outputs[input.out.index].script;
scripts.push(prev);
if (script.isScripthash(prev)) {
var redeem = script.decode(input.script[input.script.length - 1]);
redeem = script.decode(input.script[input.script.length - 1]);
scripts.push(redeem);
}
}

View File

@ -22,27 +22,31 @@ function TXPool(wallet) {
// Load TXs from storage
this._init();
}
inherits(TXPool, EventEmitter);
module.exports = TXPool;
TXPool.prototype._init = function init() {
var self = this;
if (!this._storage) {
this._loaded = true;
return;
}
var self = this;
var s = this._storage.createReadStream({
keys: false,
start: this._prefix,
end: this._prefix + 'z'
});
s.on('data', function(data) {
self.add(bcoin.tx.fromJSON(data), true);
});
s.on('error', function(err) {
self.emit('error', err);
});
s.on('end', function() {
self._loaded = true;
self.emit('load', self._lastTs);
@ -51,6 +55,9 @@ TXPool.prototype._init = function init() {
TXPool.prototype.add = function add(tx, noWrite) {
var hash = tx.hash('hex');
var ownInput, ownOutput, updated;
var i, input, key, unspent, index, orphan;
var out, key, orphans, some;
// Ignore stale pending transactions
if (tx.ts === 0 && tx.ps + 2 * 24 * 3600 < +new Date() / 1000) {
@ -70,19 +77,19 @@ TXPool.prototype.add = function add(tx, noWrite) {
}
this._all[hash] = tx;
var ownInput = this._wallet.ownInput(tx);
var ownOutput = this._wallet.ownOutput(tx);
var updated = false;
ownInput = this._wallet.ownInput(tx);
ownOutput = this._wallet.ownOutput(tx);
updated = false;
// Consume unspent money or add orphans
for (var i = 0; i < tx.inputs.length; i++) {
var input = tx.inputs[i];
var key = input.out.hash + '/' + input.out.index;
var unspent = this._unspent[key];
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
key = input.out.hash + '/' + input.out.index;
unspent = this._unspent[key];
if (unspent) {
// Add TX to inputs and spend money
var index = tx._input(unspent.tx, unspent.index);
index = tx._input(unspent.tx, unspent.index);
// Skip invalid transactions
if (!tx.verify(index))
@ -99,7 +106,7 @@ TXPool.prototype.add = function add(tx, noWrite) {
continue;
// Add orphan, if no parent transaction is yet known
var orphan = { tx: tx, index: input.out.index };
orphan = { tx: tx, index: input.out.index };
if (this._orphans[key])
this._orphans[key].push(orphan);
else
@ -130,19 +137,19 @@ TXPool.prototype.add = function add(tx, noWrite) {
}
// Add unspent outputs or fullfill orphans
for (var i = 0; i < tx.outputs.length; i++) {
var out = tx.outputs[i];
for (i = 0; i < tx.outputs.length; i++) {
out = tx.outputs[i];
// Do not add unspents for outputs that aren't ours.
if (!~ownOutput.indexOf(out))
continue;
var key = hash + '/' + i;
var orphans = this._orphans[key];
key = hash + '/' + i;
orphans = this._orphans[key];
// Add input to orphan
if (orphans) {
var some = orphans.some(checkOrphan, this);
some = orphans.some(checkOrphan, this);
if (!some)
orphans = null;
}
@ -166,10 +173,11 @@ TXPool.prototype.add = function add(tx, noWrite) {
};
TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
var self = this;
if (!this._storage || noWrite)
return;
var self = this;
this._storage.put(this._prefix + hash, tx.toJSON(), function(err) {
if (err)
self.emit('error', err);
@ -177,13 +185,14 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
};
TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
var self = this;
for (var i = 0; i < tx.outputs.length; i++)
delete this._unspent[tx.hash('hex') + '/' + i];
if (!this._storage || noWrite)
return;
var self = this;
this._storage.del(this._prefix + tx.hash('hex'), function(err) {
if (err)
self.emit('error', err);
@ -194,8 +203,8 @@ TXPool.prototype.all = function all() {
return Object.keys(this._all).map(function(key) {
return this._all[key];
}, this).filter(function(tx) {
return this._wallet.ownOutput(tx) ||
this._wallet.ownInput(tx);
return this._wallet.ownOutput(tx)
|| this._wallet.ownInput(tx);
}, this);
};
@ -208,9 +217,11 @@ TXPool.prototype.unspent = function unspent() {
};
TXPool.prototype.hasUnspent = function hasUnspent(hash, unspent) {
var has;
if (Array.isArray(hash) && hash.length && typeof hash[0] !== 'number') {
unspent = this.unspent();
var has = hash.map(function(hash) {
has = hash.map(function(hash) {
var h = this.hasUnspent(hash, unspent);
if (!h)
return false;
@ -232,7 +243,7 @@ TXPool.prototype.hasUnspent = function hasUnspent(hash, unspent) {
unspent = unspent || this.unspent();
var has = unspent.filter(function(item) {
has = unspent.filter(function(item) {
return item.tx.hash('hex') === hash;
});
@ -279,3 +290,5 @@ TXPool.prototype.fromJSON = function fromJSON(json) {
this.add(bcoin.tx.fromJSON(tx));
}, this);
};
module.exports = TXPool;

View File

@ -47,7 +47,6 @@ function TX(data, block) {
this.changeAddress = data.changeAddress || null;
}
module.exports = TX;
TX.fee = 10000;
TX.dust = 5460;
@ -77,12 +76,13 @@ TX.prototype.input = function input(i, index) {
};
TX.prototype._input = function _input(i, index) {
var hash, input, prev, lock, index, ex;
if (i instanceof TX)
i = { tx: i, index: index };
else if (typeof i === 'string' || Array.isArray(i))
i = { hash: i, index: index };
var hash;
if (i.tx)
hash = i.tx.hash('hex');
else if (i.out)
@ -93,7 +93,7 @@ TX.prototype._input = function _input(i, index) {
if (typeof hash !== 'string')
hash = utils.toHex(hash);
var input = {
input = {
out: {
tx: (i.out ? i.out.tx : i.tx) || null,
hash: utils.toHex(hash),
@ -107,8 +107,8 @@ TX.prototype._input = function _input(i, index) {
utils.hidden(input.script, '_raw', i.script._raw);
if (input.out.tx) {
var prev = input.out.tx.outputs[input.out.index].script;
var lock = bcoin.script.lockTime(prev);
prev = input.out.tx.outputs[input.out.index].script;
lock = bcoin.script.lockTime(prev);
if (lock) {
if (this._lock === 0)
this.lock = Math.max(lock.toNumber(), this.lock);
@ -123,9 +123,9 @@ TX.prototype._input = function _input(i, index) {
}
// Try modifying existing input first
var index = this._inputIndex(hash, index);
index = this._inputIndex(hash, index);
if (index !== -1) {
var ex = this.inputs[index];
ex = this.inputs[index];
input.out.tx = input.out.tx || ex.out.tx;
input.seq = input.seq || ex.seq;
input.script = input.script.length ? input.script : ex.script;
@ -139,10 +139,13 @@ TX.prototype._input = function _input(i, index) {
};
TX.prototype._inputIndex = function _inputIndex(hash, index) {
var i, ex;
if (hash instanceof TX)
hash = hash.hash('hex');
for (var i = 0; i < this.inputs.length; i++) {
var ex = this.inputs[i];
for (i = 0; i < this.inputs.length; i++) {
ex = this.inputs[i];
if (ex.out.hash === hash && ex.out.index === index)
return i;
}
@ -151,10 +154,12 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) {
};
TX.prototype.prevOut = function prevOut(i, def) {
var input;
if (typeof i === 'object')
i = this.inputs.indexOf(i);
var input = this.inputs[i];
input = this.inputs[i];
if (!input || !input.out.tx || input.out.index == null)
return def;
@ -163,6 +168,8 @@ TX.prototype.prevOut = function prevOut(i, def) {
};
TX.prototype.signatureHash = function signatureHash(i, type) {
var input, s, hash;
if (typeof i === 'object')
i = this.inputs.indexOf(i);
@ -173,18 +180,20 @@ TX.prototype.signatureHash = function signatureHash(i, type) {
type = constants.hashType[type];
// Get the current input.
var input = this.inputs[i];
input = this.inputs[i];
// Get the previous output's subscript
var s = input.out.tx.getSubscript(input.out.index);
s = input.out.tx.getSubscript(input.out.index);
// Get the hash of the current tx, minus the other inputs, plus the sighash.
var hash = this.subscriptHash(i, s, type);
hash = this.subscriptHash(i, s, type);
return hash;
};
TX.prototype.signature = function signature(i, key, type) {
var hash, signature;
if (typeof i === 'object')
i = this.inputs.indexOf(i);
@ -195,10 +204,10 @@ TX.prototype.signature = function signature(i, key, type) {
type = constants.hashType[type];
// Get the hash of the current tx, minus the other inputs, plus the sighash.
var hash = this.sigHash(i, type);
hash = this.sigHash(i, type);
// Sign the transaction with our one input
var signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
// Add the sighash as a single byte to the signature
signature = signature.concat(type);
@ -210,6 +219,7 @@ TX.prototype.signature = function signature(i, key, type) {
TX.prototype.scriptInput = function scriptInput(input, pub) {
// Get the previous output's subscript
var s = input.out.tx.getSubscript(input.out.index);
var n, i, redeem;
// Already has a script template (at least)
if (input.script.length)
@ -236,11 +246,11 @@ TX.prototype.scriptInput = function scriptInput(input, pub) {
// raw format: OP_FALSE [sig-1] [sig-2] ...
if (bcoin.script.isMultisig(s)) {
input.script = [ [] ];
var n = s[s.length - 2];
n = s[s.length - 2];
// If using pushdata instead of OP_1-16:
if (Array.isArray(n))
n = n[0] || 0;
for (var i = 0; i < n; i++)
for (i = 0; i < n; i++)
input.script[i + 1] = [];
this._recalculateFee();
return;
@ -250,12 +260,12 @@ TX.prototype.scriptInput = function scriptInput(input, pub) {
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
if (bcoin.script.isScripthash(s)) {
input.script = [ [] ];
var redeem = bcoin.script.decode(pub);
var n = redeem[redeem.length - 2];
redeem = bcoin.script.decode(pub);
n = redeem[redeem.length - 2];
// If using pushdata instead of OP_1-16:
if (Array.isArray(n))
n = n[0] || 0;
for (var i = 0; i < n; i++)
for (i = 0; i < n; i++)
input.script[i + 1] = [];
// P2SH requires the redeem script after signatures
input.script.push(pub);
@ -268,6 +278,9 @@ TX.prototype.scriptInput = function scriptInput(input, pub) {
// Sign the now-built scriptSigs
TX.prototype.signInput = function signInput(input, key, type) {
var s, hash, signature;
var len, redeem, m, keys, pub, pubn, ki, totalSigs, i;
if (!type)
type = 'all';
@ -275,13 +288,13 @@ TX.prototype.signInput = function signInput(input, key, type) {
type = constants.hashType[type];
// Get the previous output's subscript
var s = input.out.tx.getSubscript(input.out.index);
s = input.out.tx.getSubscript(input.out.index);
// Get the hash of the current tx, minus the other inputs, plus the sighash.
var hash = this.subscriptHash(this.inputs.indexOf(input), s, type);
hash = this.subscriptHash(this.inputs.indexOf(input), s, type);
// Sign the transaction with our one input
var signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
signature = bcoin.ecdsa.sign(hash, key.priv).toDER();
// Add the sighash as a single byte to the signature
signature = signature.concat(type);
@ -302,8 +315,7 @@ TX.prototype.signInput = function signInput(input, key, type) {
// raw format: OP_FALSE [sig-1] [sig-2] ...
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
var len = input.script.length;
var redeem;
len = input.script.length;
if (bcoin.script.isScripthash(s)) {
len--;
@ -312,18 +324,18 @@ TX.prototype.signInput = function signInput(input, key, type) {
redeem = s;
}
var m = redeem[0];
m = redeem[0];
// If using pushdata instead of OP_1-16:
if (Array.isArray(m))
m = m[0] || 0;
var keys = redeem.slice(1, -2);
var pub = key.getPublic(true, 'array');
var pubn = key.getPublic(false, 'array');
keys = redeem.slice(1, -2);
pub = key.getPublic(true, 'array');
pubn = key.getPublic(false, 'array');
// Find the key index so we can place
// the signature in the same index.
for (var ki = 0; ki < keys.length; ki++) {
for (ki = 0; ki < keys.length; ki++) {
if (utils.isEqual(pub, keys[ki]) || utils.isEqual(pubn, keys[ki]))
break;
}
@ -336,8 +348,8 @@ TX.prototype.signInput = function signInput(input, key, type) {
// Add our signature to the correct slot
// and count the total number of signatures.
var totalSigs = 0;
for (var i = 1; i < len; i++) {
totalSigs = 0;
for (i = 1; i < len; i++) {
if (Array.isArray(input.script[i]) && input.script[i].length) {
totalSigs++;
continue;
@ -353,7 +365,7 @@ TX.prototype.signInput = function signInput(input, key, type) {
// All signatures added. Finalize by removing empty slots.
if (totalSigs >= m) {
for (var i = len - 1; i >= 1; i--) {
for (i = len - 1; i >= 1; i--) {
if (Array.isArray(input.script[i]) && !input.script[i].length)
input.script.splice(i, 1);
}
@ -414,6 +426,8 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
options = options || output;
var script = output.script ? output.script.slice() : [];
var keys, m, n;
var hash, color;
if (output.script && output.script._raw)
utils.hidden(script, '_raw', output.script._raw);
@ -424,7 +438,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
// https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki
// https://github.com/bitcoin/bips/blob/master/bip-0019.mediawiki
// [required-sigs] [pubkey-hash1] [pubkey-hash2] ... [number-of-keys] checkmultisig
var keys = options.keys || options.address;
keys = options.keys || options.address;
if (keys === options.address) {
keys = keys.map(function(address) {
@ -440,8 +454,8 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
// compat:
options.m = options.minSignatures || options.m;
var m = options.m || keys.length;
var n = options.n || keys.length;
m = options.m || keys.length;
n = options.n || keys.length;
assert(m >= 1 && m <= n);
if (options.hash)
@ -453,7 +467,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
// make it p2sh
if (options.scripthash) {
var hash = utils.ripesha(bcoin.script.encode(script));
hash = utils.ripesha(bcoin.script.encode(script));
script = [
'hash160',
hash,
@ -480,7 +494,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
'checksig'
];
} else if (options.color) {
var color = options.color;
color = options.color;
if (typeof color === 'string')
color = utils.ascii2array(color);
assert(color.length <= 40);
@ -494,15 +508,13 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
};
TX.prototype.getSubscript = function getSubscript(index) {
var output = this.outputs[index];
assert(output);
var script = output.script;
var script = this.outputs[index].script;
return bcoin.script.subscript(script);
};
TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
var copy = this.clone();
var verifyStr, hash;
if (typeof type === 'string')
type = constants.hashType[type];
@ -550,11 +562,11 @@ TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
copy.inputs[0].script = s;
}
var verifyStr = copy.render();
verifyStr = copy.render();
utils.writeU32(verifyStr, type, verifyStr.length);
var hash = utils.dsha256(verifyStr);
hash = utils.dsha256(verifyStr);
return hash;
};
@ -568,6 +580,8 @@ TX.prototype.verify = function verify(index, force) {
return false;
return this.inputs.every(function(input, i) {
var stack, prev, push, res, redeem;
if (index !== undefined && index !== i)
return true;
@ -576,19 +590,19 @@ TX.prototype.verify = function verify(index, force) {
assert(input.out.tx.outputs.length > input.out.index);
var stack = [];
var prev = input.out.tx.outputs[input.out.index].script;
stack = [];
prev = input.out.tx.outputs[input.out.index].script;
if (bcoin.script.isScripthash(prev)) {
// p2sh transactions cannot have anything
// other than pushdata ops in the scriptSig
var push = input.script.slice(1).every(Array.isArray);
push = input.script.slice(1).every(Array.isArray);
if (!push)
return false;
}
bcoin.script.execute(input.script, stack, this, i);
var res = bcoin.script.execute(prev, stack, this, i);
res = bcoin.script.execute(prev, stack, this, i);
if (!res)
return false;
@ -598,7 +612,7 @@ TX.prototype.verify = function verify(index, force) {
return false;
if (bcoin.script.isScripthash(prev)) {
var redeem = input.script[input.script.length - 1];
redeem = input.script[input.script.length - 1];
if (!Array.isArray(redeem))
return false;
redeem = bcoin.script.decode(redeem);
@ -618,19 +632,23 @@ TX.prototype.isCoinbase = function isCoinbase() {
TX.prototype.maxSize = function maxSize() {
// Create copy with 0-script inputs
var copy = this.clone();
var size;
copy.inputs.forEach(function(input) {
input.script = [];
});
var size = copy.render().length;
size = copy.render().length;
// Add size for signatures and public keys
copy.inputs.forEach(function(input, i) {
var s, m, n, script, redeem;
// Get the previous output's script
// var s = input.out.tx.outputs[input.out.index].script;
// s = input.out.tx.outputs[input.out.index].script;
// Get the previous output's subscript
var s = input.out.tx.getSubscript(input.out.index);
s = input.out.tx.getSubscript(input.out.index);
if (bcoin.script.isPubkey(s)) {
// Signature + len
@ -651,7 +669,7 @@ TX.prototype.maxSize = function maxSize() {
// Empty byte
size += 1;
// Signature + len
var m = s[0];
m = s[0];
// If using pushdata instead of OP_1-16:
if (Array.isArray(m))
m = m[0] || 0;
@ -661,8 +679,7 @@ TX.prototype.maxSize = function maxSize() {
}
if (bcoin.script.isScripthash(s)) {
var script = this.inputs[i].script;
var redeem, m, n;
script = this.inputs[i].script;
if (script.length) {
redeem = bcoin.script.decode(script[script.length - 1]);
m = redeem[0];
@ -724,6 +741,9 @@ TX.prototype.utxos = function utxos(unspent) {
var utxos = [];
var lastAdded = 0;
var byteSize, addFee, change;
function addInput(unspent, i) {
// Add new inputs until TX will have enough funds to cover both
// minimum post cost and fee
@ -744,9 +764,9 @@ TX.prototype.utxos = function utxos(unspent) {
// (10000 satoshi for every 1024 bytes)
do {
// Calculate maximum possible size after signing
var byteSize = this.maxSize();
byteSize = this.maxSize();
var addFee = Math.ceil(byteSize / 1024) - fee;
addFee = Math.ceil(byteSize / 1024) - fee;
total.iadd(new bn(addFee * TX.fee));
fee += addFee;
@ -764,7 +784,7 @@ TX.prototype.utxos = function utxos(unspent) {
}
// How much money is left after sending outputs
var change = this.funds('in').sub(total);
change = this.funds('in').sub(total);
// Clear the tx of everything we added.
this.inputs = inputs;
@ -812,6 +832,7 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) {
TX.prototype._recalculateFee = function recalculateFee() {
var output = this.changeOutput;
if (!output) {
this.output({
address: this.changeAddress,
@ -864,26 +885,28 @@ TX.getInputData = function getInputData(input) {
if (!input || !input.script) return;
var script = input.script;
var scriptSig, pub, hash, addr, redeem, data;
var output;
if (bcoin.script.isPubkeyhashInput(script)) {
var scriptSig = utils.toHex(script[0]);
var pubKey = script[1];
var hash = utils.ripesha(pubKey);
var addr = bcoin.wallet.hash2addr(hash);
scriptSig = utils.toHex(script[0]);
pub = script[1];
hash = utils.ripesha(pub);
addr = bcoin.wallet.hash2addr(hash);
return {
sig: scriptSig,
pub: pubKey,
pub: pub,
hash: hash,
addr: addr
};
}
if (bcoin.script.isScripthashInput(script)) {
var pub = script[script.length - 1];
var hash = utils.ripesha(pub);
var addr = bcoin.wallet.hash2addr(hash, 'scripthash');
var redeem = bcoin.script.decode(pub);
var data = TX.getOutputData({ script: redeem });
pub = script[script.length - 1];
hash = utils.ripesha(pub);
addr = bcoin.wallet.hash2addr(hash, 'scripthash');
redeem = bcoin.script.decode(pub);
data = TX.getOutputData({ script: redeem });
data.pub = pub;
data.hash = hash;
data.addr = addr;
@ -904,7 +927,7 @@ TX.getInputData = function getInputData(input) {
if (!input.out.tx)
return;
var output = input.out.tx.outputs[input.out.index];
output = input.out.tx.outputs[input.out.index];
return TX.getOutputData(output);
};
@ -913,22 +936,23 @@ TX.getOutputData = function getOutputData(output) {
if (!output || !output.script) return;
var script = output.script;
var pub, hash, addr, pubs;
if (bcoin.script.isPubkey(script)) {
var pubKey = script[0];
var hash = utils.ripesha(pubKey);
var addr = bcoin.wallet.hash2addr(hash);
pub = script[0];
hash = utils.ripesha(pub);
addr = bcoin.wallet.hash2addr(hash);
return {
sig: null,
pub: pubKey,
pub: pub,
hash: hash,
addr: addr
};
}
if (bcoin.script.isPubkeyhash(script)) {
var hash = script[2];
var addr = bcoin.wallet.hash2addr(hash);
hash = script[2];
addr = bcoin.wallet.hash2addr(hash);
return {
sig: null,
pub: null,
@ -937,10 +961,10 @@ TX.getOutputData = function getOutputData(output) {
};
}
var pubs = bcoin.script.isMultisig(script);
pubs = bcoin.script.isMultisig(script);
if (pubs) {
var hash = utils.ripesha(pubs[0]);
var addr = bcoin.wallet.hash2addr(hash);
hash = utils.ripesha(pubs[0]);
addr = bcoin.wallet.hash2addr(hash);
return {
sig: null,
pub: pubs[0],
@ -963,8 +987,8 @@ TX.getOutputData = function getOutputData(output) {
}
if (bcoin.script.isScripthash(script)) {
var hash = utils.toHex(s[1]);
var addr = bcoin.wallet.hash2addr(hash, 'scripthash');
hash = utils.toHex(s[1]);
addr = bcoin.wallet.hash2addr(hash, 'scripthash');
return {
sig: null,
pub: null,
@ -991,12 +1015,14 @@ TX.prototype.getFee = function getFee() {
};
TX.prototype.funds = function funds(side) {
var acc = new bn(0);
var inputs;
if (side === 'in') {
var inputs = this.inputs.filter(function(input) {
inputs = this.inputs.filter(function(input) {
return input.out.tx;
});
var acc = new bn(0);
if (inputs.length === 0)
return acc;
@ -1008,7 +1034,6 @@ TX.prototype.funds = function funds(side) {
}
// Output
var acc = new bn(0);
if (this.outputs.length === 0)
return acc;
@ -1043,3 +1068,5 @@ TX.fromJSON = function fromJSON(json) {
return tx;
};
module.exports = TX;

View File

@ -7,15 +7,19 @@ var util = require('util');
function toArray(msg, enc) {
if (Array.isArray(msg))
return msg.slice();
if (!msg)
return [];
var res = [];
var i, c, hi, lo, slice, num;
if (typeof msg === 'string') {
if (!enc) {
for (var i = 0; i < msg.length; i++) {
var c = msg.charCodeAt(i);
var hi = c >> 8;
var lo = c & 0xff;
for (i = 0; i < msg.length; i++) {
c = msg.charCodeAt(i);
hi = c >> 8;
lo = c & 0xff;
if (hi)
res.push(hi, lo);
else
@ -25,9 +29,10 @@ function toArray(msg, enc) {
msg = msg.replace(/[^a-z0-9]+/ig, '');
if (msg.length % 2 !== 0)
msg = '0' + msg;
for (var i = 0; i < msg.length; i += 8) {
var slice = msg.slice(i, i + 8);
var num = parseInt(slice, 16);
for (i = 0; i < msg.length; i += 8) {
slice = msg.slice(i, i + 8);
num = parseInt(slice, 16);
if (slice.length === 8)
res.push((num >>> 24) & 0xff);
@ -39,15 +44,16 @@ function toArray(msg, enc) {
}
}
} else {
for (var i = 0; i < msg.length; i++)
for (i = 0; i < msg.length; i++)
res[i] = msg[i] | 0;
}
return res;
}
utils.toArray = toArray;
var base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ' +
'abcdefghijkmnopqrstuvwxyz';
var base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ'
+ 'abcdefghijkmnopqrstuvwxyz';
utils.toBase58 = function toBase58(arr) {
var n = new bn(arr, 16);
@ -56,17 +62,20 @@ utils.toBase58 = function toBase58(arr) {
var mod = new bn(0xacad10);
var res = '';
var r, end, i, c;
do {
var r = n.mod(mod);
r = n.mod(mod);
n = n.div(mod);
var end = n.cmpn(0) === 0;
end = n.cmpn(0) === 0;
utils.assert.equal(r.length, 1);
r = r.words[0];
for (var i = 0; i < 4; i++) {
var c = r % 58;
for (i = 0; i < 4; i++) {
c = r % 58;
r = (r - c) / 58;
if (c === 0 && r === 0 && end)
@ -77,7 +86,7 @@ utils.toBase58 = function toBase58(arr) {
} while (!end);
// Add leading "zeroes"
for (var i = 0; i < arr.length; i++) {
for (i = 0; i < arr.length; i++) {
if (arr[i] !== 0)
break;
res = '1' + res;
@ -87,18 +96,20 @@ utils.toBase58 = function toBase58(arr) {
};
utils.fromBase58 = function fromBase58(str) {
var i, zeroes, q, w, res, c, z;
// Count leading "zeroes"
for (var i = 0; i < str.length; i++)
for (i = 0; i < str.length; i++)
if (str[i] !== '1')
break;
var zeroes = i;
zeroes = i;
// Read 4-char words and add them to bignum
var q = 1;
var w = 0;
var res = new bn(0);
for (var i = zeroes; i < str.length; i++) {
var c = base58.indexOf(str[i]);
q = 1;
w = 0;
res = new bn(0);
for (i = zeroes; i < str.length; i++) {
c = base58.indexOf(str[i]);
if (!(c >= 0 && c < 58))
return [];
@ -113,9 +124,10 @@ utils.fromBase58 = function fromBase58(str) {
}
// Add leading "zeroes"
var z = [];
for (var i = 0; i < zeroes; i++)
z = [];
for (i = 0; i < zeroes; i++)
z.push(0);
return z.concat(res.toArray());
};
@ -152,12 +164,15 @@ utils.readU16 = function readU16(arr, off) {
utils.readU32 = function readU32(arr, off) {
if (!off)
off = 0;
var r = arr[off] |
(arr[off + 1] << 8) |
(arr[off + 2] << 16) |
(arr[off + 3] << 24);
var r = arr[off]
| (arr[off + 1] << 8)
| (arr[off + 2] << 16)
| (arr[off + 3] << 24);
if (r < 0)
r += 0x100000000;
return r;
};
@ -186,6 +201,8 @@ utils.writeU32 = function writeU32(dst, num, off) {
};
utils.writeU64 = function writeU64(dst, num, off) {
var i = 0;
if (!off)
off = 0;
@ -197,7 +214,8 @@ utils.writeU64 = function writeU64(dst, num, off) {
dst[off++] = ch;
});
var i = num.length;
i = num.length;
while (i--)
dst[off++] = num[i];
@ -223,6 +241,8 @@ utils.writeU32BE = function writeU32BE(dst, num, off) {
};
utils.writeU64BE = function writeU64BE(dst, num, off) {
var i = 0;
if (!off)
off = 0;
@ -230,7 +250,7 @@ utils.writeU64BE = function writeU64BE(dst, num, off) {
while (num.length < 8)
num.unshift(0);
for (var i = 0; i < num.length; i++)
for (; i < num.length; i++)
dst[off++] = num[i];
return 8;
@ -245,12 +265,15 @@ utils.readU16BE = function readU16BE(arr, off) {
utils.readU32BE = function readU32BE(arr, off) {
if (!off)
off = 0;
var r = (arr[off] << 24) |
(arr[off + 1] << 16) |
(arr[off + 2] << 8) |
arr[off + 3];
var r = (arr[off] << 24)
| (arr[off + 1] << 16)
| (arr[off + 2] << 8)
| arr[off + 3];
if (r < 0)
r += 0x100000000;
return r;
};
@ -261,19 +284,27 @@ utils.readU64BE = function readU64BE(arr, off) {
};
utils.writeAscii = function writeAscii(dst, str, off) {
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
var i = 0;
var c;
for (; i < str.length; i++) {
c = str.charCodeAt(i);
dst[off + i] = c & 0xff;
}
return i;
};
utils.readAscii = function readAscii(arr, off, len) {
var str = '';
for (var i = off; i < off + len; i++) {
var c = String.fromCharCode(arr[i] & 0xff);
var i = off;
var c;
for (i = off; i < off + len; i++) {
c = String.fromCharCode(arr[i] & 0xff);
str += c;
}
return str;
};
@ -290,17 +321,24 @@ utils.array2ascii = function array2ascii(arr) {
utils.copy = function copy(src, dst, off, force) {
var len = src.length;
var i = 0;
if (!force)
len = Math.min(dst.length - off, len);
for (var i = 0; i < len; i++)
for (; i < len; i++)
dst[i + off] = src[i];
return i;
};
utils.stringify = function stringify(arr) {
var res = '';
for (var i = 0; i < arr.length; i++)
var i = 0;
for (; i < arr.length; i++)
res += String.fromCharCode(arr[i]);
return res;
};
@ -312,23 +350,28 @@ function zero2(word) {
}
function toHex(msg) {
var res = '';
var i = 0;
if (typeof msg === 'string')
return msg;
var res = '';
for (var i = 0; i < msg.length; i++)
for (; i < msg.length; i++)
res += zero2(msg[i].toString(16));
return res;
}
utils.toHex = toHex;
function binaryInsert(list, item, compare, search) {
var start = 0,
end = list.length;
var start = 0;
var end = list.length;
var pos, cmp;
while (start < end) {
var pos = (start + end) >> 1;
var cmp = compare(item, list[pos]);
pos = (start + end) >> 1;
cmp = compare(item, list[pos]);
if (cmp === 0) {
start = pos;
@ -343,8 +386,10 @@ function binaryInsert(list, item, compare, search) {
if (!search)
list.splice(start, 0, item);
return start;
}
utils.binaryInsert = binaryInsert;
function bitsToTarget(bits) {
@ -352,26 +397,31 @@ function bitsToTarget(bits) {
var hi = (bits >>> 16) & 0xff;
var mid = (bits >>> 8) & 0xff;
var lo = bits & 0xff;
var res = new Array(len);
for (var i = 0; i < len - 3; i++)
var i = 0;
for (; i < len - 3; i++)
res[i] = 0;
res[i++] = lo;
res[i++] = mid;
res[i++] = hi;
if (hi === 0)
res.pop();
if (hi === 0 && mid === 0)
res.pop();
return res;
}
utils.bitsToTarget = bitsToTarget;
function testTarget(target, hash) {
if (typeof target === 'number')
target = bitsToTarget(target);
hash = utils.toArray(hash, 'hex');
for (var i = hash.length - 1; i >= target.length; i--)
@ -388,13 +438,16 @@ function testTarget(target, hash) {
return true;
}
utils.testTarget = testTarget;
utils.isEqual = function isEqual(a, b) {
var i = 0;
if (a.length !== b.length)
return false;
for (var i = 0; i < a.length; i++)
for (; i < a.length; i++)
if (a[i] !== b[i])
return false;
@ -433,12 +486,14 @@ RequestCache.prototype.add = function add(id, cb) {
};
RequestCache.prototype.fullfill = function fullfill(id, err, data) {
var cbs = this.map[id];
if (!this.map[id])
return;
var cbs = this.map[id];
delete this.map[id];
this.count--;
cbs.forEach(function(cb) {
cb(err, data);
});
@ -456,8 +511,11 @@ utils.asyncify = function asyncify(fn) {
utils.revHex = function revHex(s) {
var r = '';
for (var i = 0; i < s.length; i += 2)
var i = 0;
for (; i < s.length; i += 2)
r = s.slice(i, i + 2) + r;
return r;
};
@ -472,6 +530,9 @@ utils.assert.equal = function assertEqual(l, r, msg) {
};
utils.toBTC = function toBTC(satoshi, strict) {
var m = new bn(10000000).mul(new bn(10));
var lo = satoshi.mod(m);
if (typeof satoshi === 'string' && /^\d+(?:\.\d+)?$/.test(satoshi)) {
satoshi = new bn(satoshi, 10);
} else if (typeof satoshi === 'number') {
@ -483,8 +544,6 @@ utils.toBTC = function toBTC(satoshi, strict) {
if (!(satoshi instanceof bn))
throw new Error('could not calculate btc');
var m = new bn(10000000).mul(new bn(10));
var lo = satoshi.mod(m);
if (lo.cmpn(0) !== 0) {
lo = lo.toString(10);
while (lo.length < 8)
@ -498,10 +557,13 @@ utils.toBTC = function toBTC(satoshi, strict) {
};
utils.fromBTC = function(btc, strict) {
var m = new bn(10000000).mul(new bn(10));
var satoshi;
var parts;
if (typeof btc === 'string' && /^\d+(?:\.\d+)?$/.test(btc)) {
var parts = btc.split('.');
parts = btc.split('.');
parts[0] = parts[0] || '0';
parts[1] = parts[1] || '0';
while (parts[1].length < 8)
@ -517,8 +579,8 @@ utils.fromBTC = function(btc, strict) {
if (!(satoshi instanceof bn))
throw new Error('could not calculate satoshis');
var m = new bn(10000000).mul(new bn(10));
satoshi.imuln(m);
return satoshi;
};

View File

@ -82,18 +82,19 @@ function Wallet(options, passphrase) {
this._init();
}
inherits(Wallet, EventEmitter);
module.exports = Wallet;
Wallet.prototype._init = function init() {
var self = this;
var prevBalance = null;
if (this.tx._loaded) {
this.loaded = true;
return;
}
// Notify owners about new accepted transactions
var self = this;
var prevBalance = null;
this.tx.on('update', function(lastTs, tx) {
var b = this.balance();
if (prevBalance && prevBalance.cmp(b) !== 0)
@ -203,35 +204,43 @@ Wallet.prototype.derive = function derive() {
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
var priv = this.key.getPrivate();
var arr, chk;
if (priv)
priv = priv.toArray();
else
return;
if (!enc)
return priv;
if (enc === 'base58') {
// We'll be using ncompressed public key as an address
var arr = [ network.prefixes.privkey ];
arr = [ network.prefixes.privkey ];
// 0-pad key
while (arr.length + priv.length < 33)
arr.push(0);
arr = arr.concat(priv);
if (this.compressed)
arr.push(1);
var chk = utils.checksum(arr);
chk = utils.checksum(arr);
return utils.toBase58(arr.concat(chk));
} else {
return priv;
}
return priv;
};
Wallet.prototype.getFullPublicKey = function getFullPublicKey(enc) {
var pub = this.getOwnPublicKey();
var keys;
if (this.type === 'scripthash') {
var keys = this.getPublicKeys();
keys = this.getPublicKeys();
pub = bcoin.script.encode(bcoin.script.multisig(keys, this.m, this.n));
}
@ -297,12 +306,14 @@ Wallet.key2hash = function key2hash(key) {
};
Wallet.hash2addr = function hash2addr(hash, prefix) {
var addr;
hash = utils.toArray(hash, 'hex');
prefix = network.prefixes[prefix || 'pubkeyhash'];
hash = [ prefix ].concat(hash);
var addr = hash.concat(utils.checksum(hash));
addr = hash.concat(utils.checksum(hash));
return utils.toBase58(addr);
};
@ -317,6 +328,8 @@ Wallet.__defineGetter__('prefixes', function() {
});
Wallet.addr2hash = function addr2hash(addr, prefix) {
var chk;
if (prefix == null && typeof addr === 'string')
prefix = Wallet.prefixes[addr[0]];
@ -330,7 +343,7 @@ Wallet.addr2hash = function addr2hash(addr, prefix) {
if (addr[0] !== prefix)
return [];
var chk = utils.checksum(addr.slice(0, -4));
chk = utils.checksum(addr.slice(0, -4));
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
return [];
@ -352,11 +365,11 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
var keys = this.getPublicKeys();
var outputs = tx.outputs.filter(function(output, i) {
var s = output.script;
if (index !== undefined && index !== i)
return false;
var s = output.script;
if (bcoin.script.isPubkey(s, key))
return true;
@ -386,6 +399,8 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
var keys = this.getPublicKeys();
var inputs = tx.inputs.filter(function(input, i) {
var s;
if (index !== undefined && index !== i)
return false;
@ -404,7 +419,7 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
if (!input.out.tx)
return false;
var s = input.out.tx.outputs[input.out.index].script;
s = input.out.tx.outputs[input.out.index].script;
if (bcoin.script.isPubkey(s, key))
return true;
@ -468,11 +483,11 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, inputs) {
};
Wallet.prototype.signInputs = function(tx, type, inputs) {
var key = this.key;
if (!type)
type = 'all';
var key = this.key;
inputs = inputs || tx.inputs;
inputs = inputs.filter(function(input) {
@ -531,16 +546,22 @@ Wallet.prototype.balance = function balance() {
};
Wallet.prototype.fill = function fill(tx, cb) {
cb = utils.asyncify(cb);
var result = tx.fillUnspent(this.unspent(), this.getAddress());
var err;
cb = utils.asyncify(cb);
if (!result) {
var err = new Error('Not enough funds');
err = new Error('Not enough funds');
err.minBalance = tx.total;
cb(err);
return null;
}
this.sign(tx);
cb(null, tx);
return tx;
};
@ -590,12 +611,10 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
if (decrypt)
json.priv = decrypt(json.priv);
var priv;
var pub;
var compressed;
var priv, pub, compressed, key, w;
if (json.priv) {
var key = bcoin.utils.fromBase58(json.priv);
key = bcoin.utils.fromBase58(json.priv);
assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
assert.equal(key[0], network.prefixes.privkey);
@ -621,7 +640,7 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
priv = new hd.priv(json.hd);
}
var w = new Wallet({
w = new Wallet({
label: json.label,
priv: priv,
pub: pub,
@ -633,3 +652,5 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
return w;
};
module.exports = Wallet;