style consistency.
This commit is contained in:
parent
9fe8afa7d3
commit
5ece45091b
@ -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
|
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).
|
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
|
## Prerequisites
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -3,6 +3,8 @@ var utils = bcoin.utils;
|
|||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
|
|
||||||
function Block(data, subtype) {
|
function Block(data, subtype) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!(this instanceof Block))
|
if (!(this instanceof Block))
|
||||||
return new Block(data, subtype);
|
return new Block(data, subtype);
|
||||||
|
|
||||||
@ -28,7 +30,6 @@ function Block(data, subtype) {
|
|||||||
this.invalid = false;
|
this.invalid = false;
|
||||||
|
|
||||||
if (this.subtype === 'block') {
|
if (this.subtype === 'block') {
|
||||||
var self = this;
|
|
||||||
this.txs = data.txs || [];
|
this.txs = data.txs || [];
|
||||||
this.txs = this.txs.map(function(tx) {
|
this.txs = this.txs.map(function(tx) {
|
||||||
tx._network = self._network;
|
tx._network = self._network;
|
||||||
@ -49,7 +50,6 @@ function Block(data, subtype) {
|
|||||||
// Verify partial merkle tree and fill `ts` array
|
// Verify partial merkle tree and fill `ts` array
|
||||||
this._verifyMerkle();
|
this._verifyMerkle();
|
||||||
}
|
}
|
||||||
module.exports = Block;
|
|
||||||
|
|
||||||
Block.prototype.hash = function hash(enc) {
|
Block.prototype.hash = function hash(enc) {
|
||||||
// Hash it
|
// Hash it
|
||||||
@ -91,33 +91,30 @@ Block.prototype.hasTX = function hasTX(hash) {
|
|||||||
|
|
||||||
Block.prototype._verifyMerkle = function verifyMerkle() {
|
Block.prototype._verifyMerkle = function verifyMerkle() {
|
||||||
var height = 0;
|
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 tx = [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var j = 0;
|
var j = 0;
|
||||||
var hashes = this.hashes;
|
var hashes = this.hashes;
|
||||||
var flags = this.flags;
|
var flags = this.flags;
|
||||||
|
var i, root;
|
||||||
|
|
||||||
var root = visit(1);
|
if (this.subtype === 'block')
|
||||||
if (!root || root !== this.merkleRoot) {
|
|
||||||
this.invalid = true;
|
|
||||||
return;
|
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) {
|
function visit(depth) {
|
||||||
|
var flag, left, right;
|
||||||
|
|
||||||
if (i === flags.length * 8 || j === hashes.length)
|
if (i === flags.length * 8 || j === hashes.length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var flag = (flags[i >> 3] >>> (i & 7)) & 1;
|
flag = (flags[i >> 3] >>> (i & 7)) & 1;
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (flag === 0 || depth === height) {
|
if (flag === 0 || depth === height) {
|
||||||
@ -127,34 +124,49 @@ Block.prototype._verifyMerkle = function verifyMerkle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Go deeper
|
// Go deeper
|
||||||
var left = visit(depth + 1);
|
left = visit(depth + 1);
|
||||||
if (!left)
|
if (!left)
|
||||||
return null;
|
return null;
|
||||||
var right = visit(depth + 1);
|
|
||||||
|
right = visit(depth + 1);
|
||||||
if (right === left)
|
if (right === left)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (!right)
|
if (!right)
|
||||||
right = left;
|
right = left;
|
||||||
|
|
||||||
return utils.toHex(utils.dsha256(left + right, 'hex'));
|
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() {
|
Block.prototype.getMerkleRoot = function getMerkleRoot() {
|
||||||
var merkleTree = [];
|
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'));
|
merkleTree.push(this.txs[i].hash('hex'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var j = 0;
|
j = 0;
|
||||||
for (var size = this.txs.length; size > 1; size = ((size + 1) / 2) | 0) {
|
size = this.txs.length;
|
||||||
for (var i = 0; i < size; i += 2) {
|
|
||||||
var i2 = Math.min(i + 1, size - 1);
|
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
|
if (i2 === i + 1 && i2 + 1 === size
|
||||||
&& merkleTree[j + i] === merkleTree[j + i2]) {
|
&& merkleTree[j + i] === merkleTree[j + i2]) {
|
||||||
return utils.toHex(constants.zeroHash);
|
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));
|
merkleTree.push(utils.toHex(hash));
|
||||||
}
|
}
|
||||||
j += size;
|
j += size;
|
||||||
@ -169,6 +181,8 @@ Block.prototype.getMerkleRoot = function getMerkleRoot() {
|
|||||||
// This mimics the behavior of CheckBlockHeader()
|
// This mimics the behavior of CheckBlockHeader()
|
||||||
// and CheckBlock() in bitcoin/src/main.cpp.
|
// and CheckBlock() in bitcoin/src/main.cpp.
|
||||||
Block.prototype._checkBlock = function checkBlock() {
|
Block.prototype._checkBlock = function checkBlock() {
|
||||||
|
var i, unique, hash, merkleRoot;
|
||||||
|
|
||||||
// Check proof of work matches claimed amount
|
// Check proof of work matches claimed amount
|
||||||
if (!utils.testTarget(this.bits, this.hash()))
|
if (!utils.testTarget(this.bits, this.hash()))
|
||||||
return false;
|
return false;
|
||||||
@ -184,29 +198,29 @@ Block.prototype._checkBlock = function checkBlock() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First TX must be a coinbase
|
// First TX must be a coinbase
|
||||||
if (!this.txs.length ||
|
if (!this.txs.length
|
||||||
this.txs[0].inputs.length !== 1 ||
|
|| this.txs[0].inputs.length !== 1
|
||||||
+this.txs[0].inputs[0].out.hash !== 0)
|
|| +this.txs[0].inputs[0].out.hash !== 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The rest of the txs must not be coinbases
|
// The rest of the txs must not be coinbases
|
||||||
for (var i = 1; i < this.txs.length; i++) {
|
for (i = 1; i < this.txs.length; i++) {
|
||||||
if (this.txs[i].inputs.length === 1 &&
|
if (this.txs[i].inputs.length === 1
|
||||||
+this.txs[i].inputs[0].out.hash === 0)
|
&& +this.txs[i].inputs[0].out.hash === 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate tx ids
|
// Check for duplicate tx ids
|
||||||
var unique = {};
|
unique = {};
|
||||||
for (var i = 0; i < this.txs.length; i++) {
|
for (i = 0; i < this.txs.length; i++) {
|
||||||
var hash = this.txs[i].hash('hex');
|
hash = this.txs[i].hash('hex');
|
||||||
if (unique[hash])
|
if (unique[hash])
|
||||||
return false;
|
return false;
|
||||||
unique[hash] = true;
|
unique[hash] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build MerkleTree
|
// Build MerkleTree
|
||||||
var merkleRoot = this.getMerkleRoot();
|
merkleRoot = this.getMerkleRoot();
|
||||||
|
|
||||||
// Check merkle root
|
// Check merkle root
|
||||||
if (merkleRoot !== this.merkleRoot)
|
if (merkleRoot !== this.merkleRoot)
|
||||||
@ -228,22 +242,26 @@ Block.prototype.toJSON = function toJSON() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Block.fromJSON = function fromJSON(json) {
|
Block.fromJSON = function fromJSON(json) {
|
||||||
|
var raw, parser, data, block;
|
||||||
|
|
||||||
utils.assert.equal(json.v, 1);
|
utils.assert.equal(json.v, 1);
|
||||||
utils.assert.equal(json.type, 'block');
|
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.parseMerkleBlock(raw) :
|
||||||
parser.parseBlock(raw);
|
parser.parseBlock(raw);
|
||||||
|
|
||||||
data._network = json._network;
|
data._network = json._network;
|
||||||
|
|
||||||
var block = new Block(data, json.subtype);
|
block = new Block(data, json.subtype);
|
||||||
|
|
||||||
block._hash = json.hash;
|
block._hash = json.hash;
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Block;
|
||||||
|
|||||||
@ -12,7 +12,6 @@ function Bloom(size, n, tweak) {
|
|||||||
|
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
module.exports = Bloom;
|
|
||||||
|
|
||||||
Bloom.prototype.hash = function hash(val, n) {
|
Bloom.prototype.hash = function hash(val, n) {
|
||||||
return Bloom.hash(val, sum32(mul32(n, 0xfba4c795), this.tweak)) % this.size;
|
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) {
|
Bloom.prototype.add = function add(val, enc) {
|
||||||
|
var i, bit, pos, shift;
|
||||||
|
|
||||||
val = utils.toArray(val, enc);
|
val = utils.toArray(val, enc);
|
||||||
|
|
||||||
for (var i = 0; i < this.n; i++) {
|
for (i = 0; i < this.n; i++) {
|
||||||
var bit = this.hash(val, i);
|
bit = this.hash(val, i);
|
||||||
var pos = 1 << (bit & 0x1f);
|
pos = 1 << (bit & 0x1f);
|
||||||
var shift = bit >> 5;
|
shift = bit >> 5;
|
||||||
|
|
||||||
this.filter[shift] |= pos;
|
this.filter[shift] |= pos;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Bloom.prototype.test = function test(val, enc) {
|
Bloom.prototype.test = function test(val, enc) {
|
||||||
|
var i, bit, pos, shift;
|
||||||
|
|
||||||
val = utils.toArray(val, enc);
|
val = utils.toArray(val, enc);
|
||||||
|
|
||||||
for (var i = 0; i < this.n; i++) {
|
for (i = 0; i < this.n; i++) {
|
||||||
var bit = this.hash(val, i);
|
bit = this.hash(val, i);
|
||||||
var pos = 1 << (bit & 0x1f);
|
pos = 1 << (bit & 0x1f);
|
||||||
var shift = bit >> 5;
|
shift = bit >> 5;
|
||||||
|
|
||||||
if ((this.filter[shift] & pos) === 0)
|
if ((this.filter[shift] & pos) === 0)
|
||||||
return false;
|
return false;
|
||||||
@ -53,8 +56,10 @@ Bloom.prototype.test = function test(val, enc) {
|
|||||||
Bloom.prototype.toArray = function toArray() {
|
Bloom.prototype.toArray = function toArray() {
|
||||||
var bytes = Math.ceil(this.size / 8);
|
var bytes = Math.ceil(this.size / 8);
|
||||||
var res = new Array(this.filter.length * 4);
|
var res = new Array(this.filter.length * 4);
|
||||||
for (var i = 0; i < this.filter.length; i++) {
|
var i, w;
|
||||||
var w = this.filter[i];
|
|
||||||
|
for (i = 0; i < this.filter.length; i++) {
|
||||||
|
w = this.filter[i];
|
||||||
res[i * 4] = w & 0xff;
|
res[i * 4] = w & 0xff;
|
||||||
res[i * 4 + 1] = (w >> 8) & 0xff;
|
res[i * 4 + 1] = (w >> 8) & 0xff;
|
||||||
res[i * 4 + 2] = (w >> 16) & 0xff;
|
res[i * 4 + 2] = (w >> 16) & 0xff;
|
||||||
@ -69,22 +74,26 @@ function mul32(a, b) {
|
|||||||
var blo = b & 0xffff;
|
var blo = b & 0xffff;
|
||||||
var ahi = a >>> 16;
|
var ahi = a >>> 16;
|
||||||
var bhi = b >>> 16;
|
var bhi = b >>> 16;
|
||||||
|
var r;
|
||||||
|
|
||||||
var lo = alo * blo;
|
var lo = alo * blo;
|
||||||
var hi = (ahi * blo + bhi * alo) & 0xffff;
|
var hi = (ahi * blo + bhi * alo) & 0xffff;
|
||||||
hi += lo >>> 16;
|
hi += lo >>> 16;
|
||||||
lo &= 0xffff;
|
lo &= 0xffff;
|
||||||
var r = (hi << 16) | lo;
|
r = (hi << 16) | lo;
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
r += 0x100000000;
|
r += 0x100000000;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sum32(a, b) {
|
function sum32(a, b) {
|
||||||
var r = (a + b) & 0xffffffff;
|
var r = (a + b) & 0xffffffff;
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
r += 0x100000000;
|
r += 0x100000000;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,11 +112,14 @@ function hash(data, seed) {
|
|||||||
var n = 0xe6546b64;
|
var n = 0xe6546b64;
|
||||||
|
|
||||||
var hash = seed;
|
var hash = seed;
|
||||||
for (var i = 0; i + 4 <= data.length; i += 4) {
|
|
||||||
var w = data[i] |
|
var i, w, r, j;
|
||||||
(data[i + 1] << 8) |
|
|
||||||
(data[i + 2] << 16) |
|
for (i = 0; i + 4 <= data.length; i += 4) {
|
||||||
(data[i + 3] << 24);
|
w = data[i]
|
||||||
|
| (data[i + 1] << 8)
|
||||||
|
| (data[i + 2] << 16)
|
||||||
|
| (data[i + 3] << 24);
|
||||||
|
|
||||||
w = mul32(w, c1);
|
w = mul32(w, c1);
|
||||||
w = rotl32(w, r1);
|
w = rotl32(w, r1);
|
||||||
@ -120,8 +132,8 @@ function hash(data, seed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (i !== data.length) {
|
if (i !== data.length) {
|
||||||
var r = 0;
|
r = 0;
|
||||||
for (var j = data.length - 1; j >= i; j--)
|
for (j = data.length - 1; j >= i; j--)
|
||||||
r = (r << 8) | data[j];
|
r = (r << 8) | data[j];
|
||||||
|
|
||||||
r = mul32(r, c1);
|
r = mul32(r, c1);
|
||||||
@ -145,4 +157,7 @@ function hash(data, seed) {
|
|||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bloom.hash = hash;
|
Bloom.hash = hash;
|
||||||
|
|
||||||
|
module.exports = Bloom;
|
||||||
|
|||||||
@ -8,6 +8,8 @@ var utils = bcoin.utils;
|
|||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
|
|
||||||
function Chain(options) {
|
function Chain(options) {
|
||||||
|
var preload = network.preload;
|
||||||
|
|
||||||
if (!(this instanceof Chain))
|
if (!(this instanceof Chain))
|
||||||
return new Chain(options);
|
return new Chain(options);
|
||||||
|
|
||||||
@ -38,8 +40,6 @@ function Chain(options) {
|
|||||||
};
|
};
|
||||||
this.request = new utils.RequestCache();
|
this.request = new utils.RequestCache();
|
||||||
|
|
||||||
var preload = network.preload;
|
|
||||||
|
|
||||||
// Start from the genesis block
|
// Start from the genesis block
|
||||||
// if we're a full node.
|
// if we're a full node.
|
||||||
if (this.options.fullNode) {
|
if (this.options.fullNode) {
|
||||||
@ -60,8 +60,8 @@ function Chain(options) {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(Chain, EventEmitter);
|
inherits(Chain, EventEmitter);
|
||||||
module.exports = Chain;
|
|
||||||
|
|
||||||
function compareTs(a, b) {
|
function compareTs(a, b) {
|
||||||
return a -b;
|
return a -b;
|
||||||
@ -69,6 +69,7 @@ function compareTs(a, b) {
|
|||||||
|
|
||||||
Chain.prototype._init = function _init() {
|
Chain.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var s;
|
||||||
|
|
||||||
if (!this.storage)
|
if (!this.storage)
|
||||||
return;
|
return;
|
||||||
@ -79,7 +80,7 @@ Chain.prototype._init = function _init() {
|
|||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
var s = this.storage.createReadStream({
|
s = this.storage.createReadStream({
|
||||||
start: this.prefix,
|
start: this.prefix,
|
||||||
end: this.prefix + 'z'
|
end: this.prefix + 'z'
|
||||||
});
|
});
|
||||||
@ -103,18 +104,19 @@ Chain.prototype._init = function _init() {
|
|||||||
Chain.prototype._getRange = function _getRange(hash, ts, futureOnly) {
|
Chain.prototype._getRange = function _getRange(hash, ts, futureOnly) {
|
||||||
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
|
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
|
||||||
var start = Math.min(Math.max(0, pos), this.index.ts.length - 1);
|
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)
|
while (start > 0 && this.index.ts[start] > ts)
|
||||||
start--;
|
start--;
|
||||||
|
|
||||||
var curr = this.index.ts[start];
|
curr = this.index.ts[start];
|
||||||
var wnd = 2 * 3600;
|
wnd = 2 * 3600;
|
||||||
|
|
||||||
if (!futureOnly)
|
if (!futureOnly)
|
||||||
while (start > 0 && this.index.ts[start] + wnd > curr)
|
while (start > 0 && this.index.ts[start] + wnd > curr)
|
||||||
start--;
|
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)
|
while (end < this.index.ts.length - 1 && this.index.ts[end] - wnd < ts)
|
||||||
end++;
|
end++;
|
||||||
|
|
||||||
@ -127,13 +129,15 @@ Chain.prototype._probeIndex = function _probeIndex(hash, ts) {
|
|||||||
|
|
||||||
var start = 0;
|
var start = 0;
|
||||||
var end = this.index.ts.length;
|
var end = this.index.ts.length;
|
||||||
|
var range, i;
|
||||||
|
|
||||||
if (ts) {
|
if (ts) {
|
||||||
var range = this._getRange(hash, ts);
|
range = this._getRange(hash, ts);
|
||||||
start = range.start;
|
start = range.start;
|
||||||
end = range.end;
|
end = range.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = start; i <= end; i++)
|
for (i = start; i <= end; i++)
|
||||||
if (this.index.hashes[i] === hash)
|
if (this.index.hashes[i] === hash)
|
||||||
return { i: i, height: this.index.heights[i], ts: this.index.ts[i] };
|
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) {
|
Chain.prototype._addIndex = function _addIndex(hash, ts, height) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (this._probeIndex(hash, ts))
|
if (this._probeIndex(hash, ts))
|
||||||
return new Error('Already added.');
|
return new Error('Already added.');
|
||||||
|
|
||||||
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
|
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
|
||||||
|
var checkpoint, obj;
|
||||||
|
|
||||||
// Avoid duplicates
|
// Avoid duplicates
|
||||||
if (this.index.hashes[pos] === hash ||
|
if (this.index.hashes[pos] === hash
|
||||||
this.index.hashes[pos - 1] === hash ||
|
|| this.index.hashes[pos - 1] === hash
|
||||||
this.index.hashes[pos + 1] === hash) {
|
|| this.index.hashes[pos + 1] === hash) {
|
||||||
return new Error('Duplicate height.');
|
return new Error('Duplicate height.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkpoint = network.checkpoints[height];
|
checkpoint = network.checkpoints[height];
|
||||||
if (checkpoint) {
|
if (checkpoint) {
|
||||||
this.emit('checkpoint', height, hash, checkpoint);
|
this.emit('checkpoint', height, hash, checkpoint);
|
||||||
if (hash !== checkpoint) {
|
if (hash !== checkpoint) {
|
||||||
@ -171,8 +178,8 @@ Chain.prototype._addIndex = function _addIndex(hash, ts, height) {
|
|||||||
if (!this.storage)
|
if (!this.storage)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var self = this;
|
obj = { ts: ts, height: height };
|
||||||
var obj = { ts: ts, height: height };
|
|
||||||
this.storage.put(this.prefix + hash, obj, function(err) {
|
this.storage.put(this.prefix + hash, obj, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
@ -195,14 +202,14 @@ Chain.prototype.resetHeight = function resetHeight(height) {
|
|||||||
if (index < 0)
|
if (index < 0)
|
||||||
throw new Error('Cannot reset to height of ' + height);
|
throw new Error('Cannot reset to height of ' + height);
|
||||||
|
|
||||||
this.block.list = [];
|
this.block.list.length = 0;
|
||||||
this.block.bloom.reset();
|
this.block.bloom.reset();
|
||||||
this.orphan.map = {};
|
this.orphan.map = {};
|
||||||
this.orphan.bmap = {};
|
this.orphan.bmap = {};
|
||||||
this.orphan.count = 0;
|
this.orphan.count = 0;
|
||||||
this.index.ts = this.index.ts.slice(0, index + 1);
|
this.index.ts.length = index + 1;
|
||||||
this.index.hashes = this.index.hashes.slice(0, index + 1);
|
this.index.hashes.length = index + 1;
|
||||||
this.index.heights = this.index.heights.slice(0, index + 1);
|
this.index.heights.length = index + 1;
|
||||||
this.index.bloom.reset();
|
this.index.bloom.reset();
|
||||||
this.index.hashes.forEach(function(hash) {
|
this.index.hashes.forEach(function(hash) {
|
||||||
self.index.bloom.add(hash, 'hex');
|
self.index.bloom.add(hash, 'hex');
|
||||||
@ -211,13 +218,15 @@ Chain.prototype.resetHeight = function resetHeight(height) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype._killFork = function _killFork(probe) {
|
Chain.prototype._killFork = function _killFork(probe) {
|
||||||
|
var self = this;
|
||||||
var delta = 2 * 3600;
|
var delta = 2 * 3600;
|
||||||
var upper = probe.ts + delta;
|
var upper = probe.ts + delta;
|
||||||
var lower = probe.ts - delta;
|
var lower = probe.ts - delta;
|
||||||
|
var index, i, len, hash;
|
||||||
|
|
||||||
// Search duplicate heights down
|
// Search duplicate heights down
|
||||||
var index = -1;
|
index = -1;
|
||||||
for (var i = probe.i - 1; i > 0 && this.index.ts[i] > lower; i--) {
|
for (i = probe.i - 1; i > 0 && this.index.ts[i] > lower; i--) {
|
||||||
if (probe.height === this.index.heights[i]) {
|
if (probe.height === this.index.heights[i]) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
@ -226,8 +235,8 @@ Chain.prototype._killFork = function _killFork(probe) {
|
|||||||
|
|
||||||
// And up
|
// And up
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
var len = this.index.ts.length;
|
len = this.index.ts.length;
|
||||||
for (var i = probe.i + 1; i < len && this.index.ts[i] < upper; i++) {
|
for (i = probe.i + 1; i < len && this.index.ts[i] < upper; i++) {
|
||||||
if (probe.height === this.index.heights[i]) {
|
if (probe.height === this.index.heights[i]) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
@ -238,7 +247,7 @@ Chain.prototype._killFork = function _killFork(probe) {
|
|||||||
if (index === -1)
|
if (index === -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var hash = this.index.hashes[index];
|
hash = this.index.hashes[index];
|
||||||
this.index.hashes.splice(index, 1);
|
this.index.hashes.splice(index, 1);
|
||||||
this.index.ts.splice(index, 1);
|
this.index.ts.splice(index, 1);
|
||||||
this.index.heights.splice(index, 1);
|
this.index.heights.splice(index, 1);
|
||||||
@ -247,7 +256,6 @@ Chain.prototype._killFork = function _killFork(probe) {
|
|||||||
if (!this.storage)
|
if (!this.storage)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.storage.del(this.prefix + hash, function(err) {
|
this.storage.del(this.prefix + hash, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
@ -267,6 +275,8 @@ Chain.prototype.add = function add(block) {
|
|||||||
var res = false;
|
var res = false;
|
||||||
var err = null;
|
var err = null;
|
||||||
var initial = block;
|
var initial = block;
|
||||||
|
var hash, prev, prevProbe, range, hashes;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// No need to revalidate orphans
|
// No need to revalidate orphans
|
||||||
if (!res && !block.verify()) {
|
if (!res && !block.verify()) {
|
||||||
@ -274,8 +284,8 @@ Chain.prototype.add = function add(block) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = block.hash('hex');
|
hash = block.hash('hex');
|
||||||
var prev = block.prevBlock;
|
prev = block.prevBlock;
|
||||||
|
|
||||||
// If the block is already known to be an orphan
|
// If the block is already known to be an orphan
|
||||||
if (this.orphan.map[prev]) {
|
if (this.orphan.map[prev]) {
|
||||||
@ -283,7 +293,7 @@ Chain.prototype.add = function add(block) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prevProbe = this._probeIndex(prev, block.ts);
|
prevProbe = this._probeIndex(prev, block.ts);
|
||||||
|
|
||||||
// Remove forked nodes from storage, if shorter chain is detected
|
// Remove forked nodes from storage, if shorter chain is detected
|
||||||
if (this._killFork(prevProbe)) {
|
if (this._killFork(prevProbe)) {
|
||||||
@ -297,8 +307,9 @@ Chain.prototype.add = function add(block) {
|
|||||||
this.orphan.map[prev] = block;
|
this.orphan.map[prev] = block;
|
||||||
this.orphan.bmap[hash] = block;
|
this.orphan.bmap[hash] = block;
|
||||||
|
|
||||||
var range = this._getRange(hash, block.ts, true);
|
range = this._getRange(hash, block.ts, true);
|
||||||
var hashes = this.index.hashes.slice(range.start, range.end + 1);
|
hashes = this.index.hashes.slice(range.start, range.end + 1);
|
||||||
|
|
||||||
this.emit('missing', prev, hashes, block);
|
this.emit('missing', prev, hashes, block);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -334,6 +345,8 @@ Chain.prototype.add = function add(block) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype._compress = function compress() {
|
Chain.prototype._compress = function compress() {
|
||||||
|
var i;
|
||||||
|
|
||||||
// Keep at least 1000 blocks and at most 2000 by default
|
// Keep at least 1000 blocks and at most 2000 by default
|
||||||
if (this.block.list.length < this.cacheLimit)
|
if (this.block.list.length < this.cacheLimit)
|
||||||
return;
|
return;
|
||||||
@ -342,7 +355,7 @@ Chain.prototype._compress = function compress() {
|
|||||||
this.block.list = this.block.list.slice(-(this.cacheLimit / 2 | 0));
|
this.block.list = this.block.list.slice(-(this.cacheLimit / 2 | 0));
|
||||||
this.block.bloom.reset();
|
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]);
|
this._bloomBlock(this.block.list[i]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -351,21 +364,25 @@ Chain.prototype._bloomBlock = function _bloomBlock(block) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.has = function has(hash, noProbe, cb) {
|
Chain.prototype.has = function has(hash, noProbe, cb) {
|
||||||
|
var i;
|
||||||
|
|
||||||
if (typeof noProbe === 'function') {
|
if (typeof noProbe === 'function') {
|
||||||
cb = noProbe;
|
cb = noProbe;
|
||||||
noProbe = false;
|
noProbe = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
this.once('load', function() {
|
this.once('load', function() {
|
||||||
this.has(hash, noProbe, cb);
|
this.has(hash, noProbe, cb);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb = utils.asyncify(cb);
|
cb = utils.asyncify(cb);
|
||||||
|
|
||||||
if (this.block.bloom.test(hash, 'hex')) {
|
if (this.block.bloom.test(hash, 'hex')) {
|
||||||
if (this.strict) {
|
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)
|
if (this.block.list[i].hash('hex') === hash)
|
||||||
return cb(true);
|
return cb(true);
|
||||||
} else {
|
} else {
|
||||||
@ -406,6 +423,8 @@ Chain.prototype.hasOrphan = function hasOrphan(hash) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.get = function get(hash, force, cb) {
|
Chain.prototype.get = function get(hash, force, cb) {
|
||||||
|
var i, block;
|
||||||
|
|
||||||
if (typeof force === 'function') {
|
if (typeof force === 'function') {
|
||||||
cb = force;
|
cb = force;
|
||||||
force = false;
|
force = false;
|
||||||
@ -413,11 +432,11 @@ Chain.prototype.get = function get(hash, force, cb) {
|
|||||||
|
|
||||||
// Cached block found
|
// Cached block found
|
||||||
if (!force && this.block.bloom.test(hash, 'hex')) {
|
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) {
|
if (this.block.list[i].hash('hex') === hash) {
|
||||||
// NOTE: we return right after the statement - so `block` should be
|
// NOTE: we return right after the statement - so `block` should be
|
||||||
// valid at the time of nextTick call
|
// valid at the time of nextTick call
|
||||||
var block = this.block.list[i];
|
block = this.block.list[i];
|
||||||
bcoin.utils.nextTick(function() {
|
bcoin.utils.nextTick(function() {
|
||||||
cb(block);
|
cb(block);
|
||||||
});
|
});
|
||||||
@ -437,6 +456,7 @@ Chain.prototype.isFull = function isFull() {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var delta = (+new Date() / 1000) - this.index.ts[this.index.ts.length - 1];
|
var delta = (+new Date() / 1000) - this.index.ts[this.index.ts.length - 1];
|
||||||
|
|
||||||
return delta < 40 * 60;
|
return delta < 40 * 60;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -447,34 +467,42 @@ Chain.prototype.fillPercent = function fillPercent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
|
Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) {
|
||||||
|
var ts, hashes, heights, zip, i, count;
|
||||||
|
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
this.once('load', function() {
|
this.once('load', function() {
|
||||||
this.hashesInRange(start, end, cb);
|
this.hashesInRange(start, end, cb);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb = utils.asyncify(cb);
|
cb = utils.asyncify(cb);
|
||||||
var ts = this.index.ts;
|
ts = this.index.ts;
|
||||||
|
|
||||||
start = utils.binaryInsert(ts, start, compareTs, true);
|
start = utils.binaryInsert(ts, start, compareTs, true);
|
||||||
if (start > 0 && ts[start - 1] >= start)
|
if (start > 0 && ts[start - 1] >= start)
|
||||||
start--;
|
start--;
|
||||||
|
|
||||||
end = utils.binaryInsert(ts, end, compareTs, true);
|
end = utils.binaryInsert(ts, end, compareTs, true);
|
||||||
|
|
||||||
// Zip hashes and heights together and sort them by height
|
// Zip hashes and heights together and sort them by height
|
||||||
var hashes = this.index.hashes.slice(start, end);
|
hashes = this.index.hashes.slice(start, end);
|
||||||
var heights = this.index.heights.slice(start, end);
|
heights = this.index.heights.slice(start, end);
|
||||||
var zip = [];
|
zip = [];
|
||||||
for (var i = 0; i < hashes.length; i++)
|
|
||||||
|
for (i = 0; i < hashes.length; i++)
|
||||||
zip.push({ hash: hashes[i], height: heights[i] });
|
zip.push({ hash: hashes[i], height: heights[i] });
|
||||||
|
|
||||||
zip = zip.sort(function(a, b) {
|
zip = zip.sort(function(a, b) {
|
||||||
return a.height - b.height;
|
return a.height - b.height;
|
||||||
});
|
});
|
||||||
var hashes = zip.map(function(a) {
|
|
||||||
|
hashes = zip.map(function(a) {
|
||||||
return a.hash;
|
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);
|
return cb(hashes, count);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -538,14 +566,13 @@ Chain.prototype.locatorHashes = function(start) {
|
|||||||
|
|
||||||
Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {
|
Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var root = hash;
|
||||||
|
|
||||||
if (Array.isArray(hash))
|
if (Array.isArray(hash))
|
||||||
hash = utils.toHex(hash);
|
hash = utils.toHex(hash);
|
||||||
else if (hash.hash)
|
else if (hash.hash)
|
||||||
hash = hash.hash('hex');
|
hash = hash.hash('hex');
|
||||||
|
|
||||||
var root = hash;
|
|
||||||
|
|
||||||
while (this.orphan.bmap[hash]) {
|
while (this.orphan.bmap[hash]) {
|
||||||
root = hash;
|
root = hash;
|
||||||
hash = this.orphan.bmap[hash].prevBlock;
|
hash = this.orphan.bmap[hash].prevBlock;
|
||||||
@ -580,11 +607,17 @@ Chain.prototype.toJSON = function toJSON() {
|
|||||||
|
|
||||||
var lastTs = 0;
|
var lastTs = 0;
|
||||||
var lastHeight = -1000;
|
var lastHeight = -1000;
|
||||||
for (var i = 0; i < this.index.ts.length - keep; i++) {
|
var i, ts, delta, hdelta;
|
||||||
var ts = this.index.ts[i];
|
|
||||||
var delta = ts < 1356984000 ? delta1 :
|
for (i = 0; i < this.index.ts.length - keep; i++) {
|
||||||
ts < 1388520000 ? delta2 : delta3;
|
ts = this.index.ts[i];
|
||||||
var hdelta = this.index.heights[i] - lastHeight;
|
|
||||||
|
delta = ts < 1356984000
|
||||||
|
? delta1
|
||||||
|
: ts < 1388520000 ? delta2 : delta3;
|
||||||
|
|
||||||
|
hdelta = this.index.heights[i] - lastHeight;
|
||||||
|
|
||||||
if (ts - lastTs < delta && hdelta < 250)
|
if (ts - lastTs < delta && hdelta < 250)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -606,13 +639,18 @@ Chain.prototype.toJSON = function toJSON() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.fromJSON = function fromJSON(json) {
|
Chain.prototype.fromJSON = function fromJSON(json) {
|
||||||
|
var i;
|
||||||
|
|
||||||
assert.equal(json.v, 1);
|
assert.equal(json.v, 1);
|
||||||
assert.equal(json.type, 'chain');
|
assert.equal(json.type, 'chain');
|
||||||
|
|
||||||
if (json.network)
|
if (json.network)
|
||||||
assert.equal(json.network, network.type);
|
assert.equal(json.network, network.type);
|
||||||
|
|
||||||
this.index.hashes = json.hashes.slice();
|
this.index.hashes = json.hashes.slice();
|
||||||
this.index.ts = json.ts.slice();
|
this.index.ts = json.ts.slice();
|
||||||
this.index.heights = json.heights.slice();
|
this.index.heights = json.heights.slice();
|
||||||
|
|
||||||
if (this.index.bloom)
|
if (this.index.bloom)
|
||||||
this.index.bloom.reset();
|
this.index.bloom.reset();
|
||||||
else
|
else
|
||||||
@ -621,7 +659,8 @@ Chain.prototype.fromJSON = function fromJSON(json) {
|
|||||||
if (this.index.hashes.length === 0)
|
if (this.index.hashes.length === 0)
|
||||||
this.add(new bcoin.block(network.genesis, 'block'));
|
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');
|
this.index.bloom.add(this.index.hashes[i], 'hex');
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Chain;
|
||||||
|
|||||||
110
lib/bcoin/hd.js
110
lib/bcoin/hd.js
@ -96,13 +96,14 @@ HDSeed._entropy = function(size) {
|
|||||||
|
|
||||||
HDSeed._mnemonic = function(entropy) {
|
HDSeed._mnemonic = function(entropy) {
|
||||||
var bin = '';
|
var bin = '';
|
||||||
for (var i = 0; i < entropy.length; i++) {
|
|
||||||
bin = bin + ('00000000' + entropy[i].toString(2)).slice(-8);
|
|
||||||
}
|
|
||||||
|
|
||||||
var mnemonic = [];
|
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++) {
|
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]);
|
mnemonic.push(english[wi]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +232,7 @@ HDPriv.prototype._unbuild = function(xkey) {
|
|||||||
var raw = utils.fromBase58(xkey);
|
var raw = utils.fromBase58(xkey);
|
||||||
var data = {};
|
var data = {};
|
||||||
var off = 0;
|
var off = 0;
|
||||||
|
var hash;
|
||||||
|
|
||||||
data.version = utils.readU32BE(raw, off);
|
data.version = utils.readU32BE(raw, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
@ -248,7 +250,7 @@ HDPriv.prototype._unbuild = function(xkey) {
|
|||||||
data.checksum = utils.readU32BE(raw, off);
|
data.checksum = utils.readU32BE(raw, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
|
|
||||||
var hash = utils.dsha256(raw.slice(0, -4));
|
hash = utils.dsha256(raw.slice(0, -4));
|
||||||
if (data.checksum !== utils.readU32BE(hash, 0))
|
if (data.checksum !== utils.readU32BE(hash, 0))
|
||||||
throw new Error('checksum mismatch');
|
throw new Error('checksum mismatch');
|
||||||
|
|
||||||
@ -258,6 +260,7 @@ HDPriv.prototype._unbuild = function(xkey) {
|
|||||||
HDPriv.prototype._build = function(data) {
|
HDPriv.prototype._build = function(data) {
|
||||||
var sequence = [];
|
var sequence = [];
|
||||||
var off = 0;
|
var off = 0;
|
||||||
|
var checksum, xprivkey, pair, privateKey, publicKey, size, fingerPrint;
|
||||||
|
|
||||||
utils.writeU32BE(sequence, data.version, off);
|
utils.writeU32BE(sequence, data.version, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
@ -273,18 +276,18 @@ HDPriv.prototype._build = function(data) {
|
|||||||
off += 1;
|
off += 1;
|
||||||
utils.copy(data.privateKey, sequence, off, true);
|
utils.copy(data.privateKey, sequence, off, true);
|
||||||
off += data.privateKey.length;
|
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);
|
utils.copy(checksum, sequence, off, true);
|
||||||
off += 4;
|
off += 4;
|
||||||
|
|
||||||
var xprivkey = utils.toBase58(sequence);
|
xprivkey = utils.toBase58(sequence);
|
||||||
|
|
||||||
var pair = bcoin.ecdsa.keyPair({ priv: data.privateKey });
|
pair = bcoin.ecdsa.keyPair({ priv: data.privateKey });
|
||||||
var privateKey = pair.getPrivate().toArray();
|
privateKey = pair.getPrivate().toArray();
|
||||||
var publicKey = pair.getPublic(true, 'array');
|
publicKey = pair.getPublic(true, 'array');
|
||||||
|
|
||||||
var size = PARENT_FINGER_PRINT_SIZE;
|
size = PARENT_FINGER_PRINT_SIZE;
|
||||||
var fingerPrint = utils.ripesha(publicKey).slice(0, size);
|
fingerPrint = utils.ripesha(publicKey).slice(0, size);
|
||||||
|
|
||||||
this.version = data.version;
|
this.version = data.version;
|
||||||
this.depth = data.depth;
|
this.depth = data.depth;
|
||||||
@ -307,22 +310,24 @@ HDPriv.prototype.derive = function(index, hard) {
|
|||||||
if (typeof index === 'string')
|
if (typeof index === 'string')
|
||||||
return this.deriveString(index);
|
return this.deriveString(index);
|
||||||
|
|
||||||
|
var index_ = [];
|
||||||
|
var data, hash, leftPart, chainCode, privateKey;
|
||||||
|
|
||||||
hard = index >= HARDENED ? true : hard;
|
hard = index >= HARDENED ? true : hard;
|
||||||
if (index < HARDENED && hard === true)
|
if (index < HARDENED && hard === true)
|
||||||
index += HARDENED;
|
index += HARDENED;
|
||||||
|
|
||||||
var index_ = [];
|
|
||||||
utils.writeU32BE(index_, index, 0);
|
utils.writeU32BE(index_, index, 0);
|
||||||
|
|
||||||
var data = hard
|
data = hard
|
||||||
? [0].concat(this.privateKey).concat(index_)
|
? [0].concat(this.privateKey).concat(index_)
|
||||||
: data = [].concat(this.publicKey).concat(index_);
|
: data = [].concat(this.publicKey).concat(index_);
|
||||||
|
|
||||||
var hash = sha512hmac(data, this.chainCode);
|
hash = sha512hmac(data, this.chainCode);
|
||||||
var leftPart = new bn(hash.slice(0, 32));
|
leftPart = new bn(hash.slice(0, 32));
|
||||||
var chainCode = hash.slice(32, 64);
|
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({
|
return new HDPriv({
|
||||||
master: this.master,
|
master: this.master,
|
||||||
@ -340,6 +345,7 @@ HDPriv._getIndexes = function(path) {
|
|||||||
var steps = path.split('/');
|
var steps = path.split('/');
|
||||||
var root = steps.shift();
|
var root = steps.shift();
|
||||||
var indexes = [];
|
var indexes = [];
|
||||||
|
var i, step, hard, index;
|
||||||
|
|
||||||
if (~PATH_ROOTS.indexOf(path))
|
if (~PATH_ROOTS.indexOf(path))
|
||||||
return indexes;
|
return indexes;
|
||||||
@ -347,9 +353,9 @@ HDPriv._getIndexes = function(path) {
|
|||||||
if (!~PATH_ROOTS.indexOf(root))
|
if (!~PATH_ROOTS.indexOf(root))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
for (var i = 0; i < steps.length; i++) {
|
for (i = 0; i < steps.length; i++) {
|
||||||
var step = steps[i];
|
step = steps[i];
|
||||||
var hard = step[step.length - 1] === '\'';
|
hard = step[step.length - 1] === '\'';
|
||||||
|
|
||||||
if (hard)
|
if (hard)
|
||||||
step = step.slice(0, -1);
|
step = step.slice(0, -1);
|
||||||
@ -357,7 +363,7 @@ HDPriv._getIndexes = function(path) {
|
|||||||
if (!step || step[0] === '-')
|
if (!step || step[0] === '-')
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var index = +step;
|
index = +step;
|
||||||
if (hard)
|
if (hard)
|
||||||
index += HARDENED;
|
index += HARDENED;
|
||||||
|
|
||||||
@ -399,18 +405,17 @@ HDPriv.prototype.deriveString = function(path) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function HDPub(options) {
|
function HDPub(options) {
|
||||||
|
var data;
|
||||||
|
|
||||||
if (!(this instanceof HDPub))
|
if (!(this instanceof HDPub))
|
||||||
return new HDPub(options);
|
return new HDPub(options);
|
||||||
|
|
||||||
var data;
|
|
||||||
|
|
||||||
if (typeof options === 'string' && options.indexOf('xpub') === 0)
|
if (typeof options === 'string' && options.indexOf('xpub') === 0)
|
||||||
options = { xkey: options };
|
options = { xkey: options };
|
||||||
|
|
||||||
if (options.xkey)
|
data = options.xkey
|
||||||
data = this._unbuild(options.xkey);
|
? this._unbuild(options.xkey)
|
||||||
else
|
: options;
|
||||||
data = options;
|
|
||||||
|
|
||||||
data = this._normalize(data, network.prefixes.xpubkey);
|
data = this._normalize(data, network.prefixes.xpubkey);
|
||||||
|
|
||||||
@ -425,6 +430,7 @@ HDPub.prototype._unbuild = function(xkey) {
|
|||||||
var raw = utils.fromBase58(xkey);
|
var raw = utils.fromBase58(xkey);
|
||||||
var data = {};
|
var data = {};
|
||||||
var off = 0;
|
var off = 0;
|
||||||
|
var hash;
|
||||||
|
|
||||||
data.version = utils.readU32BE(raw, off);
|
data.version = utils.readU32BE(raw, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
@ -441,7 +447,7 @@ HDPub.prototype._unbuild = function(xkey) {
|
|||||||
data.checksum = utils.readU32BE(raw, off);
|
data.checksum = utils.readU32BE(raw, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
|
|
||||||
var hash = utils.dsha256(raw.slice(0, -4));
|
hash = utils.dsha256(raw.slice(0, -4));
|
||||||
if (data.checksum !== utils.readU32BE(hash, 0))
|
if (data.checksum !== utils.readU32BE(hash, 0))
|
||||||
throw new Error('checksum mismatch');
|
throw new Error('checksum mismatch');
|
||||||
|
|
||||||
@ -451,6 +457,7 @@ HDPub.prototype._unbuild = function(xkey) {
|
|||||||
HDPub.prototype._build = function(data) {
|
HDPub.prototype._build = function(data) {
|
||||||
var sequence = [];
|
var sequence = [];
|
||||||
var off = 0;
|
var off = 0;
|
||||||
|
var checksum, xpubkey, publicKey, size, fingerPrint;
|
||||||
|
|
||||||
utils.writeU32BE(sequence, data.version, off);
|
utils.writeU32BE(sequence, data.version, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
@ -464,7 +471,7 @@ HDPub.prototype._build = function(data) {
|
|||||||
off += data.chainCode.length;
|
off += data.chainCode.length;
|
||||||
utils.copy(data.publicKey, sequence, off, true);
|
utils.copy(data.publicKey, sequence, off, true);
|
||||||
off += data.publicKey.length;
|
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);
|
utils.copy(checksum, sequence, off, true);
|
||||||
off += 4;
|
off += 4;
|
||||||
|
|
||||||
@ -473,11 +480,11 @@ HDPub.prototype._build = function(data) {
|
|||||||
else if (utils.toHex(checksum) !== utils.toHex(data.checksum))
|
else if (utils.toHex(checksum) !== utils.toHex(data.checksum))
|
||||||
throw new Error('checksum mismatch');
|
throw new Error('checksum mismatch');
|
||||||
|
|
||||||
var xpubkey = utils.toBase58(sequence);
|
xpubkey = utils.toBase58(sequence);
|
||||||
|
|
||||||
var publicKey = data.publicKey;
|
publicKey = data.publicKey;
|
||||||
var size = PARENT_FINGER_PRINT_SIZE;
|
size = PARENT_FINGER_PRINT_SIZE;
|
||||||
var fingerPrint = utils.ripesha(publicKey).slice(0, size);
|
fingerPrint = utils.ripesha(publicKey).slice(0, size);
|
||||||
|
|
||||||
this.version = data.version;
|
this.version = data.version;
|
||||||
this.depth = data.depth;
|
this.depth = data.depth;
|
||||||
@ -495,6 +502,9 @@ HDPub.prototype._build = function(data) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HDPub.prototype.derive = function(index, hard) {
|
HDPub.prototype.derive = function(index, hard) {
|
||||||
|
var index_ = [];
|
||||||
|
var data, hash, leftPart, chainCode, pair, pubkeyPoint, publicKey;
|
||||||
|
|
||||||
if (typeof index === 'string')
|
if (typeof index === 'string')
|
||||||
return this.deriveString(index);
|
return this.deriveString(index);
|
||||||
|
|
||||||
@ -504,17 +514,16 @@ HDPub.prototype.derive = function(index, hard) {
|
|||||||
if (index < 0)
|
if (index < 0)
|
||||||
throw new Error('invalid path');
|
throw new Error('invalid path');
|
||||||
|
|
||||||
var index_ = [];
|
|
||||||
utils.writeU32BE(index_, index, 0);
|
utils.writeU32BE(index_, index, 0);
|
||||||
|
|
||||||
var data = [].concat(this.publicKey).concat(index_);
|
data = [].concat(this.publicKey).concat(index_);
|
||||||
var hash = sha512hmac(data, this.chainCode);
|
hash = sha512hmac(data, this.chainCode);
|
||||||
var leftPart = new bn(hash.slice(0, 32));
|
leftPart = new bn(hash.slice(0, 32));
|
||||||
var chainCode = hash.slice(32, 64);
|
chainCode = hash.slice(32, 64);
|
||||||
|
|
||||||
var pair = bcoin.ecdsa.keyPair({ pub: this.publicKey });
|
pair = bcoin.ecdsa.keyPair({ pub: this.publicKey });
|
||||||
var pubkeyPoint = ec.curve.g.mul(leftPart).add(pair.pub);
|
pubkeyPoint = ec.curve.g.mul(leftPart).add(pair.pub);
|
||||||
var publicKey = bcoin.ecdsa.keyFromPublic(pubkeyPoint).getPublic(true, 'array');
|
publicKey = bcoin.ecdsa.keyFromPublic(pubkeyPoint).getPublic(true, 'array');
|
||||||
|
|
||||||
return new HDPub({
|
return new HDPub({
|
||||||
// version: network.prefixes.xpubkey,
|
// version: network.prefixes.xpubkey,
|
||||||
@ -610,8 +619,8 @@ function sha512hmac(data, salt) {
|
|||||||
function randomBytes(size) {
|
function randomBytes(size) {
|
||||||
if (isBrowser) {
|
if (isBrowser) {
|
||||||
var a = Uint8Array(size);
|
var a = Uint8Array(size);
|
||||||
(window.crypto || window.msCrypto).getRandomValues(a);
|
|
||||||
var buf = new Array(size);
|
var buf = new Array(size);
|
||||||
|
(window.crypto || window.msCrypto).getRandomValues(a);
|
||||||
utils.copy(a, buf, 0);
|
utils.copy(a, buf, 0);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@ -629,6 +638,7 @@ function pbkdf2(key, salt, iterations, dkLen) {
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var hLen = 64;
|
var hLen = 64;
|
||||||
|
|
||||||
if (dkLen > (Math.pow(2, 32) - 1) * hLen)
|
if (dkLen > (Math.pow(2, 32) - 1) * hLen)
|
||||||
throw Error('Requested key length too long');
|
throw Error('Requested key length too long');
|
||||||
|
|
||||||
@ -652,9 +662,11 @@ function pbkdf2(key, salt, iterations, dkLen) {
|
|||||||
var l = Math.ceil(dkLen / hLen);
|
var l = Math.ceil(dkLen / hLen);
|
||||||
var r = dkLen - (l - 1) * hLen;
|
var r = dkLen - (l - 1) * hLen;
|
||||||
|
|
||||||
|
var i, j, k, destPos, len;
|
||||||
|
|
||||||
utils.copy(salt.slice(0, salt.length), block1, 0);
|
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 + 0] = (i >> 24 & 0xff);
|
||||||
block1[salt.length + 1] = (i >> 16 & 0xff);
|
block1[salt.length + 1] = (i >> 16 & 0xff);
|
||||||
block1[salt.length + 2] = (i >> 8 & 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);
|
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);
|
U = sha512hmac(U, key);
|
||||||
|
|
||||||
for (var k = 0; k < hLen; k++)
|
for (k = 0; k < hLen; k++)
|
||||||
T[k] ^= U[k];
|
T[k] ^= U[k];
|
||||||
}
|
}
|
||||||
|
|
||||||
var destPos = (i - 1) * hLen;
|
destPos = (i - 1) * hLen;
|
||||||
var len = (i === l ? r : hLen);
|
len = (i === l ? r : hLen);
|
||||||
utils.copy(T.slice(0, len), DK, 0);
|
utils.copy(T.slice(0, len), DK, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,8 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Peer(pool, createSocket, options) {
|
function Peer(pool, createSocket, options) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!(this instanceof Peer))
|
if (!(this instanceof Peer))
|
||||||
return new Peer(pool, createSocket, options);
|
return new Peer(pool, createSocket, options);
|
||||||
|
|
||||||
@ -31,7 +33,6 @@ function Peer(pool, createSocket, options) {
|
|||||||
this.ts = this.options.ts || 0;
|
this.ts = this.options.ts || 0;
|
||||||
|
|
||||||
if (this.options.backoff) {
|
if (this.options.backoff) {
|
||||||
var self = this;
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
self.socket = createSocket(self);
|
self.socket = createSocket(self);
|
||||||
self.emit('socket');
|
self.emit('socket');
|
||||||
@ -65,26 +66,32 @@ function Peer(pool, createSocket, options) {
|
|||||||
else
|
else
|
||||||
this.once('socket', this._init);
|
this.once('socket', this._init);
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(Peer, EventEmitter);
|
inherits(Peer, EventEmitter);
|
||||||
module.exports = Peer;
|
|
||||||
|
|
||||||
Peer.prototype._init = function init() {
|
Peer.prototype._init = function init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.socket.once('connect', function() {
|
this.socket.once('connect', function() {
|
||||||
self.ts = Date.now() / 1000 | 0;
|
self.ts = Date.now() / 1000 | 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.once('error', function(err) {
|
this.socket.once('error', function(err) {
|
||||||
self._error(err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.once('close', function() {
|
this.socket.once('close', function() {
|
||||||
self._error('socket hangup');
|
self._error('socket hangup');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('data', function(chunk) {
|
this.socket.on('data', function(chunk) {
|
||||||
self.parser.feed(chunk);
|
self.parser.feed(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.parser.on('packet', function(packet) {
|
this.parser.on('packet', function(packet) {
|
||||||
self._onPacket(packet);
|
self._onPacket(packet);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.parser.on('error', function(err) {
|
this.parser.on('error', function(err) {
|
||||||
self._error(err);
|
self._error(err);
|
||||||
});
|
});
|
||||||
@ -134,13 +141,16 @@ Peer.prototype.startSync = function startSync() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Peer.prototype.broadcast = function broadcast(items) {
|
Peer.prototype.broadcast = function broadcast(items) {
|
||||||
|
var self = this;
|
||||||
|
var result;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!Array.isArray(items))
|
if (!Array.isArray(items))
|
||||||
items = [ items ];
|
items = [ items ];
|
||||||
|
|
||||||
var self = this;
|
result = items.map(function(item) {
|
||||||
var result = items.map(function(item) {
|
|
||||||
var key = item.hash('hex');
|
var key = item.hash('hex');
|
||||||
var old = this._broadcast.map[key];
|
var old = this._broadcast.map[key];
|
||||||
if (old) {
|
if (old) {
|
||||||
@ -195,8 +205,11 @@ Peer.prototype.updateWatch = function updateWatch() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Peer.prototype.destroy = function destroy() {
|
Peer.prototype.destroy = function destroy() {
|
||||||
|
var i;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this.socket)
|
if (!this.socket)
|
||||||
return this.once('socket', this.destroy);
|
return this.once('socket', this.destroy);
|
||||||
|
|
||||||
@ -214,7 +227,7 @@ Peer.prototype.destroy = function destroy() {
|
|||||||
clearInterval(this._ping.timer);
|
clearInterval(this._ping.timer);
|
||||||
this._ping.timer = null;
|
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);
|
clearTimeout(this._request.queue[i].timer);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -223,11 +236,13 @@ Peer.prototype.destroy = function destroy() {
|
|||||||
Peer.prototype._write = function write(chunk) {
|
Peer.prototype._write = function write(chunk) {
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this.socket) {
|
if (!this.socket) {
|
||||||
return this.once('socket', function() {
|
return this.once('socket', function() {
|
||||||
this._write(chunk);
|
this._write(chunk);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NodeBuffer)
|
if (NodeBuffer)
|
||||||
this.socket.write(new NodeBuffer(chunk));
|
this.socket.write(new NodeBuffer(chunk));
|
||||||
else
|
else
|
||||||
@ -237,15 +252,17 @@ Peer.prototype._write = function write(chunk) {
|
|||||||
Peer.prototype._error = function error(err) {
|
Peer.prototype._error = function error(err) {
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.emit('error', typeof err === 'string' ? new Error(err) : err);
|
this.emit('error', typeof err === 'string' ? new Error(err) : err);
|
||||||
};
|
};
|
||||||
|
|
||||||
Peer.prototype._req = function _req(cmd, cb) {
|
Peer.prototype._req = function _req(cmd, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return cb(new Error('Destroyed, sorry'));
|
return cb(new Error('Destroyed, sorry'));
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var entry = {
|
var entry = {
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
cb: cb,
|
cb: cb,
|
||||||
@ -258,19 +275,24 @@ Peer.prototype._req = function _req(cmd, cb) {
|
|||||||
},
|
},
|
||||||
timer: null
|
timer: null
|
||||||
};
|
};
|
||||||
|
|
||||||
entry.timer = setTimeout(entry.ontimeout, this._request.timeout);
|
entry.timer = setTimeout(entry.ontimeout, this._request.timeout);
|
||||||
|
|
||||||
this._request.queue.push(entry);
|
this._request.queue.push(entry);
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
Peer.prototype._res = function _res(cmd, payload) {
|
Peer.prototype._res = function _res(cmd, payload) {
|
||||||
for (var i = 0; i < this._request.queue.length; i++) {
|
var i, entry, res;
|
||||||
var entry = this._request.queue[i];
|
|
||||||
|
for (i = 0; i < this._request.queue.length; i++) {
|
||||||
|
entry = this._request.queue[i];
|
||||||
|
|
||||||
if (!entry || entry.cmd && entry.cmd !== cmd)
|
if (!entry || entry.cmd && entry.cmd !== cmd)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var res = entry.cb(null, payload, cmd);
|
res = entry.cb(null, payload, cmd);
|
||||||
|
|
||||||
if (res === this._request.cont) {
|
if (res === this._request.cont) {
|
||||||
assert(!entry.cmd);
|
assert(!entry.cmd);
|
||||||
@ -388,6 +410,7 @@ Peer.prototype._handleGetAddr = function handleGetAddr() {
|
|||||||
this.pool.peers.block,
|
this.pool.peers.block,
|
||||||
this.pool.peers.load
|
this.pool.peers.load
|
||||||
).filter(Boolean);
|
).filter(Boolean);
|
||||||
|
var addrs;
|
||||||
|
|
||||||
// NOTE: For IPv6 BTC uses:
|
// NOTE: For IPv6 BTC uses:
|
||||||
// '0000:0000:0000:0000:0000:xxxx:xxxx:ffff'
|
// '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) {
|
if (peer.ver === 6) {
|
||||||
while (peer.ipv6.split(':').length < 8)
|
while (peer.ipv6.split(':').length < 8)
|
||||||
peer.ipv6 = '0000:' + peer.ipv6;
|
peer.ipv6 = '0000:' + peer.ipv6;
|
||||||
@ -450,16 +473,18 @@ Peer.prototype._handleInv = function handleInv(items) {
|
|||||||
return item.hash;
|
return item.hash;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var req, i, block, hash;
|
||||||
|
|
||||||
if (blocks.length === 1)
|
if (blocks.length === 1)
|
||||||
this.bestBlock = utils.toHex(blocks[0]);
|
this.bestBlock = utils.toHex(blocks[0]);
|
||||||
|
|
||||||
this.emit('blocks', blocks);
|
this.emit('blocks', blocks);
|
||||||
|
|
||||||
if (this.pool.options.fullNode) {
|
if (this.pool.options.fullNode) {
|
||||||
var req = [];
|
req = [];
|
||||||
for (var i = 0; i < blocks.length; i++) {
|
for (i = 0; i < blocks.length; i++) {
|
||||||
var block = blocks[i];
|
block = blocks[i];
|
||||||
var hash = utils.toHex(block);
|
hash = utils.toHex(block);
|
||||||
if (this.chain.hasOrphan(hash)) {
|
if (this.chain.hasOrphan(hash)) {
|
||||||
this.loadBlocks(this.chain.locatorHashes(), this.chain.getOrphanRoot(hash));
|
this.loadBlocks(this.chain.locatorHashes(), this.chain.getOrphanRoot(hash));
|
||||||
continue;
|
continue;
|
||||||
@ -507,3 +532,5 @@ Peer.prototype.loadHeaders = function loadHeaders(hashes, stop) {
|
|||||||
Peer.prototype.loadBlocks = function loadBlocks(hashes, stop) {
|
Peer.prototype.loadBlocks = function loadBlocks(hashes, stop) {
|
||||||
this._write(this.framer.getBlocks(hashes, stop));
|
this._write(this.framer.getBlocks(hashes, stop));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Peer;
|
||||||
|
|||||||
@ -29,10 +29,12 @@ function Pool(options) {
|
|||||||
this.size = options.size || 32;
|
this.size = options.size || 32;
|
||||||
this.parallel = options.parallel || 2000;
|
this.parallel = options.parallel || 2000;
|
||||||
this.redundancy = options.redundancy || 2;
|
this.redundancy = options.redundancy || 2;
|
||||||
|
|
||||||
this.backoff = {
|
this.backoff = {
|
||||||
delta: options.backoffDelta || 500,
|
delta: options.backoffDelta || 500,
|
||||||
max: options.backoffMax || 5000
|
max: options.backoffMax || 5000
|
||||||
};
|
};
|
||||||
|
|
||||||
this.load = {
|
this.load = {
|
||||||
timeout: options.loadTimeout || 3000,
|
timeout: options.loadTimeout || 3000,
|
||||||
interval: options.loadInterval || 5000,
|
interval: options.loadInterval || 5000,
|
||||||
@ -44,8 +46,10 @@ function Pool(options) {
|
|||||||
hwm: options.hwm || this.parallel * 8,
|
hwm: options.hwm || this.parallel * 8,
|
||||||
hiReached: false
|
hiReached: false
|
||||||
};
|
};
|
||||||
|
|
||||||
this.maxRetries = options.maxRetries || 42;
|
this.maxRetries = options.maxRetries || 42;
|
||||||
this.requestTimeout = options.requestTimeout || 10000;
|
this.requestTimeout = options.requestTimeout || 10000;
|
||||||
|
|
||||||
this.chain = new bcoin.chain({
|
this.chain = new bcoin.chain({
|
||||||
storage: this.storage,
|
storage: this.storage,
|
||||||
// Since regular blocks contain transactions and full merkle
|
// Since regular blocks contain transactions and full merkle
|
||||||
@ -54,10 +58,14 @@ function Pool(options) {
|
|||||||
fullNode: this.options.fullNode,
|
fullNode: this.options.fullNode,
|
||||||
startHeight: this.options.startHeight
|
startHeight: this.options.startHeight
|
||||||
});
|
});
|
||||||
|
|
||||||
this.watchMap = {};
|
this.watchMap = {};
|
||||||
this.bloom = new bcoin.bloom(8 * 1024,
|
|
||||||
10,
|
this.bloom = new bcoin.bloom(
|
||||||
(Math.random() * 0xffffffff) | 0);
|
8 * 1024,
|
||||||
|
10,
|
||||||
|
(Math.random() * 0xffffffff) | 0
|
||||||
|
);
|
||||||
|
|
||||||
this.bestHeight = 0;
|
this.bestHeight = 0;
|
||||||
this.bestBlock = null;
|
this.bestBlock = null;
|
||||||
@ -72,14 +80,17 @@ function Pool(options) {
|
|||||||
// Peers that are loading block ids
|
// Peers that are loading block ids
|
||||||
load: null
|
load: null
|
||||||
};
|
};
|
||||||
|
|
||||||
this.block = {
|
this.block = {
|
||||||
lastHash: null
|
lastHash: null
|
||||||
};
|
};
|
||||||
|
|
||||||
this.request = {
|
this.request = {
|
||||||
map: {},
|
map: {},
|
||||||
active: 0,
|
active: 0,
|
||||||
queue: []
|
queue: []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.validate = {
|
this.validate = {
|
||||||
// 5 days scan delta for obtaining TXs
|
// 5 days scan delta for obtaining TXs
|
||||||
delta: 5 * 24 * 3600,
|
delta: 5 * 24 * 3600,
|
||||||
@ -117,15 +128,18 @@ function Pool(options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(Pool, EventEmitter);
|
inherits(Pool, EventEmitter);
|
||||||
module.exports = Pool;
|
|
||||||
|
|
||||||
Pool.prototype._init = function _init() {
|
Pool.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var i;
|
||||||
|
|
||||||
this._addLoader();
|
this._addLoader();
|
||||||
for (var i = 0; i < this.size; i++)
|
|
||||||
|
for (i = 0; i < this.size; i++)
|
||||||
this._addPeer(0);
|
this._addPeer(0);
|
||||||
|
|
||||||
this._load();
|
this._load();
|
||||||
|
|
||||||
this.chain.on('missing', function(hash, preload, parent) {
|
this.chain.on('missing', function(hash, preload, parent) {
|
||||||
@ -141,19 +155,22 @@ Pool.prototype._init = function _init() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._addLoader = function _addLoader() {
|
Pool.prototype._addLoader = function _addLoader() {
|
||||||
|
var self = this;
|
||||||
|
var peer, interval, timer;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.peers.load !== null)
|
if (this.peers.load !== null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var peer = new bcoin.peer(this, this.createSocket, {
|
peer = new bcoin.peer(this, this.createSocket, {
|
||||||
backoff: 750 * Math.random(),
|
backoff: 750 * Math.random(),
|
||||||
startHeight: this.options.startHeight,
|
startHeight: this.options.startHeight,
|
||||||
relay: this.options.relay
|
relay: this.options.relay
|
||||||
});
|
});
|
||||||
this.peers.load = peer;
|
this.peers.load = peer;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
peer.on('error', function(err) {
|
peer.on('error', function(err) {
|
||||||
self.emit('error', err, peer);
|
self.emit('error', err, peer);
|
||||||
});
|
});
|
||||||
@ -168,7 +185,7 @@ Pool.prototype._addLoader = function _addLoader() {
|
|||||||
self._addLoader();
|
self._addLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
var interval = setInterval(function() {
|
interval = setInterval(function() {
|
||||||
self._load();
|
self._load();
|
||||||
}, this.load.interval);
|
}, this.load.interval);
|
||||||
|
|
||||||
@ -178,7 +195,8 @@ Pool.prototype._addLoader = function _addLoader() {
|
|||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.options.fullNode) return;
|
if (this.options.fullNode)
|
||||||
|
return;
|
||||||
|
|
||||||
function destroy() {
|
function destroy() {
|
||||||
// Chain is full and up-to-date
|
// Chain is full and up-to-date
|
||||||
@ -191,7 +209,8 @@ Pool.prototype._addLoader = function _addLoader() {
|
|||||||
|
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
}
|
}
|
||||||
var timer = setTimeout(destroy, this.load.timeout);
|
|
||||||
|
timer = setTimeout(destroy, this.load.timeout);
|
||||||
|
|
||||||
// Split blocks and request them using multiple peers
|
// Split blocks and request them using multiple peers
|
||||||
peer.on('blocks', function(hashes) {
|
peer.on('blocks', function(hashes) {
|
||||||
@ -236,7 +255,11 @@ Pool.prototype.isFull = function isFull() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._loadRange = function _loadRange(hashes, force) {
|
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)
|
if (!hashes)
|
||||||
return;
|
return;
|
||||||
@ -245,29 +268,33 @@ Pool.prototype._loadRange = function _loadRange(hashes, force) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Limit number of requests
|
// Limit number of requests
|
||||||
var now = +new Date();
|
|
||||||
if (!force && now - this.load.lastRange < this.load.rangeWindow)
|
if (!force && now - this.load.lastRange < this.load.rangeWindow)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.load.lastRange = now;
|
this.load.lastRange = now;
|
||||||
|
|
||||||
if (!this.peers.load)
|
if (!this.peers.load)
|
||||||
this._addLoader();
|
this._addLoader();
|
||||||
|
|
||||||
var last = hashes[hashes.length - 1];
|
last = hashes[hashes.length - 1];
|
||||||
|
|
||||||
hashes.slice(0, -1).forEach(function(hash) {
|
hashes.slice(0, -1).forEach(function(hash) {
|
||||||
this.peers.load.loadBlocks([ hash ], last);
|
this.peers.load.loadBlocks([ hash ], last);
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._load = function _load() {
|
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) {
|
if (this.request.queue.length >= this.load.hwm) {
|
||||||
this.load.hiReached = true;
|
this.load.hiReached = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.load.hiReached = false;
|
this.load.hiReached = false;
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Load more blocks, starting from last hash
|
// Load more blocks, starting from last hash
|
||||||
if (this.block.lastHash)
|
if (this.block.lastHash)
|
||||||
@ -286,12 +313,16 @@ Pool.prototype._load = function _load() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._addPeer = function _addPeer(backoff) {
|
Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||||
|
var self = this;
|
||||||
|
var peer;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.peers.block.length + this.peers.pending.length >= this.size)
|
if (this.peers.block.length + this.peers.pending.length >= this.size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var peer = new bcoin.peer(this, this.createSocket, {
|
peer = new bcoin.peer(this, this.createSocket, {
|
||||||
backoff: backoff,
|
backoff: backoff,
|
||||||
startHeight: this.options.startHeight,
|
startHeight: this.options.startHeight,
|
||||||
relay: this.options.relay
|
relay: this.options.relay
|
||||||
@ -302,7 +333,6 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
|||||||
peer._retry = 0;
|
peer._retry = 0;
|
||||||
|
|
||||||
// Create new peer on failure
|
// Create new peer on failure
|
||||||
var self = this;
|
|
||||||
peer.on('error', function(err) {
|
peer.on('error', function(err) {
|
||||||
self.emit('error', err, peer);
|
self.emit('error', err, peer);
|
||||||
});
|
});
|
||||||
@ -315,10 +345,12 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
peer.once('ack', function() {
|
peer.once('ack', function() {
|
||||||
|
var i;
|
||||||
|
|
||||||
if (self.destroyed)
|
if (self.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var i = self.peers.pending.indexOf(peer);
|
i = self.peers.pending.indexOf(peer);
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
self.peers.pending.splice(i, 1);
|
self.peers.pending.splice(i, 1);
|
||||||
self.peers.block.push(peer);
|
self.peers.block.push(peer);
|
||||||
@ -349,6 +381,9 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
peer.on('block', function(block) {
|
peer.on('block', function(block) {
|
||||||
|
var hashes = self.chain.index.hashes;
|
||||||
|
var hash, len, orphan, err;
|
||||||
|
|
||||||
if (self.syncPeer !== peer)
|
if (self.syncPeer !== peer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -356,11 +391,11 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
|||||||
|
|
||||||
self._response(block);
|
self._response(block);
|
||||||
|
|
||||||
var hash = block.hash('hex');
|
hash = block.hash('hex');
|
||||||
var len = self.chain.index.hashes.length;
|
len = hashes.length;
|
||||||
var orphan = self.chain.hasOrphan(block);
|
orphan = self.chain.hasOrphan(block);
|
||||||
|
|
||||||
var err = self.chain.add(block);
|
err = self.chain.add(block);
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('chain-error', err, peer);
|
self.emit('chain-error', err, peer);
|
||||||
|
|
||||||
@ -373,12 +408,10 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.chain.index.hashes.length === len)
|
if (hashes.length === len)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var top = self.chain.index.hashes[self.chain.index.hashes.length - 1];
|
self.needSync = hashes[hashes.length - 1] !== self.bestBlock;
|
||||||
|
|
||||||
self.needSync = top !== self.bestBlock;
|
|
||||||
|
|
||||||
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
||||||
self.emit('block', block, peer);
|
self.emit('block', block, peer);
|
||||||
@ -386,9 +419,9 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.chain.on('fork', function(height, hash, checkpoint) {
|
this.chain.on('fork', function(height, hash, checkpoint) {
|
||||||
if (!self.syncPeer)
|
|
||||||
return;
|
|
||||||
var peer = self.syncPeer;
|
var peer = self.syncPeer;
|
||||||
|
if (!peer)
|
||||||
|
return;
|
||||||
delete self.syncPeer;
|
delete self.syncPeer;
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
self.startSync();
|
self.startSync();
|
||||||
@ -450,6 +483,7 @@ Pool.prototype.bestPeer = function bestPeer() {
|
|||||||
this.peers.block.forEach(function(peer) {
|
this.peers.block.forEach(function(peer) {
|
||||||
if (!peer.version || !peer.socket)
|
if (!peer.version || !peer.socket)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!best || peer.version.height > best.version.height)
|
if (!best || peer.version.height > best.version.height)
|
||||||
best = peer;
|
best = peer;
|
||||||
});
|
});
|
||||||
@ -495,13 +529,15 @@ Pool.prototype._removePeer = function _removePeer(peer) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.watch = function watch(id) {
|
Pool.prototype.watch = function watch(id) {
|
||||||
|
var hid, i;
|
||||||
|
|
||||||
if (id instanceof bcoin.wallet) {
|
if (id instanceof bcoin.wallet) {
|
||||||
this.watchWallet(id);
|
this.watchWallet(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
var hid = utils.toHex(id);
|
hid = utils.toHex(id);
|
||||||
if (this.watchMap[hid])
|
if (this.watchMap[hid])
|
||||||
this.watchMap[hid]++;
|
this.watchMap[hid]++;
|
||||||
else
|
else
|
||||||
@ -515,11 +551,14 @@ Pool.prototype.watch = function watch(id) {
|
|||||||
|
|
||||||
if (this.peers.load)
|
if (this.peers.load)
|
||||||
this.peers.load.updateWatch();
|
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();
|
this.peers.block[i].updateWatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.unwatch = function unwatch(id) {
|
Pool.prototype.unwatch = function unwatch(id) {
|
||||||
|
var i;
|
||||||
|
|
||||||
if (!this.bloom.test(id, 'hex'))
|
if (!this.bloom.test(id, 'hex'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -538,17 +577,21 @@ Pool.prototype.unwatch = function unwatch(id) {
|
|||||||
// Resend it to peers
|
// Resend it to peers
|
||||||
if (this.peers.load)
|
if (this.peers.load)
|
||||||
this.peers.load.updateWatch();
|
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();
|
this.peers.block[i].updateWatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.addWallet = function addWallet(w, defaultTs) {
|
Pool.prototype.addWallet = function addWallet(w, defaultTs) {
|
||||||
|
var self = this;
|
||||||
|
var e;
|
||||||
|
|
||||||
if (this.wallets.indexOf(w) !== -1)
|
if (this.wallets.indexOf(w) !== -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this.watchWallet(w);
|
this.watchWallet(w);
|
||||||
|
|
||||||
var self = this;
|
e = new EventEmitter();
|
||||||
var e = new EventEmitter();
|
|
||||||
if (w.loaded)
|
if (w.loaded)
|
||||||
search(w.lastTs);
|
search(w.lastTs);
|
||||||
else
|
else
|
||||||
@ -606,16 +649,19 @@ Pool.prototype.unwatchWallet = function unwatchWallet(w) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.search = function search(id, range, e) {
|
Pool.prototype.search = function search(id, range, e) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
e = e || new EventEmitter();
|
e = e || new EventEmitter();
|
||||||
|
|
||||||
// Optional id argument
|
// Optional id argument
|
||||||
if (id !== null &&
|
if (id !== null
|
||||||
typeof id === 'object' &&
|
&& typeof id === 'object'
|
||||||
!Array.isArray(id) ||
|
&& !Array.isArray(id)
|
||||||
typeof id === 'number') {
|
|| typeof id === 'number') {
|
||||||
range = id;
|
range = id;
|
||||||
id = null;
|
id = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof id === 'string')
|
if (typeof id === 'string')
|
||||||
id = utils.toArray(id, 'hex');
|
id = utils.toArray(id, 'hex');
|
||||||
|
|
||||||
@ -633,7 +679,6 @@ Pool.prototype.search = function search(id, range, e) {
|
|||||||
if (!range.start)
|
if (!range.start)
|
||||||
range.start = +new Date() / 1000 - 432000;
|
range.start = +new Date() / 1000 - 432000;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.chain.hashesInRange(range.start, range.end, function(hashes, count) {
|
this.chain.hashesInRange(range.start, range.end, function(hashes, count) {
|
||||||
var waiting = 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) {
|
Pool.prototype._request = function _request(type, hash, options, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
// Optional `force`
|
// Optional `force`
|
||||||
if (typeof options === 'function') {
|
if (typeof options === 'function') {
|
||||||
cb = options;
|
cb = options;
|
||||||
@ -703,8 +750,6 @@ Pool.prototype._request = function _request(type, hash, options, cb) {
|
|||||||
if (this.request.map[hash])
|
if (this.request.map[hash])
|
||||||
return this.request.map[hash].addCallback(cb);
|
return this.request.map[hash].addCallback(cb);
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Block should be not in chain, or be requested
|
// Block should be not in chain, or be requested
|
||||||
if (!options.force && type === 'block')
|
if (!options.force && type === 'block')
|
||||||
return this.chain.has(hash, true, next);
|
return this.chain.has(hash, true, next);
|
||||||
@ -732,8 +777,11 @@ Pool.prototype._response = function _response(entity) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._scheduleRequests = function _scheduleRequests() {
|
Pool.prototype._scheduleRequests = function _scheduleRequests() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.request.active > this.parallel / 2)
|
if (this.request.active > this.parallel / 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -749,7 +797,6 @@ Pool.prototype._scheduleRequests = function _scheduleRequests() {
|
|||||||
if (this.load.timer !== null)
|
if (this.load.timer !== null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.load.timer = setTimeout(function() {
|
this.load.timer = setTimeout(function() {
|
||||||
self.load.timer = null;
|
self.load.timer = null;
|
||||||
self._doRequests();
|
self._doRequests();
|
||||||
@ -757,6 +804,9 @@ Pool.prototype._scheduleRequests = function _scheduleRequests() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._doRequests = function _doRequests() {
|
Pool.prototype._doRequests = function _doRequests() {
|
||||||
|
var queue, above, items, below;
|
||||||
|
var red, count, split, i, off, req, j;
|
||||||
|
|
||||||
if (this.request.active >= this.parallel)
|
if (this.request.active >= this.parallel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -764,11 +814,11 @@ Pool.prototype._doRequests = function _doRequests() {
|
|||||||
if (this.peers.block.length === 0)
|
if (this.peers.block.length === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var queue = this.request.queue;
|
queue = this.request.queue;
|
||||||
var above = queue.length >= this.load.lwm;
|
above = queue.length >= this.load.lwm;
|
||||||
var items = queue.slice(0, this.parallel - this.request.active);
|
items = queue.slice(0, this.parallel - this.request.active);
|
||||||
this.request.queue = queue.slice(items.length);
|
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
|
// Watermark boundary crossed, load more blocks
|
||||||
if (above && below && this.load.hiReached)
|
if (above && below && this.load.hiReached)
|
||||||
@ -779,19 +829,20 @@ Pool.prototype._doRequests = function _doRequests() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Split list between peers
|
// Split list between peers
|
||||||
var red = this.redundancy;
|
red = this.redundancy;
|
||||||
var count = this.peers.block.length;
|
count = this.peers.block.length;
|
||||||
var split = Math.ceil(items.length * red / count);
|
split = Math.ceil(items.length * red / count);
|
||||||
for (var i = 0, off = 0; i < count; i += red, off += split) {
|
for (i = 0, off = 0; i < count; i += red, off += split) {
|
||||||
var req = items.slice(off, off + split).map(mapReq, this);
|
req = items.slice(off, off + split).map(mapReq, this);
|
||||||
|
for (j = 0; j < red && i + j < count; j++)
|
||||||
for (var j = 0; j < red && i + j < count; j++) {
|
|
||||||
this.peers.block[i + j].getData(req);
|
this.peers.block[i + j].getData(req);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.getTX = function getTX(hash, range, cb) {
|
Pool.prototype.getTX = function getTX(hash, range, cb) {
|
||||||
|
var self = this;
|
||||||
|
var cbs, tx, finished, req, delta;
|
||||||
|
|
||||||
hash = utils.toHex(hash);
|
hash = utils.toHex(hash);
|
||||||
|
|
||||||
if (typeof range === 'function') {
|
if (typeof range === 'function') {
|
||||||
@ -802,19 +853,20 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
|
|||||||
// Do not perform duplicate searches
|
// Do not perform duplicate searches
|
||||||
if (this.validate.map[hash])
|
if (this.validate.map[hash])
|
||||||
return this.validate.map[hash].push(cb);
|
return this.validate.map[hash].push(cb);
|
||||||
var cbs = [ cb ];
|
|
||||||
|
cbs = [ cb ];
|
||||||
this.validate.map[hash] = cbs;
|
this.validate.map[hash] = cbs;
|
||||||
|
|
||||||
// Add request without queueing it to get notification at the time of load
|
// Add request without queueing it to get notification at the time of load
|
||||||
var tx = null;
|
tx = null;
|
||||||
var finished = false;
|
finished = false;
|
||||||
var req = this._request('tx', hash, { noQueue: true }, function(t) {
|
req = this._request('tx', hash, { noQueue: true }, function(t) {
|
||||||
finished = true;
|
finished = true;
|
||||||
tx = t;
|
tx = t;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Do incremental search until the TX is found
|
// Do incremental search until the TX is found
|
||||||
var delta = this.validate.delta;
|
delta = this.validate.delta;
|
||||||
|
|
||||||
// Start from the existing range if given
|
// Start from the existing range if given
|
||||||
if (range)
|
if (range)
|
||||||
@ -822,9 +874,6 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
|
|||||||
else
|
else
|
||||||
range = { start: (+new Date() / 1000) - delta, end: 0 };
|
range = { start: (+new Date() / 1000) - delta, end: 0 };
|
||||||
|
|
||||||
var self = this;
|
|
||||||
doSearch();
|
|
||||||
|
|
||||||
function doSearch() {
|
function doSearch() {
|
||||||
var e = self.search(hash, range);
|
var e = self.search(hash, range);
|
||||||
e.on('end', function(empty) {
|
e.on('end', function(empty) {
|
||||||
@ -849,12 +898,14 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
|
|||||||
doSearch();
|
doSearch();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doSearch();
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.sendTX = function sendTX(tx) {
|
Pool.prototype.sendTX = function sendTX(tx) {
|
||||||
|
var self = this;
|
||||||
var e = new EventEmitter();
|
var e = new EventEmitter();
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var entry = {
|
var entry = {
|
||||||
tx: tx,
|
tx: tx,
|
||||||
e: e,
|
e: e,
|
||||||
@ -864,6 +915,7 @@ Pool.prototype.sendTX = function sendTX(tx) {
|
|||||||
self.tx.list.splice(i, 1);
|
self.tx.list.splice(i, 1);
|
||||||
}, this.tx.timeout)
|
}, this.tx.timeout)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tx.list.push(entry);
|
this.tx.list.push(entry);
|
||||||
|
|
||||||
this.peers.block.forEach(function(peer) {
|
this.peers.block.forEach(function(peer) {
|
||||||
@ -880,6 +932,7 @@ Pool.prototype.sendTX = function sendTX(tx) {
|
|||||||
Pool.prototype.destroy = function destroy() {
|
Pool.prototype.destroy = function destroy() {
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.destroyed = true;
|
this.destroyed = true;
|
||||||
|
|
||||||
if (this.peers.load)
|
if (this.peers.load)
|
||||||
@ -888,18 +941,23 @@ Pool.prototype.destroy = function destroy() {
|
|||||||
this.request.queue.slice().forEach(function(item) {
|
this.request.queue.slice().forEach(function(item) {
|
||||||
item.finish(null);
|
item.finish(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tx.list.forEach(function(tx) {
|
this.tx.list.forEach(function(tx) {
|
||||||
clearTimeout(tx.timer);
|
clearTimeout(tx.timer);
|
||||||
tx.timer = null;
|
tx.timer = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.peers.pending.slice().forEach(function(peer) {
|
this.peers.pending.slice().forEach(function(peer) {
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.peers.block.slice().forEach(function(peer) {
|
this.peers.block.slice().forEach(function(peer) {
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.load.timer)
|
if (this.load.timer)
|
||||||
clearTimeout(this.load.timer);
|
clearTimeout(this.load.timer);
|
||||||
|
|
||||||
this.load.timer = null;
|
this.load.timer = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -920,6 +978,8 @@ Pool.prototype.fromJSON = function fromJSON(json) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function LoadRequest(pool, type, hash, cb) {
|
function LoadRequest(pool, type, hash, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
@ -930,7 +990,6 @@ function LoadRequest(pool, type, hash, cb) {
|
|||||||
this.active = false;
|
this.active = false;
|
||||||
this.noQueue = false;
|
this.noQueue = false;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.onclose = function onclose() {
|
this.onclose = function onclose() {
|
||||||
if (self.pool.destroyed)
|
if (self.pool.destroyed)
|
||||||
self.clear();
|
self.clear();
|
||||||
@ -941,8 +1000,9 @@ function LoadRequest(pool, type, hash, cb) {
|
|||||||
|
|
||||||
LoadRequest.prototype.start = function start(peer) {
|
LoadRequest.prototype.start = function start(peer) {
|
||||||
var self = this;
|
var self = this;
|
||||||
assert(!this.active);
|
var reqType;
|
||||||
|
|
||||||
|
assert(!this.active);
|
||||||
this.active = true;
|
this.active = true;
|
||||||
this.pool.request.active++;
|
this.pool.request.active++;
|
||||||
|
|
||||||
@ -956,7 +1016,6 @@ LoadRequest.prototype.start = function start(peer) {
|
|||||||
this.peer = peer;
|
this.peer = peer;
|
||||||
this.peer.once('close', this.onclose);
|
this.peer.once('close', this.onclose);
|
||||||
|
|
||||||
var reqType;
|
|
||||||
if (this.type === 'block')
|
if (this.type === 'block')
|
||||||
reqType = 'filtered';
|
reqType = 'filtered';
|
||||||
else if (this.type === 'tx')
|
else if (this.type === 'tx')
|
||||||
@ -991,9 +1050,11 @@ LoadRequest.prototype.clear = function clear() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
LoadRequest.prototype.retry = function retry() {
|
LoadRequest.prototype.retry = function retry() {
|
||||||
|
var peer = this.peer;
|
||||||
|
|
||||||
// Put block into the queue, ensure that the queue is always sorted by ts
|
// Put block into the queue, ensure that the queue is always sorted by ts
|
||||||
utils.binaryInsert(this.pool.request.queue, this, LoadRequest.compare);
|
utils.binaryInsert(this.pool.request.queue, this, LoadRequest.compare);
|
||||||
var peer = this.peer;
|
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
||||||
// Kill peer, if it misbehaves
|
// Kill peer, if it misbehaves
|
||||||
@ -1005,12 +1066,14 @@ LoadRequest.prototype.retry = function retry() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
LoadRequest.prototype.finish = function finish(entity) {
|
LoadRequest.prototype.finish = function finish(entity) {
|
||||||
|
var index;
|
||||||
|
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
this.clear();
|
this.clear();
|
||||||
} else {
|
} else {
|
||||||
// It could be that request was never sent to the node, remove it from
|
// It could be that request was never sent to the node, remove it from
|
||||||
// queue and forget about it
|
// 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);
|
assert(index !== -1 || this.noQueue);
|
||||||
if (!this.noQueue)
|
if (!this.noQueue)
|
||||||
this.pool.request.queue.splice(index, 1);
|
this.pool.request.queue.splice(index, 1);
|
||||||
@ -1031,3 +1094,5 @@ LoadRequest.prototype.addCallback = function addCallback(cb) {
|
|||||||
if (cb)
|
if (cb)
|
||||||
this.cbs.push(cb);
|
this.cbs.push(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Pool;
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
var bcoin = require('../../bcoin');
|
var bcoin = require('../../bcoin');
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
exports.minVersion = 70001;
|
exports.minVersion = 70001;
|
||||||
exports.version = 70002;
|
exports.version = 70002;
|
||||||
|
|
||||||
@ -119,10 +121,11 @@ exports.opcodes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.opcodes['-1'] = 0x50 + -1;
|
exports.opcodes['-1'] = 0x50 + -1;
|
||||||
for (var i = 1; i <= 16; i++)
|
|
||||||
|
for (i = 1; i <= 16; i++)
|
||||||
exports.opcodes[i] = 0x50 + 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.opcodes['nop' + (i + 3)] = 0xb2 + i;
|
||||||
|
|
||||||
exports.opcodesByVal = new Array(256);
|
exports.opcodesByVal = new Array(256);
|
||||||
|
|||||||
@ -19,20 +19,20 @@ function Framer(options) {
|
|||||||
this.agent = utils.toArray(options.agent || '/bcoin:' + version + '/');
|
this.agent = utils.toArray(options.agent || '/bcoin:' + version + '/');
|
||||||
this.agent = this.agent.slice(0, 0xfc);
|
this.agent = this.agent.slice(0, 0xfc);
|
||||||
}
|
}
|
||||||
module.exports = Framer;
|
|
||||||
|
|
||||||
Framer.prototype.header = function header(cmd, payload) {
|
Framer.prototype.header = function header(cmd, payload) {
|
||||||
|
var h = new Array(24);
|
||||||
|
var len, i;
|
||||||
|
|
||||||
assert(cmd.length < 12);
|
assert(cmd.length < 12);
|
||||||
assert(payload.length <= 0xffffffff);
|
assert(payload.length <= 0xffffffff);
|
||||||
|
|
||||||
var h = new Array(24);
|
|
||||||
|
|
||||||
// Magic value
|
// Magic value
|
||||||
writeU32(h, network.magic, 0);
|
writeU32(h, network.magic, 0);
|
||||||
|
|
||||||
// Command
|
// Command
|
||||||
var len = writeAscii(h, cmd, 4);
|
len = writeAscii(h, cmd, 4);
|
||||||
for (var i = 4 + len; i < 4 + 12; i++)
|
for (i = 4 + len; i < 4 + 12; i++)
|
||||||
h[i] = 0;
|
h[i] = 0;
|
||||||
|
|
||||||
// Payload length
|
// Payload length
|
||||||
@ -64,6 +64,7 @@ Framer.prototype._addr = function addr(buf, off) {
|
|||||||
Framer.prototype.version = function version(packet) {
|
Framer.prototype.version = function version(packet) {
|
||||||
var p = new Array(86 + this.agent.length);
|
var p = new Array(86 + this.agent.length);
|
||||||
var off = 0;
|
var off = 0;
|
||||||
|
var ts, i;
|
||||||
|
|
||||||
if (!packet)
|
if (!packet)
|
||||||
packet = {};
|
packet = {};
|
||||||
@ -76,7 +77,7 @@ Framer.prototype.version = function version(packet) {
|
|||||||
off += writeU32(p, 0, off);
|
off += writeU32(p, 0, off);
|
||||||
|
|
||||||
// Timestamp
|
// Timestamp
|
||||||
var ts = ((+new Date()) / 1000) | 0;
|
ts = ((+new Date()) / 1000) | 0;
|
||||||
off += writeU32(p, ts, off);
|
off += writeU32(p, ts, off);
|
||||||
off += writeU32(p, 0, off);
|
off += writeU32(p, 0, off);
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ Framer.prototype.version = function version(packet) {
|
|||||||
p[off++] = 0;
|
p[off++] = 0;
|
||||||
} else {
|
} else {
|
||||||
off += varint(p, this.agent.length, off);
|
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];
|
p[off++] = this.agent[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,14 +140,16 @@ function varint(arr, value, off) {
|
|||||||
Framer.prototype._inv = function _inv(command, items) {
|
Framer.prototype._inv = function _inv(command, items) {
|
||||||
var res = [];
|
var res = [];
|
||||||
var off = varint(res, items.length, 0);
|
var off = varint(res, items.length, 0);
|
||||||
|
var i, hash;
|
||||||
|
|
||||||
assert(items.length <= 50000);
|
assert(items.length <= 50000);
|
||||||
|
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (i = 0; i < items.length; i++) {
|
||||||
// Type
|
// Type
|
||||||
off += writeU32(res, constants.inv[items[i].type], off);
|
off += writeU32(res, constants.inv[items[i].type], off);
|
||||||
|
|
||||||
// Hash
|
// Hash
|
||||||
var hash = items[i].hash;
|
hash = items[i].hash;
|
||||||
if (typeof hash === 'string')
|
if (typeof hash === 'string')
|
||||||
hash = utils.toArray(hash, 'hex');
|
hash = utils.toArray(hash, 'hex');
|
||||||
assert.equal(hash.length, 32);
|
assert.equal(hash.length, 32);
|
||||||
@ -181,10 +184,10 @@ Framer.prototype.pong = function pong(nonce) {
|
|||||||
Framer.prototype.filterLoad = function filterLoad(bloom, update) {
|
Framer.prototype.filterLoad = function filterLoad(bloom, update) {
|
||||||
var filter = bloom.toArray();
|
var filter = bloom.toArray();
|
||||||
var before = [];
|
var before = [];
|
||||||
varint(before, filter.length, 0);
|
|
||||||
|
|
||||||
var after = new Array(9);
|
var after = new Array(9);
|
||||||
|
|
||||||
|
varint(before, filter.length, 0);
|
||||||
|
|
||||||
// Number of hash functions
|
// Number of hash functions
|
||||||
writeU32(after, bloom.n, 0);
|
writeU32(after, bloom.n, 0);
|
||||||
|
|
||||||
@ -194,8 +197,7 @@ Framer.prototype.filterLoad = function filterLoad(bloom, update) {
|
|||||||
// nFlags
|
// nFlags
|
||||||
after[8] = constants.filterFlags[update];
|
after[8] = constants.filterFlags[update];
|
||||||
|
|
||||||
var r = this.packet('filterload', before.concat(filter, after));
|
return this.packet('filterload', before.concat(filter, after));
|
||||||
return r;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Framer.prototype.filterClear = function filterClear() {
|
Framer.prototype.filterClear = function filterClear() {
|
||||||
@ -212,28 +214,36 @@ Framer.prototype.getBlocks = function getBlocks(hashes, stop) {
|
|||||||
|
|
||||||
Framer.prototype._getBlocks = function _getBlocks(cmd, hashes, stop) {
|
Framer.prototype._getBlocks = function _getBlocks(cmd, hashes, stop) {
|
||||||
var p = [];
|
var p = [];
|
||||||
|
var off, i, hash, len;
|
||||||
|
|
||||||
writeU32(p, constants.version, 0);
|
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);
|
p.length = off + 32 * (hashes.length + 1);
|
||||||
|
|
||||||
for (var i = 0; i < hashes.length; i++) {
|
for (i = 0; i < hashes.length; i++) {
|
||||||
var hash = hashes[i];
|
hash = hashes[i];
|
||||||
|
|
||||||
if (typeof hash === 'string')
|
if (typeof hash === 'string')
|
||||||
hash = utils.toArray(hash, 'hex');
|
hash = utils.toArray(hash, 'hex');
|
||||||
var len = utils.copy(hash, p, off);
|
|
||||||
|
len = utils.copy(hash, p, off);
|
||||||
|
|
||||||
for (; len < 32; len++)
|
for (; len < 32; len++)
|
||||||
p[off + len] = 0;
|
p[off + len] = 0;
|
||||||
|
|
||||||
off += len;
|
off += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stop) {
|
if (stop) {
|
||||||
stop = utils.toArray(stop, 'hex');
|
stop = utils.toArray(stop, 'hex');
|
||||||
var len = utils.copy(stop, p, off);
|
len = utils.copy(stop, p, off);
|
||||||
} else {
|
} else {
|
||||||
var len = 0;
|
len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; len < 32; len++)
|
for (; len < 32; len++)
|
||||||
p[off + len] = 0;
|
p[off + len] = 0;
|
||||||
|
|
||||||
assert.equal(off + len, p.length);
|
assert.equal(off + len, p.length);
|
||||||
|
|
||||||
return this.packet(cmd, p);
|
return this.packet(cmd, p);
|
||||||
@ -241,16 +251,18 @@ Framer.prototype._getBlocks = function _getBlocks(cmd, hashes, stop) {
|
|||||||
|
|
||||||
Framer.tx = function tx(tx) {
|
Framer.tx = function tx(tx) {
|
||||||
var p = [];
|
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);
|
off += varint(p, tx.inputs.length, off);
|
||||||
|
|
||||||
for (var i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
var input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
|
|
||||||
off += utils.copy(utils.toArray(input.out.hash, 'hex'), p, off, true);
|
off += utils.copy(utils.toArray(input.out.hash, 'hex'), p, off, true);
|
||||||
off += writeU32(p, input.out.index, off);
|
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 += varint(p, s.length, off);
|
||||||
off += utils.copy(s, p, off, true);
|
off += utils.copy(s, p, off, true);
|
||||||
|
|
||||||
@ -258,17 +270,19 @@ Framer.tx = function tx(tx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
off += varint(p, tx.outputs.length, off);
|
off += varint(p, tx.outputs.length, off);
|
||||||
for (var i = 0; i < tx.outputs.length; i++) {
|
for (i = 0; i < tx.outputs.length; i++) {
|
||||||
var output = tx.outputs[i];
|
output = tx.outputs[i];
|
||||||
|
|
||||||
// Put LE value
|
// Put LE value
|
||||||
var value = output.value.toArray().slice().reverse();
|
value = output.value.toArray().slice().reverse();
|
||||||
assert(value.length <= 8);
|
assert(value.length <= 8);
|
||||||
|
|
||||||
off += utils.copy(value, p, off, true);
|
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;
|
p[off] = 0;
|
||||||
|
|
||||||
var s = bcoin.script.encode(output.script);
|
s = bcoin.script.encode(output.script);
|
||||||
off += varint(p, s.length, off);
|
off += varint(p, s.length, off);
|
||||||
off += utils.copy(s, p, off, true);
|
off += utils.copy(s, p, off, true);
|
||||||
}
|
}
|
||||||
@ -356,15 +370,15 @@ Framer.prototype.merkleBlock = function merkleBlock(block) {
|
|||||||
|
|
||||||
Framer.prototype.addr = function addr(peers) {
|
Framer.prototype.addr = function addr(peers) {
|
||||||
var p = [];
|
var p = [];
|
||||||
var i = 0;
|
|
||||||
var off = 0;
|
var off = 0;
|
||||||
var peer;
|
var peer;
|
||||||
var start = (Date.now() / 1000 | 0) - process.uptime();
|
var start = (Date.now() / 1000 | 0) - process.uptime();
|
||||||
|
var i;
|
||||||
|
|
||||||
// count
|
// count
|
||||||
off += varint(p, peers.length, off);
|
off += varint(p, peers.length, off);
|
||||||
|
|
||||||
for (; i < peers.length; i++) {
|
for (i = 0; i < peers.length; i++) {
|
||||||
peer = peers[i];
|
peer = peers[i];
|
||||||
|
|
||||||
// timestamp
|
// timestamp
|
||||||
@ -387,3 +401,5 @@ Framer.prototype.addr = function addr(peers) {
|
|||||||
|
|
||||||
return this.packet('addr', p);
|
return this.packet('addr', p);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Framer;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ var utils = bcoin.utils;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var network = exports;
|
var network = exports;
|
||||||
|
var main, testnet;
|
||||||
|
|
||||||
network.set = function(type) {
|
network.set = function(type) {
|
||||||
var net = network[type];
|
var net = network[type];
|
||||||
@ -16,7 +17,7 @@ network.set = function(type) {
|
|||||||
* Main
|
* Main
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var main = network.main = {};
|
main = network.main = {};
|
||||||
|
|
||||||
main.prefixes = {
|
main.prefixes = {
|
||||||
pubkey: 0,
|
pubkey: 0,
|
||||||
@ -103,7 +104,7 @@ main.preload = require('./preload');
|
|||||||
* https://en.bitcoin.it/wiki/Testnet
|
* https://en.bitcoin.it/wiki/Testnet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var testnet = network.testnet = {};
|
testnet = network.testnet = {};
|
||||||
|
|
||||||
testnet.type = 'testnet';
|
testnet.type = 'testnet';
|
||||||
|
|
||||||
|
|||||||
@ -22,20 +22,28 @@ function Parser() {
|
|||||||
this.waiting = 24;
|
this.waiting = 24;
|
||||||
this.packet = null;
|
this.packet = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(Parser, EventEmitter);
|
inherits(Parser, EventEmitter);
|
||||||
module.exports = Parser;
|
|
||||||
|
|
||||||
Parser.prototype._error = function _error(str) {
|
Parser.prototype._error = function _error(str) {
|
||||||
this.emit('error', new Error(str));
|
this.emit('error', new Error(str));
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.feed = function feed(data) {
|
Parser.prototype.feed = function feed(data) {
|
||||||
|
var chunk, i, off, len;
|
||||||
|
|
||||||
this.pendingTotal += data.length;
|
this.pendingTotal += data.length;
|
||||||
this.pending.push(data);
|
this.pending.push(data);
|
||||||
|
|
||||||
while (this.pendingTotal >= this.waiting) {
|
while (this.pendingTotal >= this.waiting) {
|
||||||
// Concat chunks
|
// Concat chunks
|
||||||
var chunk = new Array(this.waiting);
|
chunk = new Array(this.waiting);
|
||||||
for (var i = 0, off = 0, len = 0; off < chunk.length; i++) {
|
|
||||||
|
i = 0;
|
||||||
|
off = 0;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
for (; off < chunk.length; i++) {
|
||||||
len = utils.copy(this.pending[0], chunk, off);
|
len = utils.copy(this.pending[0], chunk, off);
|
||||||
if (len === this.pending[0].length)
|
if (len === this.pending[0].length)
|
||||||
this.pending.shift();
|
this.pending.shift();
|
||||||
@ -45,6 +53,7 @@ Parser.prototype.feed = function feed(data) {
|
|||||||
this.pending[0] = this.pending[0].slice(len);
|
this.pending[0] = this.pending[0].slice(len);
|
||||||
off += len;
|
off += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.equal(off, chunk.length);
|
assert.equal(off, chunk.length);
|
||||||
|
|
||||||
// Slice buffers
|
// Slice buffers
|
||||||
@ -56,32 +65,39 @@ Parser.prototype.feed = function feed(data) {
|
|||||||
Parser.prototype.parse = function parse(chunk) {
|
Parser.prototype.parse = function parse(chunk) {
|
||||||
if (this.packet === null) {
|
if (this.packet === null) {
|
||||||
this.packet = this.parseHeader(chunk) || {};
|
this.packet = this.parseHeader(chunk) || {};
|
||||||
} else {
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
Parser.prototype.parseHeader = function parseHeader(h) {
|
||||||
var magic = readU32(h, 0);
|
var i, magic, cmd;
|
||||||
|
|
||||||
|
magic = readU32(h, 0);
|
||||||
|
|
||||||
if (magic !== network.magic) {
|
if (magic !== network.magic) {
|
||||||
return this._error('Invalid magic value: ' + magic.toString(16));
|
return this._error('Invalid magic value: ' + magic.toString(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count length of the cmd
|
// 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)
|
if (i === 12)
|
||||||
return this._error('Not NULL-terminated cmd');
|
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);
|
this.waiting = readU32(h, 16);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -113,30 +129,32 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseVersion = function parseVersion(p) {
|
Parser.prototype.parseVersion = function parseVersion(p) {
|
||||||
|
var v, services, ts, nonce, result, off, agent, height, relay;
|
||||||
|
|
||||||
if (p.length < 85)
|
if (p.length < 85)
|
||||||
return this._error('version packet is too small');
|
return this._error('version packet is too small');
|
||||||
|
|
||||||
var v = readU32(p, 0);
|
v = readU32(p, 0);
|
||||||
var services = readU64(p, 4);
|
services = readU64(p, 4);
|
||||||
|
|
||||||
// Timestamp
|
// Timestamp
|
||||||
var ts = readU64(p, 12);
|
ts = readU64(p, 12);
|
||||||
|
|
||||||
// Nonce, very dramatic
|
// 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
|
// User agent length
|
||||||
var result = readIntv(p, 80);
|
result = readIntv(p, 80);
|
||||||
var off = result.off;
|
off = result.off;
|
||||||
var agent = p.slice(off, off + result.r);
|
agent = p.slice(off, off + result.r);
|
||||||
off += result.r;
|
off += result.r;
|
||||||
|
|
||||||
// Start height
|
// Start height
|
||||||
var height = readU32(p, off);
|
height = readU32(p, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
|
|
||||||
// Relay
|
// Relay
|
||||||
var relay = p.length > off ? p[off] === 1 : true;
|
relay = p.length > off ? p[off] === 1 : true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
v: v,
|
v: v,
|
||||||
@ -150,10 +168,11 @@ Parser.prototype.parseVersion = function parseVersion(p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function readIntv(p, off) {
|
function readIntv(p, off) {
|
||||||
|
var r, bytes;
|
||||||
|
|
||||||
if (!off)
|
if (!off)
|
||||||
off = 0;
|
off = 0;
|
||||||
|
|
||||||
var r, bytes;
|
|
||||||
if (p[off] < 0xfd) {
|
if (p[off] < 0xfd) {
|
||||||
r = p[off];
|
r = p[off];
|
||||||
bytes = 1;
|
bytes = 1;
|
||||||
@ -172,45 +191,54 @@ function readIntv(p, off) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Parser.prototype.parseInvList = function parseInvList(p) {
|
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);
|
p = p.slice(count.off);
|
||||||
count = count.r;
|
count = count.r;
|
||||||
|
|
||||||
if (p.length < count * 36)
|
if (p.length < count * 36)
|
||||||
return this._error('Invalid getdata size');
|
return this._error('Invalid getdata size');
|
||||||
|
|
||||||
var items = [];
|
for (i = 0; i < count; i++, off += 36) {
|
||||||
for (var i = 0, off = 0; i < count; i++, off += 36) {
|
|
||||||
items.push({
|
items.push({
|
||||||
type: constants.invByVal[readU32(p, off)],
|
type: constants.invByVal[readU32(p, off)],
|
||||||
hash: p.slice(off + 4, off + 36)
|
hash: p.slice(off + 4, off + 36)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) {
|
Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) {
|
||||||
|
var i, hashCount, off, hashes, flagCount, flags;
|
||||||
|
|
||||||
if (p.length < 86)
|
if (p.length < 86)
|
||||||
return this._error('Invalid merkleblock size');
|
return this._error('Invalid merkleblock size');
|
||||||
|
|
||||||
var hashCount = readIntv(p, 84);
|
hashCount = readIntv(p, 84);
|
||||||
var off = hashCount.off;
|
off = hashCount.off;
|
||||||
hashCount = hashCount.r;
|
hashCount = hashCount.r;
|
||||||
|
|
||||||
if (off + 32 * hashCount + 1 > p.length)
|
if (off + 32 * hashCount + 1 > p.length)
|
||||||
return this._error('Invalid hash count');
|
return this._error('Invalid hash count');
|
||||||
|
|
||||||
var hashes = new Array(hashCount);
|
hashes = new Array(hashCount);
|
||||||
for (var i = 0; i < hashCount; i++)
|
|
||||||
|
for (i = 0; i < hashCount; i++)
|
||||||
hashes[i] = p.slice(off + i * 32, off + (i + 1) * 32);
|
hashes[i] = p.slice(off + i * 32, off + (i + 1) * 32);
|
||||||
|
|
||||||
off = off + 32 * hashCount;
|
off = off + 32 * hashCount;
|
||||||
var flagCount = readIntv(p, off);
|
flagCount = readIntv(p, off);
|
||||||
off = flagCount.off;
|
off = flagCount.off;
|
||||||
flagCount = flagCount.r;
|
flagCount = flagCount.r;
|
||||||
|
|
||||||
if (off + flagCount > p.length)
|
if (off + flagCount > p.length)
|
||||||
return this._error('Invalid flag count');
|
return this._error('Invalid flag count');
|
||||||
|
|
||||||
var flags = p.slice(off, off + flagCount);
|
flags = p.slice(off, off + flagCount);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: readU32(p, 0),
|
version: readU32(p, 0),
|
||||||
@ -228,19 +256,20 @@ Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseHeaders = function parseHeaders(p) {
|
Parser.prototype.parseHeaders = function parseHeaders(p) {
|
||||||
|
var headers = [];
|
||||||
|
var i, result, off, count, header, start, r;
|
||||||
|
|
||||||
if (p.length < 81)
|
if (p.length < 81)
|
||||||
return this._error('Invalid headers size');
|
return this._error('Invalid headers size');
|
||||||
|
|
||||||
var result = readIntv(p, 0);
|
result = readIntv(p, 0);
|
||||||
var off = result.off;
|
off = result.off;
|
||||||
var count = result.r;
|
count = result.r;
|
||||||
|
|
||||||
var headers = [];
|
|
||||||
|
|
||||||
if (p.length >= off + 81) {
|
if (p.length >= off + 81) {
|
||||||
for (var i = 0; i < count && off + 81 < p.length; i++) {
|
for (i = 0; i < count && off + 81 < p.length; i++) {
|
||||||
var header = {};
|
header = {};
|
||||||
var start = off;
|
start = off;
|
||||||
header.version = readU32(p, off);
|
header.version = readU32(p, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
header.prevBlock = p.slice(off, off + 32);
|
header.prevBlock = p.slice(off, off + 32);
|
||||||
@ -253,7 +282,7 @@ Parser.prototype.parseHeaders = function parseHeaders(p) {
|
|||||||
off += 4;
|
off += 4;
|
||||||
header.nonce = readU32(p, off);
|
header.nonce = readU32(p, off);
|
||||||
off += 4;
|
off += 4;
|
||||||
var r = readIntv(p, off);
|
r = readIntv(p, off);
|
||||||
header.totalTX = r.r;
|
header.totalTX = r.r;
|
||||||
off = r.off;
|
off = r.off;
|
||||||
header._raw = p.slice(start, start + 80);
|
header._raw = p.slice(start, start + 80);
|
||||||
@ -265,17 +294,19 @@ Parser.prototype.parseHeaders = function parseHeaders(p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseBlock = function parseBlock(p) {
|
Parser.prototype.parseBlock = function parseBlock(p) {
|
||||||
|
var txs = [];
|
||||||
|
var i, result, off, totalTX, tx;
|
||||||
|
|
||||||
if (p.length < 81)
|
if (p.length < 81)
|
||||||
return this._error('Invalid block size');
|
return this._error('Invalid block size');
|
||||||
|
|
||||||
var result = readIntv(p, 80);
|
result = readIntv(p, 80);
|
||||||
var off = result.off;
|
off = result.off;
|
||||||
var totalTX = result.r;
|
totalTX = result.r;
|
||||||
var txs = [];
|
|
||||||
|
|
||||||
if (p.length >= off + 10) {
|
if (p.length >= off + 10) {
|
||||||
for (var i = 0; i < totalTX; i++) {
|
for (i = 0; i < totalTX; i++) {
|
||||||
var tx = this.parseTX(p.slice(off));
|
tx = this.parseTX(p.slice(off));
|
||||||
off += tx._off;
|
off += tx._off;
|
||||||
txs.push(tx);
|
txs.push(tx);
|
||||||
}
|
}
|
||||||
@ -296,12 +327,15 @@ Parser.prototype.parseBlock = function parseBlock(p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseTXIn = function parseTXIn(p) {
|
Parser.prototype.parseTXIn = function parseTXIn(p) {
|
||||||
|
var scriptLen, off;
|
||||||
|
|
||||||
if (p.length < 41)
|
if (p.length < 41)
|
||||||
return this._error('Invalid tx_in size');
|
return this._error('Invalid tx_in size');
|
||||||
|
|
||||||
var scriptLen = readIntv(p, 36);
|
scriptLen = readIntv(p, 36);
|
||||||
var off = scriptLen.off;
|
off = scriptLen.off;
|
||||||
scriptLen = scriptLen.r;
|
scriptLen = scriptLen.r;
|
||||||
|
|
||||||
if (off + scriptLen + 4 > p.length)
|
if (off + scriptLen + 4 > p.length)
|
||||||
return this._error('Invalid tx_in script 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) {
|
Parser.prototype.parseTXOut = function parseTXOut(p) {
|
||||||
|
var scriptLen, off;
|
||||||
|
|
||||||
if (p.length < 9)
|
if (p.length < 9)
|
||||||
return this._error('Invalid tx_out size');
|
return this._error('Invalid tx_out size');
|
||||||
|
|
||||||
var scriptLen = readIntv(p, 8);
|
scriptLen = readIntv(p, 8);
|
||||||
var off = scriptLen.off;
|
off = scriptLen.off;
|
||||||
scriptLen = scriptLen.r;
|
scriptLen = scriptLen.r;
|
||||||
|
|
||||||
if (off + scriptLen > p.length)
|
if (off + scriptLen > p.length)
|
||||||
return this._error('Invalid tx_out script 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) {
|
Parser.prototype.parseTX = function parseTX(p) {
|
||||||
|
var inCount, off, txIn, tx;
|
||||||
|
var outCount, off, txOut;
|
||||||
|
var i;
|
||||||
|
|
||||||
if (p.length < 10)
|
if (p.length < 10)
|
||||||
return this._error('Invalid tx size');
|
return this._error('Invalid tx size');
|
||||||
|
|
||||||
var inCount = readIntv(p, 4);
|
inCount = readIntv(p, 4);
|
||||||
var off = inCount.off;
|
off = inCount.off;
|
||||||
inCount = inCount.r;
|
inCount = inCount.r;
|
||||||
|
|
||||||
if (inCount < 0)
|
if (inCount < 0)
|
||||||
return this._error('Invalid tx_in count (negative)');
|
return this._error('Invalid tx_in count (negative)');
|
||||||
|
|
||||||
if (off + 41 * inCount + 5 > p.length)
|
if (off + 41 * inCount + 5 > p.length)
|
||||||
return this._error('Invalid tx_in count (too big)');
|
return this._error('Invalid tx_in count (too big)');
|
||||||
|
|
||||||
var txIn = new Array(inCount);
|
txIn = new Array(inCount);
|
||||||
for (var i = 0; i < inCount; i++) {
|
for (i = 0; i < inCount; i++) {
|
||||||
var tx = this.parseTXIn(p.slice(off));
|
tx = this.parseTXIn(p.slice(off));
|
||||||
|
|
||||||
if (!tx)
|
if (!tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
txIn[i] = tx;
|
txIn[i] = tx;
|
||||||
off += tx.size;
|
off += tx.size;
|
||||||
|
|
||||||
@ -357,19 +402,21 @@ Parser.prototype.parseTX = function parseTX(p) {
|
|||||||
return this._error('Invalid tx_in offset');
|
return this._error('Invalid tx_in offset');
|
||||||
}
|
}
|
||||||
|
|
||||||
var outCount = readIntv(p, off);
|
outCount = readIntv(p, off);
|
||||||
var off = outCount.off;
|
off = outCount.off;
|
||||||
outCount = outCount.r;
|
outCount = outCount.r;
|
||||||
if (outCount < 0)
|
if (outCount < 0)
|
||||||
return this._error('Invalid tx_out count (negative)');
|
return this._error('Invalid tx_out count (negative)');
|
||||||
if (off + 9 * outCount + 4 > p.length)
|
if (off + 9 * outCount + 4 > p.length)
|
||||||
return this._error('Invalid tx_out count (too big)');
|
return this._error('Invalid tx_out count (too big)');
|
||||||
|
|
||||||
var txOut = new Array(outCount);
|
txOut = new Array(outCount);
|
||||||
for (var i = 0; i < outCount; i++) {
|
for (i = 0; i < outCount; i++) {
|
||||||
var tx = this.parseTXOut(p.slice(off));
|
tx = this.parseTXOut(p.slice(off));
|
||||||
|
|
||||||
if (!tx)
|
if (!tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
txOut[i] = tx;
|
txOut[i] = tx;
|
||||||
off += tx.size;
|
off += tx.size;
|
||||||
|
|
||||||
@ -389,32 +436,36 @@ Parser.prototype.parseTX = function parseTX(p) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype.parseReject = function parseReject(p) {
|
Parser.prototype.parseReject = function parseReject(p) {
|
||||||
|
var messageLen, off, message, ccode, reasonLen, reason, data;
|
||||||
|
|
||||||
if (p.length < 3)
|
if (p.length < 3)
|
||||||
return this._error('Invalid reject size');
|
return this._error('Invalid reject size');
|
||||||
|
|
||||||
var messageLen = readIntv(p, 0);
|
messageLen = readIntv(p, 0);
|
||||||
var off = messageLen.off;
|
off = messageLen.off;
|
||||||
messageLen = messageLen.r;
|
messageLen = messageLen.r;
|
||||||
|
|
||||||
if (off + messageLen + 2 > p.length)
|
if (off + messageLen + 2 > p.length)
|
||||||
return this._error('Invalid reject message');
|
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;
|
off += messageLen;
|
||||||
|
|
||||||
var ccode = p[off];
|
ccode = p[off];
|
||||||
off++;
|
off++;
|
||||||
|
|
||||||
var reasonLen = readIntv(p, off);
|
reasonLen = readIntv(p, off);
|
||||||
off = reasonLen.off;
|
off = reasonLen.off;
|
||||||
reasonLen = reasonLen.r;
|
reasonLen = reasonLen.r;
|
||||||
|
|
||||||
if (off + reasonLen > p.length)
|
if (off + reasonLen > p.length)
|
||||||
return this._error('Invalid reject reason');
|
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;
|
off += reasonLen;
|
||||||
|
|
||||||
var data = p.slice(off, off + 32);
|
data = p.slice(off, off + 32);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: message,
|
message: message,
|
||||||
@ -429,34 +480,35 @@ Parser.prototype.parseAddr = function parseAddr(p) {
|
|||||||
return this._error('Invalid addr size');
|
return this._error('Invalid addr size');
|
||||||
|
|
||||||
var addrs = [];
|
var addrs = [];
|
||||||
|
var i, len, off, count, ts, service, ipv6, ipv4, port;
|
||||||
|
|
||||||
// count
|
// count
|
||||||
var len = readIntv(p, 0);
|
len = readIntv(p, 0);
|
||||||
var off = len.off;
|
off = len.off;
|
||||||
var count = len.r;
|
count = len.r;
|
||||||
|
|
||||||
p = p.slice(off);
|
p = p.slice(off);
|
||||||
|
|
||||||
for (var i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
// timestamp - LE
|
// timestamp - LE
|
||||||
var ts = utils.readU32(p, 0);
|
ts = utils.readU32(p, 0);
|
||||||
|
|
||||||
// NODE_NETWORK service - LE
|
// NODE_NETWORK service - LE
|
||||||
var service = utils.readU64(p, 4);
|
service = utils.readU64(p, 4);
|
||||||
|
|
||||||
// ipv6 - BE
|
// 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);
|
ipv6 = '::' + ipv6.replace(/(.{4})/g, '$1:').slice(0, -1);
|
||||||
|
|
||||||
// ipv4 - BE
|
// ipv4 - BE
|
||||||
var ipv4 = utils.readU32BE(p, 24);
|
ipv4 = utils.readU32BE(p, 24);
|
||||||
ipv4 = ((ipv4 >> 24) & 0xff) + '.' +
|
ipv4 = ((ipv4 >> 24) & 0xff)
|
||||||
((ipv4 >> 16) & 0xff) + '.' +
|
+ '.' + ((ipv4 >> 16) & 0xff)
|
||||||
((ipv4 >> 8) & 0xff) + '.' +
|
+ '.' + ((ipv4 >> 8) & 0xff)
|
||||||
((ipv4 >> 0) & 0xff);
|
+ '.' + ((ipv4 >> 0) & 0xff);
|
||||||
|
|
||||||
// port - BE
|
// port - BE
|
||||||
var port = utils.readU16BE(p, 28);
|
port = utils.readU16BE(p, 28);
|
||||||
|
|
||||||
addrs.push({
|
addrs.push({
|
||||||
ts: ts,
|
ts: ts,
|
||||||
@ -471,3 +523,5 @@ Parser.prototype.parseAddr = function parseAddr(p) {
|
|||||||
|
|
||||||
return addrs;
|
return addrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Parser;
|
||||||
|
|||||||
@ -8,9 +8,13 @@ var script = exports;
|
|||||||
script.decode = function decode(s) {
|
script.decode = function decode(s) {
|
||||||
if (!s)
|
if (!s)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
var opcodes = [];
|
var opcodes = [];
|
||||||
for (var i = 0; i < s.length;) {
|
var i = 0;
|
||||||
var b = s[i++];
|
var b, opcode, len;
|
||||||
|
|
||||||
|
while (i < s.length) {
|
||||||
|
b = s[i++];
|
||||||
|
|
||||||
// Next `b` bytes should be pushed to stack
|
// Next `b` bytes should be pushed to stack
|
||||||
if (b >= 0x01 && b <= 0x4b) {
|
if (b >= 0x01 && b <= 0x4b) {
|
||||||
@ -35,7 +39,7 @@ script.decode = function decode(s) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var opcode = constants.opcodesByVal[b];
|
opcode = constants.opcodesByVal[b];
|
||||||
|
|
||||||
if (i >= s.length) {
|
if (i >= s.length) {
|
||||||
opcodes.push(opcode || b);
|
opcodes.push(opcode || b);
|
||||||
@ -43,7 +47,7 @@ script.decode = function decode(s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opcode === 'pushdata1') {
|
if (opcode === 'pushdata1') {
|
||||||
var len = s[i];
|
len = s[i];
|
||||||
i += 1;
|
i += 1;
|
||||||
opcodes.push(s.slice(i, i + len));
|
opcodes.push(s.slice(i, i + len));
|
||||||
i += len;
|
i += len;
|
||||||
@ -52,7 +56,7 @@ script.decode = function decode(s) {
|
|||||||
len: len
|
len: len
|
||||||
});
|
});
|
||||||
} else if (opcode === 'pushdata2') {
|
} else if (opcode === 'pushdata2') {
|
||||||
var len = utils.readU16(s, i);
|
len = utils.readU16(s, i);
|
||||||
i += 2;
|
i += 2;
|
||||||
opcodes.push(s.slice(i, i + len));
|
opcodes.push(s.slice(i, i + len));
|
||||||
i += len;
|
i += len;
|
||||||
@ -61,7 +65,7 @@ script.decode = function decode(s) {
|
|||||||
len: len
|
len: len
|
||||||
});
|
});
|
||||||
} else if (opcode === 'pushdata4') {
|
} else if (opcode === 'pushdata4') {
|
||||||
var len = utils.readU32(s, i);
|
len = utils.readU32(s, i);
|
||||||
i += 4;
|
i += 4;
|
||||||
opcodes.push(s.slice(i, i + len));
|
opcodes.push(s.slice(i, i + len));
|
||||||
i += len;
|
i += len;
|
||||||
@ -85,8 +89,11 @@ script.encode = function encode(s) {
|
|||||||
|
|
||||||
var opcodes = constants.opcodes;
|
var opcodes = constants.opcodes;
|
||||||
var res = [];
|
var res = [];
|
||||||
for (var i = 0; i < s.length; i++) {
|
var i = 0;
|
||||||
var instr = s[i];
|
var instr;
|
||||||
|
|
||||||
|
for (i = 0; i < s.length; i++) {
|
||||||
|
instr = s[i];
|
||||||
|
|
||||||
// Push value to stack
|
// Push value to stack
|
||||||
if (Array.isArray(instr)) {
|
if (Array.isArray(instr)) {
|
||||||
@ -134,27 +141,30 @@ script.encode = function encode(s) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script.subscript = function subscript(s, lastSep) {
|
script.subscript = function subscript(s, lastSep) {
|
||||||
|
var i, res;
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
if (lastSep == null) {
|
if (lastSep == null) {
|
||||||
lastSep = -1;
|
lastSep = -1;
|
||||||
for (var i = 0; i < s.length; i++) {
|
for (i = 0; i < s.length; i++) {
|
||||||
if (s[i] === 'codesep')
|
if (s[i] === 'codesep')
|
||||||
lastSep = i;
|
lastSep = i;
|
||||||
else if (s[i] === 'checksig' ||
|
else if (s[i] === 'checksig'
|
||||||
s[i] === 'checksigverify' ||
|
|| s[i] === 'checksigverify'
|
||||||
s[i] === 'checkmultisig' ||
|
|| s[i] === 'checkmultisig'
|
||||||
s[i] === 'checkmultisigverify') {
|
|| s[i] === 'checkmultisigverify') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = [];
|
res = [];
|
||||||
for (var i = lastSep + 1; i < s.length; i++)
|
for (i = lastSep + 1; i < s.length; i++) {
|
||||||
if (s[i] !== 'codesep')
|
if (s[i] !== 'codesep')
|
||||||
res.push(s[i]);
|
res.push(s[i]);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
@ -179,8 +189,10 @@ script.verify = function verify(hash, sig, pub) {
|
|||||||
|
|
||||||
script._next = function(to, s, pc) {
|
script._next = function(to, s, pc) {
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
|
var o;
|
||||||
|
|
||||||
while (s[pc]) {
|
while (s[pc]) {
|
||||||
var o = s[pc];
|
o = s[pc];
|
||||||
if (o === 'if_' || o === 'notif')
|
if (o === 'if_' || o === 'notif')
|
||||||
depth++;
|
depth++;
|
||||||
else if (o === 'else_')
|
else if (o === 'else_')
|
||||||
@ -195,6 +207,7 @@ script._next = function(to, s, pc) {
|
|||||||
depth++;
|
depth++;
|
||||||
pc++;
|
pc++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -205,11 +218,22 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var lastSep = -1;
|
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 || [];
|
stack.alt = stack.alt || [];
|
||||||
|
|
||||||
for (var pc = 0; pc < s.length; pc++) {
|
for (pc = 0; pc < s.length; pc++) {
|
||||||
var o = s[pc];
|
o = s[pc];
|
||||||
|
|
||||||
if (Array.isArray(o)) {
|
if (Array.isArray(o)) {
|
||||||
if (o.length > constants.script.maxPush)
|
if (o.length > constants.script.maxPush)
|
||||||
@ -237,16 +261,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
}
|
}
|
||||||
case 'if_':
|
case 'if_':
|
||||||
case 'notif': {
|
case 'notif': {
|
||||||
var val = false;
|
val = false;
|
||||||
if (stack.length < 1)
|
if (stack.length < 1)
|
||||||
return false;
|
return false;
|
||||||
var v = stack.pop();
|
v = stack.pop();
|
||||||
val = new bn(v).cmpn(0) !== 0;
|
val = new bn(v).cmpn(0) !== 0;
|
||||||
if (o === 'notif')
|
if (o === 'notif')
|
||||||
val = !val;
|
val = !val;
|
||||||
var if_ = pc;
|
if_ = pc;
|
||||||
var else_ = script._next('else_', s, pc);
|
else_ = script._next('else_', s, pc);
|
||||||
var endif = script._next('endif', s, pc);
|
endif = script._next('endif', s, pc);
|
||||||
// Splice out the statement blocks we don't need
|
// Splice out the statement blocks we don't need
|
||||||
if (val) {
|
if (val) {
|
||||||
if (endif === -1)
|
if (endif === -1)
|
||||||
@ -339,10 +363,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'roll': {
|
case 'roll': {
|
||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
return false;
|
return false;
|
||||||
var n = new bn(stack.pop()).toNumber();
|
n = new bn(stack.pop()).toNumber();
|
||||||
if (n < 0 || n >= stack.length)
|
if (n < 0 || n >= stack.length)
|
||||||
return false;
|
return false;
|
||||||
var v = stack[-n - 1];
|
v = stack[-n - 1];
|
||||||
if (o === 'roll')
|
if (o === 'roll')
|
||||||
stack.splice(stack.length - n - 1, 1);
|
stack.splice(stack.length - n - 1, 1);
|
||||||
stack.push(v);
|
stack.push(v);
|
||||||
@ -351,9 +375,9 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'rot': {
|
case 'rot': {
|
||||||
if (stack.length < 3)
|
if (stack.length < 3)
|
||||||
return false;
|
return false;
|
||||||
var v3 = stack[stack.length - 3];
|
v3 = stack[stack.length - 3];
|
||||||
var v2 = stack[stack.length - 2];
|
v2 = stack[stack.length - 2];
|
||||||
var v1 = stack[stack.length - 1];
|
v1 = stack[stack.length - 1];
|
||||||
stack[stack.length - 3] = v2;
|
stack[stack.length - 3] = v2;
|
||||||
stack[stack.length - 2] = v3;
|
stack[stack.length - 2] = v3;
|
||||||
v2 = stack[stack.length - 2];
|
v2 = stack[stack.length - 2];
|
||||||
@ -364,8 +388,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'swap': {
|
case 'swap': {
|
||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
return false;
|
return false;
|
||||||
var v2 = stack[stack.length - 2];
|
v2 = stack[stack.length - 2];
|
||||||
var v1 = stack[stack.length - 1];
|
v1 = stack[stack.length - 1];
|
||||||
stack[stack.length - 2] = v1;
|
stack[stack.length - 2] = v1;
|
||||||
stack[stack.length - 1] = v2;
|
stack[stack.length - 1] = v2;
|
||||||
break;
|
break;
|
||||||
@ -386,8 +410,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'dup2': {
|
case 'dup2': {
|
||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
return false;
|
return false;
|
||||||
var v1 = stack[stack.length - 1];
|
v1 = stack[stack.length - 1];
|
||||||
var v2 = stack[stack.length - 2];
|
v2 = stack[stack.length - 2];
|
||||||
stack.push(v1);
|
stack.push(v1);
|
||||||
stack.push(v2);
|
stack.push(v2);
|
||||||
break;
|
break;
|
||||||
@ -395,9 +419,9 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'dup3': {
|
case 'dup3': {
|
||||||
if (stack.length < 3)
|
if (stack.length < 3)
|
||||||
return false;
|
return false;
|
||||||
var v1 = stack[stack.length - 1];
|
v1 = stack[stack.length - 1];
|
||||||
var v2 = stack[stack.length - 2];
|
v2 = stack[stack.length - 2];
|
||||||
var v3 = stack[stack.length - 3];
|
v3 = stack[stack.length - 3];
|
||||||
stack.push(v1);
|
stack.push(v1);
|
||||||
stack.push(v2);
|
stack.push(v2);
|
||||||
stack.push(v3);
|
stack.push(v3);
|
||||||
@ -406,8 +430,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'over2': {
|
case 'over2': {
|
||||||
if (stack.length < 4)
|
if (stack.length < 4)
|
||||||
return false;
|
return false;
|
||||||
var v1 = stack[stack.length - 4];
|
v1 = stack[stack.length - 4];
|
||||||
var v2 = stack[stack.length - 3];
|
v2 = stack[stack.length - 3];
|
||||||
stack.push(v1);
|
stack.push(v1);
|
||||||
stack.push(v2);
|
stack.push(v2);
|
||||||
break;
|
break;
|
||||||
@ -415,8 +439,8 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'rot2': {
|
case 'rot2': {
|
||||||
if (stack.length < 6)
|
if (stack.length < 6)
|
||||||
return false;
|
return false;
|
||||||
var v1 = stack[stack.length - 6];
|
v1 = stack[stack.length - 6];
|
||||||
var v2 = stack[stack.length - 5];
|
v2 = stack[stack.length - 5];
|
||||||
stack.splice(stack.length - 6, 2);
|
stack.splice(stack.length - 6, 2);
|
||||||
stack.push(v1);
|
stack.push(v1);
|
||||||
stack.push(v2);
|
stack.push(v2);
|
||||||
@ -425,10 +449,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'swap2': {
|
case 'swap2': {
|
||||||
if (stack.length < 4)
|
if (stack.length < 4)
|
||||||
return false;
|
return false;
|
||||||
var v4 = stack[stack.length - 4];
|
v4 = stack[stack.length - 4];
|
||||||
var v3 = stack[stack.length - 3];
|
v3 = stack[stack.length - 3];
|
||||||
var v2 = stack[stack.length - 2];
|
v2 = stack[stack.length - 2];
|
||||||
var v1 = stack[stack.length - 1];
|
v1 = stack[stack.length - 1];
|
||||||
stack[stack.length - 4] = v2;
|
stack[stack.length - 4] = v2;
|
||||||
stack[stack.length - 2] = v4;
|
stack[stack.length - 2] = v4;
|
||||||
stack[stack.length - 3] = v1;
|
stack[stack.length - 3] = v1;
|
||||||
@ -449,7 +473,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'noteq0': {
|
case 'noteq0': {
|
||||||
if (stack.length < 1)
|
if (stack.length < 1)
|
||||||
return false;
|
return false;
|
||||||
var n = new bn(stack.pop());
|
n = new bn(stack.pop());
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case 'add1':
|
case 'add1':
|
||||||
n.iadd(1);
|
n.iadd(1);
|
||||||
@ -505,9 +529,9 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'max':
|
case 'max':
|
||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
return false;
|
return false;
|
||||||
var n2 = new bn(stack.pop());
|
n2 = new bn(stack.pop());
|
||||||
var n1 = new bn(stack.pop());
|
n1 = new bn(stack.pop());
|
||||||
var n = new bn(0);
|
n = new bn(0);
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case 'add':
|
case 'add':
|
||||||
n = n1.add(b2);
|
n = n1.add(b2);
|
||||||
@ -551,7 +575,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var res = n.cmpn(0) !== 0;
|
res = n.cmpn(0) !== 0;
|
||||||
if (o === 'numeqverify') {
|
if (o === 'numeqverify') {
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -563,10 +587,10 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'within':
|
case 'within':
|
||||||
if (stack.length < 3)
|
if (stack.length < 3)
|
||||||
return false;
|
return false;
|
||||||
var n3 = new bn(stack.pop());
|
n3 = new bn(stack.pop());
|
||||||
var n2 = new bn(stack.pop());
|
n2 = new bn(stack.pop());
|
||||||
var n1 = new bn(stack.pop());
|
n1 = new bn(stack.pop());
|
||||||
var val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
|
val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
|
||||||
stack.push(val.cmpn(0) !== 0 ? [ 1 ] : []);
|
stack.push(val.cmpn(0) !== 0 ? [ 1 ] : []);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -611,7 +635,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
case 'eq': {
|
case 'eq': {
|
||||||
if (stack.length < 2)
|
if (stack.length < 2)
|
||||||
return false;
|
return false;
|
||||||
var res = utils.isEqual(stack.pop(), stack.pop());
|
res = utils.isEqual(stack.pop(), stack.pop());
|
||||||
if (o === 'eqverify') {
|
if (o === 'eqverify') {
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -625,19 +649,19 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
if (!tx || stack.length < 2)
|
if (!tx || stack.length < 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var pub = stack.pop();
|
pub = stack.pop();
|
||||||
var sig = stack.pop();
|
sig = stack.pop();
|
||||||
var type = sig[sig.length - 1];
|
type = sig[sig.length - 1];
|
||||||
if (!constants.rhashType[type & 0x1f])
|
if (!constants.rhashType[type & 0x1f])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!script.isValidSig(sig))
|
if (!script.isValidSig(sig))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var subscript = script.subscript(s, lastSep);
|
subscript = script.subscript(s, lastSep);
|
||||||
var hash = tx.subscriptHash(index, subscript, type);
|
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 (o === 'checksigverify') {
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -652,7 +676,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
if (!tx || stack.length < 3)
|
if (!tx || stack.length < 3)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var n = stack.pop();
|
n = stack.pop();
|
||||||
if (n.length !== 1 || !(1 <= n[0] && n[0] <= 15))
|
if (n.length !== 1 || !(1 <= n[0] && n[0] <= 15))
|
||||||
return false;
|
return false;
|
||||||
n = n[0] || 0;
|
n = n[0] || 0;
|
||||||
@ -660,16 +684,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
if (stack.length < n + 1)
|
if (stack.length < n + 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var keys = [];
|
keys = [];
|
||||||
for (var i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
var key = stack.pop();
|
key = stack.pop();
|
||||||
if (!(33 <= key.length && key.length <= 65))
|
if (!(33 <= key.length && key.length <= 65))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
keys.push(key);
|
keys.push(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
var m = stack.pop();
|
m = stack.pop();
|
||||||
if (m.length !== 1 || !(1 <= m[0] && m[0] <= n))
|
if (m.length !== 1 || !(1 <= m[0] && m[0] <= n))
|
||||||
return false;
|
return false;
|
||||||
m = m[0] || 0;
|
m = m[0] || 0;
|
||||||
@ -677,23 +701,23 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
if (stack.length < m + 1)
|
if (stack.length < m + 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var subscript = script.subscript(s, lastSep);
|
subscript = script.subscript(s, lastSep);
|
||||||
|
|
||||||
// Get signatures
|
// Get signatures
|
||||||
var succ = 0;
|
succ = 0;
|
||||||
for (var i = 0; i < m; i++) {
|
for (i = 0; i < m; i++) {
|
||||||
var sig = stack.pop();
|
sig = stack.pop();
|
||||||
var type = sig[sig.length - 1];
|
type = sig[sig.length - 1];
|
||||||
if (!constants.rhashType[type & 0x1f])
|
if (!constants.rhashType[type & 0x1f])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!script.isValidSig(sig))
|
if (!script.isValidSig(sig))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var hash = tx.subscriptHash(index, subscript, type);
|
hash = tx.subscriptHash(index, subscript, type);
|
||||||
|
|
||||||
// Strict order:
|
// 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)
|
if (res)
|
||||||
succ++;
|
succ++;
|
||||||
}
|
}
|
||||||
@ -701,7 +725,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
// Extra value
|
// Extra value
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
|
||||||
var res = succ >= m;
|
res = succ >= m;
|
||||||
if (o === 'checkmultisigverify') {
|
if (o === 'checkmultisigverify') {
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -716,15 +740,15 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
if (!tx || stack.length === 0)
|
if (!tx || stack.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var lock = new bn(stack[stack.length - 1]).toNumber();
|
lock = new bn(stack[stack.length - 1]).toNumber();
|
||||||
|
|
||||||
if (lock < 0)
|
if (lock < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var threshold = constants.locktimeThreshold;
|
threshold = constants.locktimeThreshold;
|
||||||
if (!(
|
if (!(
|
||||||
(tx.lock < threshold && lock < threshold) ||
|
(tx.lock < threshold && lock < threshold)
|
||||||
(tx.lock >= threshold && lock >= threshold)
|
|| (tx.lock >= threshold && lock >= threshold)
|
||||||
)) {
|
)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -747,15 +771,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
if (recurse++ > 2)
|
if (recurse++ > 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var evalScript = stack.pop();
|
evalScript = stack.pop();
|
||||||
if (!Array.isArray(evalScript))
|
if (!Array.isArray(evalScript))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
evalScript = script.decode(evalScript);
|
evalScript = script.decode(evalScript);
|
||||||
|
|
||||||
var res = evalScript.some(function(op) {
|
res = evalScript.some(function(op) {
|
||||||
return op === 'codesep';
|
return op === 'codesep';
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -780,11 +805,16 @@ script.execute = function execute(s, stack, tx, index, recurse) {
|
|||||||
|
|
||||||
script.exec = function(input, output, tx, i, recurse) {
|
script.exec = function(input, output, tx, i, recurse) {
|
||||||
var stack = [];
|
var stack = [];
|
||||||
|
var res;
|
||||||
|
|
||||||
script.execute(input, stack, tx, i, recurse);
|
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 || new bn(stack.pop()).cmp(0) !== 0)
|
||||||
if (!res || stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
|
if (!res || stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -847,8 +877,8 @@ script.isPubkey = function isPubkey(s, key) {
|
|||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
return utils.isEqual(s[0], key);
|
return utils.isEqual(s[0], key);
|
||||||
else
|
|
||||||
return s[0];
|
return s[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
||||||
@ -858,21 +888,24 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) {
|
|||||||
if (s.length !== 5)
|
if (s.length !== 5)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var match = s[0] === 'dup' &&
|
var match = s[0] === 'dup'
|
||||||
s[1] === 'hash160' &&
|
&& s[1] === 'hash160'
|
||||||
Array.isArray(s[2]) &&
|
&& Array.isArray(s[2])
|
||||||
s[3] === 'eqverify' &&
|
&& s[3] === 'eqverify'
|
||||||
s[4] === 'checksig';
|
&& s[4] === 'checksig';
|
||||||
|
|
||||||
if (!match)
|
if (!match)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (hash)
|
if (hash)
|
||||||
return utils.isEqual(s[2], hash);
|
return utils.isEqual(s[2], hash);
|
||||||
else
|
|
||||||
return s[2];
|
return s[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
script.isMultisig = function isMultisig(s, pubs) {
|
script.isMultisig = function isMultisig(s, pubs) {
|
||||||
|
var m, n, keys, isArray, total;
|
||||||
|
|
||||||
if (script.lockTime(s))
|
if (script.lockTime(s))
|
||||||
s = s.slice(3);
|
s = s.slice(3);
|
||||||
|
|
||||||
@ -883,7 +916,7 @@ script.isMultisig = function isMultisig(s, pubs) {
|
|||||||
if (pubs && !Array.isArray(pubs[0]))
|
if (pubs && !Array.isArray(pubs[0]))
|
||||||
pubs = [pubs];
|
pubs = [pubs];
|
||||||
|
|
||||||
var m = s[0];
|
m = s[0];
|
||||||
if (typeof m === 'number' && m >= 1 && m <= 15)
|
if (typeof m === 'number' && m >= 1 && m <= 15)
|
||||||
m = [m];
|
m = [m];
|
||||||
if (!Array.isArray(m) || m.length !== 1)
|
if (!Array.isArray(m) || m.length !== 1)
|
||||||
@ -893,7 +926,7 @@ script.isMultisig = function isMultisig(s, pubs) {
|
|||||||
if (s[s.length - 1] !== 'checkmultisig')
|
if (s[s.length - 1] !== 'checkmultisig')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var n = s[s.length - 2];
|
n = s[s.length - 2];
|
||||||
if (typeof n === 'number' && n >= 1 && n <= 15)
|
if (typeof n === 'number' && n >= 1 && n <= 15)
|
||||||
n = [n];
|
n = [n];
|
||||||
if (!Array.isArray(n) || n.length !== 1)
|
if (!Array.isArray(n) || n.length !== 1)
|
||||||
@ -903,17 +936,19 @@ script.isMultisig = function isMultisig(s, pubs) {
|
|||||||
if (n + 3 !== s.length)
|
if (n + 3 !== s.length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var keys = s.slice(1, 1 + n);
|
keys = s.slice(1, 1 + n);
|
||||||
var isArray = keys.every(function(k) {
|
|
||||||
|
isArray = keys.every(function(k) {
|
||||||
return Array.isArray(k);
|
return Array.isArray(k);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isArray)
|
if (!isArray)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!pubs)
|
if (!pubs)
|
||||||
return keys;
|
return keys;
|
||||||
|
|
||||||
var total = keys.filter(function(k) {
|
total = keys.filter(function(k) {
|
||||||
return pubs.some(function(pub) {
|
return pubs.some(function(pub) {
|
||||||
return utils.isEqual(k, pub);
|
return utils.isEqual(k, pub);
|
||||||
});
|
});
|
||||||
@ -929,10 +964,10 @@ script.isScripthash = function isScripthash(s, hash) {
|
|||||||
if (s.length !== 3)
|
if (s.length !== 3)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var res = s[0] === 'hash160' &&
|
var res = s[0] === 'hash160'
|
||||||
Array.isArray(s[1]) &&
|
&& Array.isArray(s[1])
|
||||||
s[1].length === 20 &&
|
&& s[1].length === 20
|
||||||
s[2] === 'eq';
|
&& s[2] === 'eq';
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -947,9 +982,9 @@ script.isColored = function isColored(s) {
|
|||||||
if (s.length !== 2)
|
if (s.length !== 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return s[0] === 'ret' &&
|
return s[0] === 'ret'
|
||||||
Array.isArray(s[1]) &&
|
&& Array.isArray(s[1])
|
||||||
s[1].length <= 40;
|
&& s[1].length <= 40;
|
||||||
};
|
};
|
||||||
|
|
||||||
script.colored = function colored(s) {
|
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]))
|
if (s.length !== 2 || !Array.isArray(s[0]) || !Array.isArray(s[1]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// var res = script.isValidSig(s[0]) &&
|
// var res = script.isValidSig(s[0])
|
||||||
// 33 <= s[1].length && s[1].length <= 65;
|
// && 33 <= s[1].length && s[1].length <= 65;
|
||||||
|
|
||||||
var res = 9 <= s[0].length && s[0].length <= 73 &&
|
var res = 9 <= s[0].length && s[0].length <= 73
|
||||||
33 <= s[1].length && s[1].length <= 65;
|
&& 33 <= s[1].length && s[1].length <= 65;
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
@ -1002,21 +1037,23 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script.isMultisigInput = function isMultisigInput(s, pubs, tx, i) {
|
script.isMultisigInput = function isMultisigInput(s, pubs, tx, i) {
|
||||||
|
var i, res, o;
|
||||||
|
|
||||||
if (s.length < 3)
|
if (s.length < 3)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Array.isArray(s[0]) || s[0].length !== 0)
|
if (!Array.isArray(s[0]) || s[0].length !== 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (var i = 1; i < s.length; i++) {
|
for (i = 1; i < s.length; i++) {
|
||||||
// var res = Array.isArray(s[i]) && script.isValidSig(s[i]);
|
// res = Array.isArray(s[i]) && script.isValidSig(s[i]);
|
||||||
var res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
|
res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pubs && pubs.length >= 2) {
|
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);
|
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) {
|
script.isScripthashInput = function isScripthashInput(s, redeem) {
|
||||||
|
var i, res, r, keys;
|
||||||
|
|
||||||
if (s.length < 4)
|
if (s.length < 4)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Array.isArray(s[0]) || s[0].length !== 0)
|
if (!Array.isArray(s[0]) || s[0].length !== 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (var i = 1; i < s.length - 1; i++) {
|
for (i = 1; i < s.length - 1; i++) {
|
||||||
// var res = Array.isArray(s[i]) && script.isValidSig(s[i]);
|
// res = Array.isArray(s[i]) && script.isValidSig(s[i]);
|
||||||
var res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
|
res = Array.isArray(s[i]) && 9 <= s[i].length && s[i].length <= 73;
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
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)
|
if (r[r.length - 1] !== constants.opcodes.checkmultisig)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (redeem)
|
if (redeem)
|
||||||
return utils.isEqual(redeem, r);
|
return utils.isEqual(redeem, r);
|
||||||
|
|
||||||
var keys = script.decode(r).slice(1, -2);
|
keys = script.decode(r).slice(1, -2);
|
||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
};
|
};
|
||||||
@ -1061,6 +1100,8 @@ script.isScripthashInput = function isScripthashInput(s, redeem) {
|
|||||||
* This function is consensus-critical since BIP66.
|
* This function is consensus-critical since BIP66.
|
||||||
*/
|
*/
|
||||||
script.isValidSig = function(sig) {
|
script.isValidSig = function(sig) {
|
||||||
|
var lenR, lenS;
|
||||||
|
|
||||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||||
if (sig.length === 0)
|
if (sig.length === 0)
|
||||||
@ -1093,14 +1134,14 @@ script.isValidSig = function(sig) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Extract the length of the R element.
|
// 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.
|
// Make sure the length of the S element is still inside the signature.
|
||||||
if (5 + lenR >= sig.length)
|
if (5 + lenR >= sig.length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Extract the length of the S element.
|
// 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
|
// Verify that the length of the signature matches the sum of the length
|
||||||
// of the elements.
|
// of the elements.
|
||||||
@ -1146,14 +1187,15 @@ script.isValidSig = function(sig) {
|
|||||||
|
|
||||||
script.format = function(input, output) {
|
script.format = function(input, output) {
|
||||||
var scripts = [];
|
var scripts = [];
|
||||||
|
var prev, redeem;
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
scripts.push(input.script);
|
scripts.push(input.script);
|
||||||
if (input.out.tx && input.out.tx.outputs[input.out.index]) {
|
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);
|
scripts.push(prev);
|
||||||
if (script.isScripthash(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);
|
scripts.push(redeem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,27 +22,31 @@ function TXPool(wallet) {
|
|||||||
// Load TXs from storage
|
// Load TXs from storage
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(TXPool, EventEmitter);
|
inherits(TXPool, EventEmitter);
|
||||||
module.exports = TXPool;
|
|
||||||
|
|
||||||
TXPool.prototype._init = function init() {
|
TXPool.prototype._init = function init() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!this._storage) {
|
if (!this._storage) {
|
||||||
this._loaded = true;
|
this._loaded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var s = this._storage.createReadStream({
|
var s = this._storage.createReadStream({
|
||||||
keys: false,
|
keys: false,
|
||||||
start: this._prefix,
|
start: this._prefix,
|
||||||
end: this._prefix + 'z'
|
end: this._prefix + 'z'
|
||||||
});
|
});
|
||||||
|
|
||||||
s.on('data', function(data) {
|
s.on('data', function(data) {
|
||||||
self.add(bcoin.tx.fromJSON(data), true);
|
self.add(bcoin.tx.fromJSON(data), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
s.on('error', function(err) {
|
s.on('error', function(err) {
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
s.on('end', function() {
|
s.on('end', function() {
|
||||||
self._loaded = true;
|
self._loaded = true;
|
||||||
self.emit('load', self._lastTs);
|
self.emit('load', self._lastTs);
|
||||||
@ -51,6 +55,9 @@ TXPool.prototype._init = function init() {
|
|||||||
|
|
||||||
TXPool.prototype.add = function add(tx, noWrite) {
|
TXPool.prototype.add = function add(tx, noWrite) {
|
||||||
var hash = tx.hash('hex');
|
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
|
// Ignore stale pending transactions
|
||||||
if (tx.ts === 0 && tx.ps + 2 * 24 * 3600 < +new Date() / 1000) {
|
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;
|
this._all[hash] = tx;
|
||||||
|
|
||||||
var ownInput = this._wallet.ownInput(tx);
|
ownInput = this._wallet.ownInput(tx);
|
||||||
var ownOutput = this._wallet.ownOutput(tx);
|
ownOutput = this._wallet.ownOutput(tx);
|
||||||
var updated = false;
|
updated = false;
|
||||||
|
|
||||||
// Consume unspent money or add orphans
|
// Consume unspent money or add orphans
|
||||||
for (var i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
var input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
var key = input.out.hash + '/' + input.out.index;
|
key = input.out.hash + '/' + input.out.index;
|
||||||
var unspent = this._unspent[key];
|
unspent = this._unspent[key];
|
||||||
|
|
||||||
if (unspent) {
|
if (unspent) {
|
||||||
// Add TX to inputs and spend money
|
// 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
|
// Skip invalid transactions
|
||||||
if (!tx.verify(index))
|
if (!tx.verify(index))
|
||||||
@ -99,7 +106,7 @@ TXPool.prototype.add = function add(tx, noWrite) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Add orphan, if no parent transaction is yet known
|
// 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])
|
if (this._orphans[key])
|
||||||
this._orphans[key].push(orphan);
|
this._orphans[key].push(orphan);
|
||||||
else
|
else
|
||||||
@ -130,19 +137,19 @@ TXPool.prototype.add = function add(tx, noWrite) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add unspent outputs or fullfill orphans
|
// Add unspent outputs or fullfill orphans
|
||||||
for (var i = 0; i < tx.outputs.length; i++) {
|
for (i = 0; i < tx.outputs.length; i++) {
|
||||||
var out = tx.outputs[i];
|
out = tx.outputs[i];
|
||||||
|
|
||||||
// Do not add unspents for outputs that aren't ours.
|
// Do not add unspents for outputs that aren't ours.
|
||||||
if (!~ownOutput.indexOf(out))
|
if (!~ownOutput.indexOf(out))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var key = hash + '/' + i;
|
key = hash + '/' + i;
|
||||||
var orphans = this._orphans[key];
|
orphans = this._orphans[key];
|
||||||
|
|
||||||
// Add input to orphan
|
// Add input to orphan
|
||||||
if (orphans) {
|
if (orphans) {
|
||||||
var some = orphans.some(checkOrphan, this);
|
some = orphans.some(checkOrphan, this);
|
||||||
if (!some)
|
if (!some)
|
||||||
orphans = null;
|
orphans = null;
|
||||||
}
|
}
|
||||||
@ -166,10 +173,11 @@ TXPool.prototype.add = function add(tx, noWrite) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
|
TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (!this._storage || noWrite)
|
if (!this._storage || noWrite)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this._storage.put(this._prefix + hash, tx.toJSON(), function(err) {
|
this._storage.put(this._prefix + hash, tx.toJSON(), function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
@ -177,13 +185,14 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
|
TXPool.prototype._removeTX = function _removeTX(tx, noWrite) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
for (var i = 0; i < tx.outputs.length; i++)
|
for (var i = 0; i < tx.outputs.length; i++)
|
||||||
delete this._unspent[tx.hash('hex') + '/' + i];
|
delete this._unspent[tx.hash('hex') + '/' + i];
|
||||||
|
|
||||||
if (!this._storage || noWrite)
|
if (!this._storage || noWrite)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this._storage.del(this._prefix + tx.hash('hex'), function(err) {
|
this._storage.del(this._prefix + tx.hash('hex'), function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
self.emit('error', err);
|
self.emit('error', err);
|
||||||
@ -194,8 +203,8 @@ TXPool.prototype.all = function all() {
|
|||||||
return Object.keys(this._all).map(function(key) {
|
return Object.keys(this._all).map(function(key) {
|
||||||
return this._all[key];
|
return this._all[key];
|
||||||
}, this).filter(function(tx) {
|
}, this).filter(function(tx) {
|
||||||
return this._wallet.ownOutput(tx) ||
|
return this._wallet.ownOutput(tx)
|
||||||
this._wallet.ownInput(tx);
|
|| this._wallet.ownInput(tx);
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -208,9 +217,11 @@ TXPool.prototype.unspent = function unspent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype.hasUnspent = function hasUnspent(hash, unspent) {
|
TXPool.prototype.hasUnspent = function hasUnspent(hash, unspent) {
|
||||||
|
var has;
|
||||||
|
|
||||||
if (Array.isArray(hash) && hash.length && typeof hash[0] !== 'number') {
|
if (Array.isArray(hash) && hash.length && typeof hash[0] !== 'number') {
|
||||||
unspent = this.unspent();
|
unspent = this.unspent();
|
||||||
var has = hash.map(function(hash) {
|
has = hash.map(function(hash) {
|
||||||
var h = this.hasUnspent(hash, unspent);
|
var h = this.hasUnspent(hash, unspent);
|
||||||
if (!h)
|
if (!h)
|
||||||
return false;
|
return false;
|
||||||
@ -232,7 +243,7 @@ TXPool.prototype.hasUnspent = function hasUnspent(hash, unspent) {
|
|||||||
|
|
||||||
unspent = unspent || this.unspent();
|
unspent = unspent || this.unspent();
|
||||||
|
|
||||||
var has = unspent.filter(function(item) {
|
has = unspent.filter(function(item) {
|
||||||
return item.tx.hash('hex') === hash;
|
return item.tx.hash('hex') === hash;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -279,3 +290,5 @@ TXPool.prototype.fromJSON = function fromJSON(json) {
|
|||||||
this.add(bcoin.tx.fromJSON(tx));
|
this.add(bcoin.tx.fromJSON(tx));
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = TXPool;
|
||||||
|
|||||||
193
lib/bcoin/tx.js
193
lib/bcoin/tx.js
@ -47,7 +47,6 @@ function TX(data, block) {
|
|||||||
|
|
||||||
this.changeAddress = data.changeAddress || null;
|
this.changeAddress = data.changeAddress || null;
|
||||||
}
|
}
|
||||||
module.exports = TX;
|
|
||||||
|
|
||||||
TX.fee = 10000;
|
TX.fee = 10000;
|
||||||
TX.dust = 5460;
|
TX.dust = 5460;
|
||||||
@ -77,12 +76,13 @@ TX.prototype.input = function input(i, index) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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)
|
if (i instanceof TX)
|
||||||
i = { tx: i, index: index };
|
i = { tx: i, index: index };
|
||||||
else if (typeof i === 'string' || Array.isArray(i))
|
else if (typeof i === 'string' || Array.isArray(i))
|
||||||
i = { hash: i, index: index };
|
i = { hash: i, index: index };
|
||||||
|
|
||||||
var hash;
|
|
||||||
if (i.tx)
|
if (i.tx)
|
||||||
hash = i.tx.hash('hex');
|
hash = i.tx.hash('hex');
|
||||||
else if (i.out)
|
else if (i.out)
|
||||||
@ -93,7 +93,7 @@ TX.prototype._input = function _input(i, index) {
|
|||||||
if (typeof hash !== 'string')
|
if (typeof hash !== 'string')
|
||||||
hash = utils.toHex(hash);
|
hash = utils.toHex(hash);
|
||||||
|
|
||||||
var input = {
|
input = {
|
||||||
out: {
|
out: {
|
||||||
tx: (i.out ? i.out.tx : i.tx) || null,
|
tx: (i.out ? i.out.tx : i.tx) || null,
|
||||||
hash: utils.toHex(hash),
|
hash: utils.toHex(hash),
|
||||||
@ -107,8 +107,8 @@ TX.prototype._input = function _input(i, index) {
|
|||||||
utils.hidden(input.script, '_raw', i.script._raw);
|
utils.hidden(input.script, '_raw', i.script._raw);
|
||||||
|
|
||||||
if (input.out.tx) {
|
if (input.out.tx) {
|
||||||
var prev = input.out.tx.outputs[input.out.index].script;
|
prev = input.out.tx.outputs[input.out.index].script;
|
||||||
var lock = bcoin.script.lockTime(prev);
|
lock = bcoin.script.lockTime(prev);
|
||||||
if (lock) {
|
if (lock) {
|
||||||
if (this._lock === 0)
|
if (this._lock === 0)
|
||||||
this.lock = Math.max(lock.toNumber(), this.lock);
|
this.lock = Math.max(lock.toNumber(), this.lock);
|
||||||
@ -123,9 +123,9 @@ TX.prototype._input = function _input(i, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try modifying existing input first
|
// Try modifying existing input first
|
||||||
var index = this._inputIndex(hash, index);
|
index = this._inputIndex(hash, index);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
var ex = this.inputs[index];
|
ex = this.inputs[index];
|
||||||
input.out.tx = input.out.tx || ex.out.tx;
|
input.out.tx = input.out.tx || ex.out.tx;
|
||||||
input.seq = input.seq || ex.seq;
|
input.seq = input.seq || ex.seq;
|
||||||
input.script = input.script.length ? input.script : ex.script;
|
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) {
|
TX.prototype._inputIndex = function _inputIndex(hash, index) {
|
||||||
|
var i, ex;
|
||||||
|
|
||||||
if (hash instanceof TX)
|
if (hash instanceof TX)
|
||||||
hash = hash.hash('hex');
|
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)
|
if (ex.out.hash === hash && ex.out.index === index)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -151,10 +154,12 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.prevOut = function prevOut(i, def) {
|
TX.prototype.prevOut = function prevOut(i, def) {
|
||||||
|
var input;
|
||||||
|
|
||||||
if (typeof i === 'object')
|
if (typeof i === 'object')
|
||||||
i = this.inputs.indexOf(i);
|
i = this.inputs.indexOf(i);
|
||||||
|
|
||||||
var input = this.inputs[i];
|
input = this.inputs[i];
|
||||||
|
|
||||||
if (!input || !input.out.tx || input.out.index == null)
|
if (!input || !input.out.tx || input.out.index == null)
|
||||||
return def;
|
return def;
|
||||||
@ -163,6 +168,8 @@ TX.prototype.prevOut = function prevOut(i, def) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signatureHash = function signatureHash(i, type) {
|
TX.prototype.signatureHash = function signatureHash(i, type) {
|
||||||
|
var input, s, hash;
|
||||||
|
|
||||||
if (typeof i === 'object')
|
if (typeof i === 'object')
|
||||||
i = this.inputs.indexOf(i);
|
i = this.inputs.indexOf(i);
|
||||||
|
|
||||||
@ -173,18 +180,20 @@ TX.prototype.signatureHash = function signatureHash(i, type) {
|
|||||||
type = constants.hashType[type];
|
type = constants.hashType[type];
|
||||||
|
|
||||||
// Get the current input.
|
// Get the current input.
|
||||||
var input = this.inputs[i];
|
input = this.inputs[i];
|
||||||
|
|
||||||
// Get the previous output's subscript
|
// 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.
|
// 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;
|
return hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signature = function signature(i, key, type) {
|
TX.prototype.signature = function signature(i, key, type) {
|
||||||
|
var hash, signature;
|
||||||
|
|
||||||
if (typeof i === 'object')
|
if (typeof i === 'object')
|
||||||
i = this.inputs.indexOf(i);
|
i = this.inputs.indexOf(i);
|
||||||
|
|
||||||
@ -195,10 +204,10 @@ TX.prototype.signature = function signature(i, key, type) {
|
|||||||
type = constants.hashType[type];
|
type = constants.hashType[type];
|
||||||
|
|
||||||
// Get the hash of the current tx, minus the other inputs, plus the sighash.
|
// 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
|
// 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
|
// Add the sighash as a single byte to the signature
|
||||||
signature = signature.concat(type);
|
signature = signature.concat(type);
|
||||||
@ -210,6 +219,7 @@ TX.prototype.signature = function signature(i, key, type) {
|
|||||||
TX.prototype.scriptInput = function scriptInput(input, pub) {
|
TX.prototype.scriptInput = function scriptInput(input, pub) {
|
||||||
// Get the previous output's subscript
|
// Get the previous output's subscript
|
||||||
var s = input.out.tx.getSubscript(input.out.index);
|
var s = input.out.tx.getSubscript(input.out.index);
|
||||||
|
var n, i, redeem;
|
||||||
|
|
||||||
// Already has a script template (at least)
|
// Already has a script template (at least)
|
||||||
if (input.script.length)
|
if (input.script.length)
|
||||||
@ -236,11 +246,11 @@ TX.prototype.scriptInput = function scriptInput(input, pub) {
|
|||||||
// raw format: OP_FALSE [sig-1] [sig-2] ...
|
// raw format: OP_FALSE [sig-1] [sig-2] ...
|
||||||
if (bcoin.script.isMultisig(s)) {
|
if (bcoin.script.isMultisig(s)) {
|
||||||
input.script = [ [] ];
|
input.script = [ [] ];
|
||||||
var n = s[s.length - 2];
|
n = s[s.length - 2];
|
||||||
// If using pushdata instead of OP_1-16:
|
// If using pushdata instead of OP_1-16:
|
||||||
if (Array.isArray(n))
|
if (Array.isArray(n))
|
||||||
n = n[0] || 0;
|
n = n[0] || 0;
|
||||||
for (var i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
input.script[i + 1] = [];
|
input.script[i + 1] = [];
|
||||||
this._recalculateFee();
|
this._recalculateFee();
|
||||||
return;
|
return;
|
||||||
@ -250,12 +260,12 @@ TX.prototype.scriptInput = function scriptInput(input, pub) {
|
|||||||
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
|
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
|
||||||
if (bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isScripthash(s)) {
|
||||||
input.script = [ [] ];
|
input.script = [ [] ];
|
||||||
var redeem = bcoin.script.decode(pub);
|
redeem = bcoin.script.decode(pub);
|
||||||
var n = redeem[redeem.length - 2];
|
n = redeem[redeem.length - 2];
|
||||||
// If using pushdata instead of OP_1-16:
|
// If using pushdata instead of OP_1-16:
|
||||||
if (Array.isArray(n))
|
if (Array.isArray(n))
|
||||||
n = n[0] || 0;
|
n = n[0] || 0;
|
||||||
for (var i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
input.script[i + 1] = [];
|
input.script[i + 1] = [];
|
||||||
// P2SH requires the redeem script after signatures
|
// P2SH requires the redeem script after signatures
|
||||||
input.script.push(pub);
|
input.script.push(pub);
|
||||||
@ -268,6 +278,9 @@ TX.prototype.scriptInput = function scriptInput(input, pub) {
|
|||||||
|
|
||||||
// Sign the now-built scriptSigs
|
// Sign the now-built scriptSigs
|
||||||
TX.prototype.signInput = function signInput(input, key, type) {
|
TX.prototype.signInput = function signInput(input, key, type) {
|
||||||
|
var s, hash, signature;
|
||||||
|
var len, redeem, m, keys, pub, pubn, ki, totalSigs, i;
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
type = 'all';
|
type = 'all';
|
||||||
|
|
||||||
@ -275,13 +288,13 @@ TX.prototype.signInput = function signInput(input, key, type) {
|
|||||||
type = constants.hashType[type];
|
type = constants.hashType[type];
|
||||||
|
|
||||||
// Get the previous output's subscript
|
// 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.
|
// 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
|
// 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
|
// Add the sighash as a single byte to the signature
|
||||||
signature = signature.concat(type);
|
signature = signature.concat(type);
|
||||||
@ -302,8 +315,7 @@ TX.prototype.signInput = function signInput(input, key, type) {
|
|||||||
// raw format: OP_FALSE [sig-1] [sig-2] ...
|
// raw format: OP_FALSE [sig-1] [sig-2] ...
|
||||||
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
|
// p2sh format: OP_FALSE [sig-1] [sig-2] ... [redeem-script]
|
||||||
if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) {
|
||||||
var len = input.script.length;
|
len = input.script.length;
|
||||||
var redeem;
|
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isScripthash(s)) {
|
||||||
len--;
|
len--;
|
||||||
@ -312,18 +324,18 @@ TX.prototype.signInput = function signInput(input, key, type) {
|
|||||||
redeem = s;
|
redeem = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
var m = redeem[0];
|
m = redeem[0];
|
||||||
// If using pushdata instead of OP_1-16:
|
// If using pushdata instead of OP_1-16:
|
||||||
if (Array.isArray(m))
|
if (Array.isArray(m))
|
||||||
m = m[0] || 0;
|
m = m[0] || 0;
|
||||||
|
|
||||||
var keys = redeem.slice(1, -2);
|
keys = redeem.slice(1, -2);
|
||||||
var pub = key.getPublic(true, 'array');
|
pub = key.getPublic(true, 'array');
|
||||||
var pubn = key.getPublic(false, 'array');
|
pubn = key.getPublic(false, 'array');
|
||||||
|
|
||||||
// Find the key index so we can place
|
// Find the key index so we can place
|
||||||
// the signature in the same index.
|
// 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]))
|
if (utils.isEqual(pub, keys[ki]) || utils.isEqual(pubn, keys[ki]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -336,8 +348,8 @@ TX.prototype.signInput = function signInput(input, key, type) {
|
|||||||
|
|
||||||
// Add our signature to the correct slot
|
// Add our signature to the correct slot
|
||||||
// and count the total number of signatures.
|
// and count the total number of signatures.
|
||||||
var totalSigs = 0;
|
totalSigs = 0;
|
||||||
for (var i = 1; i < len; i++) {
|
for (i = 1; i < len; i++) {
|
||||||
if (Array.isArray(input.script[i]) && input.script[i].length) {
|
if (Array.isArray(input.script[i]) && input.script[i].length) {
|
||||||
totalSigs++;
|
totalSigs++;
|
||||||
continue;
|
continue;
|
||||||
@ -353,7 +365,7 @@ TX.prototype.signInput = function signInput(input, key, type) {
|
|||||||
|
|
||||||
// All signatures added. Finalize by removing empty slots.
|
// All signatures added. Finalize by removing empty slots.
|
||||||
if (totalSigs >= m) {
|
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)
|
if (Array.isArray(input.script[i]) && !input.script[i].length)
|
||||||
input.script.splice(i, 1);
|
input.script.splice(i, 1);
|
||||||
}
|
}
|
||||||
@ -414,6 +426,8 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
|
|||||||
options = options || output;
|
options = options || output;
|
||||||
|
|
||||||
var script = output.script ? output.script.slice() : [];
|
var script = output.script ? output.script.slice() : [];
|
||||||
|
var keys, m, n;
|
||||||
|
var hash, color;
|
||||||
|
|
||||||
if (output.script && output.script._raw)
|
if (output.script && output.script._raw)
|
||||||
utils.hidden(script, '_raw', 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-0011.mediawiki
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0019.mediawiki
|
// https://github.com/bitcoin/bips/blob/master/bip-0019.mediawiki
|
||||||
// [required-sigs] [pubkey-hash1] [pubkey-hash2] ... [number-of-keys] checkmultisig
|
// [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) {
|
if (keys === options.address) {
|
||||||
keys = keys.map(function(address) {
|
keys = keys.map(function(address) {
|
||||||
@ -440,8 +454,8 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
|
|||||||
|
|
||||||
// compat:
|
// compat:
|
||||||
options.m = options.minSignatures || options.m;
|
options.m = options.minSignatures || options.m;
|
||||||
var m = options.m || keys.length;
|
m = options.m || keys.length;
|
||||||
var n = options.n || keys.length;
|
n = options.n || keys.length;
|
||||||
|
|
||||||
assert(m >= 1 && m <= n);
|
assert(m >= 1 && m <= n);
|
||||||
if (options.hash)
|
if (options.hash)
|
||||||
@ -453,7 +467,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
|
|||||||
|
|
||||||
// make it p2sh
|
// make it p2sh
|
||||||
if (options.scripthash) {
|
if (options.scripthash) {
|
||||||
var hash = utils.ripesha(bcoin.script.encode(script));
|
hash = utils.ripesha(bcoin.script.encode(script));
|
||||||
script = [
|
script = [
|
||||||
'hash160',
|
'hash160',
|
||||||
hash,
|
hash,
|
||||||
@ -480,7 +494,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
|
|||||||
'checksig'
|
'checksig'
|
||||||
];
|
];
|
||||||
} else if (options.color) {
|
} else if (options.color) {
|
||||||
var color = options.color;
|
color = options.color;
|
||||||
if (typeof color === 'string')
|
if (typeof color === 'string')
|
||||||
color = utils.ascii2array(color);
|
color = utils.ascii2array(color);
|
||||||
assert(color.length <= 40);
|
assert(color.length <= 40);
|
||||||
@ -494,15 +508,13 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.getSubscript = function getSubscript(index) {
|
TX.prototype.getSubscript = function getSubscript(index) {
|
||||||
var output = this.outputs[index];
|
var script = this.outputs[index].script;
|
||||||
assert(output);
|
|
||||||
|
|
||||||
var script = output.script;
|
|
||||||
return bcoin.script.subscript(script);
|
return bcoin.script.subscript(script);
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
|
TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
|
||||||
var copy = this.clone();
|
var copy = this.clone();
|
||||||
|
var verifyStr, hash;
|
||||||
|
|
||||||
if (typeof type === 'string')
|
if (typeof type === 'string')
|
||||||
type = constants.hashType[type];
|
type = constants.hashType[type];
|
||||||
@ -550,11 +562,11 @@ TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
|
|||||||
copy.inputs[0].script = s;
|
copy.inputs[0].script = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
var verifyStr = copy.render();
|
verifyStr = copy.render();
|
||||||
|
|
||||||
utils.writeU32(verifyStr, type, verifyStr.length);
|
utils.writeU32(verifyStr, type, verifyStr.length);
|
||||||
|
|
||||||
var hash = utils.dsha256(verifyStr);
|
hash = utils.dsha256(verifyStr);
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
@ -568,6 +580,8 @@ TX.prototype.verify = function verify(index, force) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
return this.inputs.every(function(input, i) {
|
return this.inputs.every(function(input, i) {
|
||||||
|
var stack, prev, push, res, redeem;
|
||||||
|
|
||||||
if (index !== undefined && index !== i)
|
if (index !== undefined && index !== i)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -576,19 +590,19 @@ TX.prototype.verify = function verify(index, force) {
|
|||||||
|
|
||||||
assert(input.out.tx.outputs.length > input.out.index);
|
assert(input.out.tx.outputs.length > input.out.index);
|
||||||
|
|
||||||
var stack = [];
|
stack = [];
|
||||||
var prev = input.out.tx.outputs[input.out.index].script;
|
prev = input.out.tx.outputs[input.out.index].script;
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(prev)) {
|
if (bcoin.script.isScripthash(prev)) {
|
||||||
// p2sh transactions cannot have anything
|
// p2sh transactions cannot have anything
|
||||||
// other than pushdata ops in the scriptSig
|
// 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)
|
if (!push)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bcoin.script.execute(input.script, stack, this, i);
|
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)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -598,7 +612,7 @@ TX.prototype.verify = function verify(index, force) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(prev)) {
|
if (bcoin.script.isScripthash(prev)) {
|
||||||
var redeem = input.script[input.script.length - 1];
|
redeem = input.script[input.script.length - 1];
|
||||||
if (!Array.isArray(redeem))
|
if (!Array.isArray(redeem))
|
||||||
return false;
|
return false;
|
||||||
redeem = bcoin.script.decode(redeem);
|
redeem = bcoin.script.decode(redeem);
|
||||||
@ -618,19 +632,23 @@ TX.prototype.isCoinbase = function isCoinbase() {
|
|||||||
TX.prototype.maxSize = function maxSize() {
|
TX.prototype.maxSize = function maxSize() {
|
||||||
// Create copy with 0-script inputs
|
// Create copy with 0-script inputs
|
||||||
var copy = this.clone();
|
var copy = this.clone();
|
||||||
|
var size;
|
||||||
|
|
||||||
copy.inputs.forEach(function(input) {
|
copy.inputs.forEach(function(input) {
|
||||||
input.script = [];
|
input.script = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
var size = copy.render().length;
|
size = copy.render().length;
|
||||||
|
|
||||||
// Add size for signatures and public keys
|
// Add size for signatures and public keys
|
||||||
copy.inputs.forEach(function(input, i) {
|
copy.inputs.forEach(function(input, i) {
|
||||||
|
var s, m, n, script, redeem;
|
||||||
|
|
||||||
// Get the previous output's script
|
// 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
|
// 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)) {
|
if (bcoin.script.isPubkey(s)) {
|
||||||
// Signature + len
|
// Signature + len
|
||||||
@ -651,7 +669,7 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
// Empty byte
|
// Empty byte
|
||||||
size += 1;
|
size += 1;
|
||||||
// Signature + len
|
// Signature + len
|
||||||
var m = s[0];
|
m = s[0];
|
||||||
// If using pushdata instead of OP_1-16:
|
// If using pushdata instead of OP_1-16:
|
||||||
if (Array.isArray(m))
|
if (Array.isArray(m))
|
||||||
m = m[0] || 0;
|
m = m[0] || 0;
|
||||||
@ -661,8 +679,7 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(s)) {
|
if (bcoin.script.isScripthash(s)) {
|
||||||
var script = this.inputs[i].script;
|
script = this.inputs[i].script;
|
||||||
var redeem, m, n;
|
|
||||||
if (script.length) {
|
if (script.length) {
|
||||||
redeem = bcoin.script.decode(script[script.length - 1]);
|
redeem = bcoin.script.decode(script[script.length - 1]);
|
||||||
m = redeem[0];
|
m = redeem[0];
|
||||||
@ -724,6 +741,9 @@ TX.prototype.utxos = function utxos(unspent) {
|
|||||||
var utxos = [];
|
var utxos = [];
|
||||||
|
|
||||||
var lastAdded = 0;
|
var lastAdded = 0;
|
||||||
|
|
||||||
|
var byteSize, addFee, change;
|
||||||
|
|
||||||
function addInput(unspent, i) {
|
function addInput(unspent, i) {
|
||||||
// Add new inputs until TX will have enough funds to cover both
|
// Add new inputs until TX will have enough funds to cover both
|
||||||
// minimum post cost and fee
|
// minimum post cost and fee
|
||||||
@ -744,9 +764,9 @@ TX.prototype.utxos = function utxos(unspent) {
|
|||||||
// (10000 satoshi for every 1024 bytes)
|
// (10000 satoshi for every 1024 bytes)
|
||||||
do {
|
do {
|
||||||
// Calculate maximum possible size after signing
|
// 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));
|
total.iadd(new bn(addFee * TX.fee));
|
||||||
fee += addFee;
|
fee += addFee;
|
||||||
|
|
||||||
@ -764,7 +784,7 @@ TX.prototype.utxos = function utxos(unspent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// How much money is left after sending outputs
|
// 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.
|
// Clear the tx of everything we added.
|
||||||
this.inputs = inputs;
|
this.inputs = inputs;
|
||||||
@ -812,6 +832,7 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) {
|
|||||||
|
|
||||||
TX.prototype._recalculateFee = function recalculateFee() {
|
TX.prototype._recalculateFee = function recalculateFee() {
|
||||||
var output = this.changeOutput;
|
var output = this.changeOutput;
|
||||||
|
|
||||||
if (!output) {
|
if (!output) {
|
||||||
this.output({
|
this.output({
|
||||||
address: this.changeAddress,
|
address: this.changeAddress,
|
||||||
@ -864,26 +885,28 @@ TX.getInputData = function getInputData(input) {
|
|||||||
if (!input || !input.script) return;
|
if (!input || !input.script) return;
|
||||||
|
|
||||||
var script = input.script;
|
var script = input.script;
|
||||||
|
var scriptSig, pub, hash, addr, redeem, data;
|
||||||
|
var output;
|
||||||
|
|
||||||
if (bcoin.script.isPubkeyhashInput(script)) {
|
if (bcoin.script.isPubkeyhashInput(script)) {
|
||||||
var scriptSig = utils.toHex(script[0]);
|
scriptSig = utils.toHex(script[0]);
|
||||||
var pubKey = script[1];
|
pub = script[1];
|
||||||
var hash = utils.ripesha(pubKey);
|
hash = utils.ripesha(pub);
|
||||||
var addr = bcoin.wallet.hash2addr(hash);
|
addr = bcoin.wallet.hash2addr(hash);
|
||||||
return {
|
return {
|
||||||
sig: scriptSig,
|
sig: scriptSig,
|
||||||
pub: pubKey,
|
pub: pub,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
addr: addr
|
addr: addr
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isScripthashInput(script)) {
|
if (bcoin.script.isScripthashInput(script)) {
|
||||||
var pub = script[script.length - 1];
|
pub = script[script.length - 1];
|
||||||
var hash = utils.ripesha(pub);
|
hash = utils.ripesha(pub);
|
||||||
var addr = bcoin.wallet.hash2addr(hash, 'scripthash');
|
addr = bcoin.wallet.hash2addr(hash, 'scripthash');
|
||||||
var redeem = bcoin.script.decode(pub);
|
redeem = bcoin.script.decode(pub);
|
||||||
var data = TX.getOutputData({ script: redeem });
|
data = TX.getOutputData({ script: redeem });
|
||||||
data.pub = pub;
|
data.pub = pub;
|
||||||
data.hash = hash;
|
data.hash = hash;
|
||||||
data.addr = addr;
|
data.addr = addr;
|
||||||
@ -904,7 +927,7 @@ TX.getInputData = function getInputData(input) {
|
|||||||
if (!input.out.tx)
|
if (!input.out.tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var output = input.out.tx.outputs[input.out.index];
|
output = input.out.tx.outputs[input.out.index];
|
||||||
|
|
||||||
return TX.getOutputData(output);
|
return TX.getOutputData(output);
|
||||||
};
|
};
|
||||||
@ -913,22 +936,23 @@ TX.getOutputData = function getOutputData(output) {
|
|||||||
if (!output || !output.script) return;
|
if (!output || !output.script) return;
|
||||||
|
|
||||||
var script = output.script;
|
var script = output.script;
|
||||||
|
var pub, hash, addr, pubs;
|
||||||
|
|
||||||
if (bcoin.script.isPubkey(script)) {
|
if (bcoin.script.isPubkey(script)) {
|
||||||
var pubKey = script[0];
|
pub = script[0];
|
||||||
var hash = utils.ripesha(pubKey);
|
hash = utils.ripesha(pub);
|
||||||
var addr = bcoin.wallet.hash2addr(hash);
|
addr = bcoin.wallet.hash2addr(hash);
|
||||||
return {
|
return {
|
||||||
sig: null,
|
sig: null,
|
||||||
pub: pubKey,
|
pub: pub,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
addr: addr
|
addr: addr
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isPubkeyhash(script)) {
|
if (bcoin.script.isPubkeyhash(script)) {
|
||||||
var hash = script[2];
|
hash = script[2];
|
||||||
var addr = bcoin.wallet.hash2addr(hash);
|
addr = bcoin.wallet.hash2addr(hash);
|
||||||
return {
|
return {
|
||||||
sig: null,
|
sig: null,
|
||||||
pub: 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) {
|
if (pubs) {
|
||||||
var hash = utils.ripesha(pubs[0]);
|
hash = utils.ripesha(pubs[0]);
|
||||||
var addr = bcoin.wallet.hash2addr(hash);
|
addr = bcoin.wallet.hash2addr(hash);
|
||||||
return {
|
return {
|
||||||
sig: null,
|
sig: null,
|
||||||
pub: pubs[0],
|
pub: pubs[0],
|
||||||
@ -963,8 +987,8 @@ TX.getOutputData = function getOutputData(output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(script)) {
|
if (bcoin.script.isScripthash(script)) {
|
||||||
var hash = utils.toHex(s[1]);
|
hash = utils.toHex(s[1]);
|
||||||
var addr = bcoin.wallet.hash2addr(hash, 'scripthash');
|
addr = bcoin.wallet.hash2addr(hash, 'scripthash');
|
||||||
return {
|
return {
|
||||||
sig: null,
|
sig: null,
|
||||||
pub: null,
|
pub: null,
|
||||||
@ -991,12 +1015,14 @@ TX.prototype.getFee = function getFee() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.funds = function funds(side) {
|
TX.prototype.funds = function funds(side) {
|
||||||
|
var acc = new bn(0);
|
||||||
|
var inputs;
|
||||||
|
|
||||||
if (side === 'in') {
|
if (side === 'in') {
|
||||||
var inputs = this.inputs.filter(function(input) {
|
inputs = this.inputs.filter(function(input) {
|
||||||
return input.out.tx;
|
return input.out.tx;
|
||||||
});
|
});
|
||||||
|
|
||||||
var acc = new bn(0);
|
|
||||||
if (inputs.length === 0)
|
if (inputs.length === 0)
|
||||||
return acc;
|
return acc;
|
||||||
|
|
||||||
@ -1008,7 +1034,6 @@ TX.prototype.funds = function funds(side) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
var acc = new bn(0);
|
|
||||||
if (this.outputs.length === 0)
|
if (this.outputs.length === 0)
|
||||||
return acc;
|
return acc;
|
||||||
|
|
||||||
@ -1043,3 +1068,5 @@ TX.fromJSON = function fromJSON(json) {
|
|||||||
|
|
||||||
return tx;
|
return tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = TX;
|
||||||
|
|||||||
@ -7,15 +7,19 @@ var util = require('util');
|
|||||||
function toArray(msg, enc) {
|
function toArray(msg, enc) {
|
||||||
if (Array.isArray(msg))
|
if (Array.isArray(msg))
|
||||||
return msg.slice();
|
return msg.slice();
|
||||||
|
|
||||||
if (!msg)
|
if (!msg)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
var res = [];
|
var res = [];
|
||||||
|
var i, c, hi, lo, slice, num;
|
||||||
|
|
||||||
if (typeof msg === 'string') {
|
if (typeof msg === 'string') {
|
||||||
if (!enc) {
|
if (!enc) {
|
||||||
for (var i = 0; i < msg.length; i++) {
|
for (i = 0; i < msg.length; i++) {
|
||||||
var c = msg.charCodeAt(i);
|
c = msg.charCodeAt(i);
|
||||||
var hi = c >> 8;
|
hi = c >> 8;
|
||||||
var lo = c & 0xff;
|
lo = c & 0xff;
|
||||||
if (hi)
|
if (hi)
|
||||||
res.push(hi, lo);
|
res.push(hi, lo);
|
||||||
else
|
else
|
||||||
@ -25,9 +29,10 @@ function toArray(msg, enc) {
|
|||||||
msg = msg.replace(/[^a-z0-9]+/ig, '');
|
msg = msg.replace(/[^a-z0-9]+/ig, '');
|
||||||
if (msg.length % 2 !== 0)
|
if (msg.length % 2 !== 0)
|
||||||
msg = '0' + msg;
|
msg = '0' + msg;
|
||||||
for (var i = 0; i < msg.length; i += 8) {
|
|
||||||
var slice = msg.slice(i, i + 8);
|
for (i = 0; i < msg.length; i += 8) {
|
||||||
var num = parseInt(slice, 16);
|
slice = msg.slice(i, i + 8);
|
||||||
|
num = parseInt(slice, 16);
|
||||||
|
|
||||||
if (slice.length === 8)
|
if (slice.length === 8)
|
||||||
res.push((num >>> 24) & 0xff);
|
res.push((num >>> 24) & 0xff);
|
||||||
@ -39,15 +44,16 @@ function toArray(msg, enc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < msg.length; i++)
|
for (i = 0; i < msg.length; i++)
|
||||||
res[i] = msg[i] | 0;
|
res[i] = msg[i] | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
utils.toArray = toArray;
|
utils.toArray = toArray;
|
||||||
|
|
||||||
var base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ' +
|
var base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ'
|
||||||
'abcdefghijkmnopqrstuvwxyz';
|
+ 'abcdefghijkmnopqrstuvwxyz';
|
||||||
|
|
||||||
utils.toBase58 = function toBase58(arr) {
|
utils.toBase58 = function toBase58(arr) {
|
||||||
var n = new bn(arr, 16);
|
var n = new bn(arr, 16);
|
||||||
@ -56,17 +62,20 @@ utils.toBase58 = function toBase58(arr) {
|
|||||||
var mod = new bn(0xacad10);
|
var mod = new bn(0xacad10);
|
||||||
|
|
||||||
var res = '';
|
var res = '';
|
||||||
|
|
||||||
|
var r, end, i, c;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var r = n.mod(mod);
|
r = n.mod(mod);
|
||||||
n = n.div(mod);
|
n = n.div(mod);
|
||||||
|
|
||||||
var end = n.cmpn(0) === 0;
|
end = n.cmpn(0) === 0;
|
||||||
|
|
||||||
utils.assert.equal(r.length, 1);
|
utils.assert.equal(r.length, 1);
|
||||||
r = r.words[0];
|
r = r.words[0];
|
||||||
|
|
||||||
for (var i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
var c = r % 58;
|
c = r % 58;
|
||||||
r = (r - c) / 58;
|
r = (r - c) / 58;
|
||||||
|
|
||||||
if (c === 0 && r === 0 && end)
|
if (c === 0 && r === 0 && end)
|
||||||
@ -77,7 +86,7 @@ utils.toBase58 = function toBase58(arr) {
|
|||||||
} while (!end);
|
} while (!end);
|
||||||
|
|
||||||
// Add leading "zeroes"
|
// Add leading "zeroes"
|
||||||
for (var i = 0; i < arr.length; i++) {
|
for (i = 0; i < arr.length; i++) {
|
||||||
if (arr[i] !== 0)
|
if (arr[i] !== 0)
|
||||||
break;
|
break;
|
||||||
res = '1' + res;
|
res = '1' + res;
|
||||||
@ -87,18 +96,20 @@ utils.toBase58 = function toBase58(arr) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.fromBase58 = function fromBase58(str) {
|
utils.fromBase58 = function fromBase58(str) {
|
||||||
|
var i, zeroes, q, w, res, c, z;
|
||||||
|
|
||||||
// Count leading "zeroes"
|
// Count leading "zeroes"
|
||||||
for (var i = 0; i < str.length; i++)
|
for (i = 0; i < str.length; i++)
|
||||||
if (str[i] !== '1')
|
if (str[i] !== '1')
|
||||||
break;
|
break;
|
||||||
var zeroes = i;
|
zeroes = i;
|
||||||
|
|
||||||
// Read 4-char words and add them to bignum
|
// Read 4-char words and add them to bignum
|
||||||
var q = 1;
|
q = 1;
|
||||||
var w = 0;
|
w = 0;
|
||||||
var res = new bn(0);
|
res = new bn(0);
|
||||||
for (var i = zeroes; i < str.length; i++) {
|
for (i = zeroes; i < str.length; i++) {
|
||||||
var c = base58.indexOf(str[i]);
|
c = base58.indexOf(str[i]);
|
||||||
if (!(c >= 0 && c < 58))
|
if (!(c >= 0 && c < 58))
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
@ -113,9 +124,10 @@ utils.fromBase58 = function fromBase58(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add leading "zeroes"
|
// Add leading "zeroes"
|
||||||
var z = [];
|
z = [];
|
||||||
for (var i = 0; i < zeroes; i++)
|
for (i = 0; i < zeroes; i++)
|
||||||
z.push(0);
|
z.push(0);
|
||||||
|
|
||||||
return z.concat(res.toArray());
|
return z.concat(res.toArray());
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,12 +164,15 @@ utils.readU16 = function readU16(arr, off) {
|
|||||||
utils.readU32 = function readU32(arr, off) {
|
utils.readU32 = function readU32(arr, off) {
|
||||||
if (!off)
|
if (!off)
|
||||||
off = 0;
|
off = 0;
|
||||||
var r = arr[off] |
|
|
||||||
(arr[off + 1] << 8) |
|
var r = arr[off]
|
||||||
(arr[off + 2] << 16) |
|
| (arr[off + 1] << 8)
|
||||||
(arr[off + 3] << 24);
|
| (arr[off + 2] << 16)
|
||||||
|
| (arr[off + 3] << 24);
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
r += 0x100000000;
|
r += 0x100000000;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -186,6 +201,8 @@ utils.writeU32 = function writeU32(dst, num, off) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.writeU64 = function writeU64(dst, num, off) {
|
utils.writeU64 = function writeU64(dst, num, off) {
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
if (!off)
|
if (!off)
|
||||||
off = 0;
|
off = 0;
|
||||||
|
|
||||||
@ -197,7 +214,8 @@ utils.writeU64 = function writeU64(dst, num, off) {
|
|||||||
dst[off++] = ch;
|
dst[off++] = ch;
|
||||||
});
|
});
|
||||||
|
|
||||||
var i = num.length;
|
i = num.length;
|
||||||
|
|
||||||
while (i--)
|
while (i--)
|
||||||
dst[off++] = num[i];
|
dst[off++] = num[i];
|
||||||
|
|
||||||
@ -223,6 +241,8 @@ utils.writeU32BE = function writeU32BE(dst, num, off) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.writeU64BE = function writeU64BE(dst, num, off) {
|
utils.writeU64BE = function writeU64BE(dst, num, off) {
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
if (!off)
|
if (!off)
|
||||||
off = 0;
|
off = 0;
|
||||||
|
|
||||||
@ -230,7 +250,7 @@ utils.writeU64BE = function writeU64BE(dst, num, off) {
|
|||||||
while (num.length < 8)
|
while (num.length < 8)
|
||||||
num.unshift(0);
|
num.unshift(0);
|
||||||
|
|
||||||
for (var i = 0; i < num.length; i++)
|
for (; i < num.length; i++)
|
||||||
dst[off++] = num[i];
|
dst[off++] = num[i];
|
||||||
|
|
||||||
return 8;
|
return 8;
|
||||||
@ -245,12 +265,15 @@ utils.readU16BE = function readU16BE(arr, off) {
|
|||||||
utils.readU32BE = function readU32BE(arr, off) {
|
utils.readU32BE = function readU32BE(arr, off) {
|
||||||
if (!off)
|
if (!off)
|
||||||
off = 0;
|
off = 0;
|
||||||
var r = (arr[off] << 24) |
|
|
||||||
(arr[off + 1] << 16) |
|
var r = (arr[off] << 24)
|
||||||
(arr[off + 2] << 8) |
|
| (arr[off + 1] << 16)
|
||||||
arr[off + 3];
|
| (arr[off + 2] << 8)
|
||||||
|
| arr[off + 3];
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
r += 0x100000000;
|
r += 0x100000000;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -261,19 +284,27 @@ utils.readU64BE = function readU64BE(arr, off) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.writeAscii = function writeAscii(dst, str, off) {
|
utils.writeAscii = function writeAscii(dst, str, off) {
|
||||||
for (var i = 0; i < str.length; i++) {
|
var i = 0;
|
||||||
var c = str.charCodeAt(i);
|
var c;
|
||||||
|
|
||||||
|
for (; i < str.length; i++) {
|
||||||
|
c = str.charCodeAt(i);
|
||||||
dst[off + i] = c & 0xff;
|
dst[off + i] = c & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.readAscii = function readAscii(arr, off, len) {
|
utils.readAscii = function readAscii(arr, off, len) {
|
||||||
var str = '';
|
var str = '';
|
||||||
for (var i = off; i < off + len; i++) {
|
var i = off;
|
||||||
var c = String.fromCharCode(arr[i] & 0xff);
|
var c;
|
||||||
|
|
||||||
|
for (i = off; i < off + len; i++) {
|
||||||
|
c = String.fromCharCode(arr[i] & 0xff);
|
||||||
str += c;
|
str += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -290,17 +321,24 @@ utils.array2ascii = function array2ascii(arr) {
|
|||||||
|
|
||||||
utils.copy = function copy(src, dst, off, force) {
|
utils.copy = function copy(src, dst, off, force) {
|
||||||
var len = src.length;
|
var len = src.length;
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
if (!force)
|
if (!force)
|
||||||
len = Math.min(dst.length - off, len);
|
len = Math.min(dst.length - off, len);
|
||||||
for (var i = 0; i < len; i++)
|
|
||||||
|
for (; i < len; i++)
|
||||||
dst[i + off] = src[i];
|
dst[i + off] = src[i];
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.stringify = function stringify(arr) {
|
utils.stringify = function stringify(arr) {
|
||||||
var res = '';
|
var res = '';
|
||||||
for (var i = 0; i < arr.length; i++)
|
var i = 0;
|
||||||
|
|
||||||
|
for (; i < arr.length; i++)
|
||||||
res += String.fromCharCode(arr[i]);
|
res += String.fromCharCode(arr[i]);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -312,23 +350,28 @@ function zero2(word) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toHex(msg) {
|
function toHex(msg) {
|
||||||
|
var res = '';
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
if (typeof msg === 'string')
|
if (typeof msg === 'string')
|
||||||
return msg;
|
return msg;
|
||||||
|
|
||||||
var res = '';
|
for (; i < msg.length; i++)
|
||||||
for (var i = 0; i < msg.length; i++)
|
|
||||||
res += zero2(msg[i].toString(16));
|
res += zero2(msg[i].toString(16));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.toHex = toHex;
|
utils.toHex = toHex;
|
||||||
|
|
||||||
function binaryInsert(list, item, compare, search) {
|
function binaryInsert(list, item, compare, search) {
|
||||||
var start = 0,
|
var start = 0;
|
||||||
end = list.length;
|
var end = list.length;
|
||||||
|
var pos, cmp;
|
||||||
|
|
||||||
while (start < end) {
|
while (start < end) {
|
||||||
var pos = (start + end) >> 1;
|
pos = (start + end) >> 1;
|
||||||
var cmp = compare(item, list[pos]);
|
cmp = compare(item, list[pos]);
|
||||||
|
|
||||||
if (cmp === 0) {
|
if (cmp === 0) {
|
||||||
start = pos;
|
start = pos;
|
||||||
@ -343,8 +386,10 @@ function binaryInsert(list, item, compare, search) {
|
|||||||
|
|
||||||
if (!search)
|
if (!search)
|
||||||
list.splice(start, 0, item);
|
list.splice(start, 0, item);
|
||||||
|
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.binaryInsert = binaryInsert;
|
utils.binaryInsert = binaryInsert;
|
||||||
|
|
||||||
function bitsToTarget(bits) {
|
function bitsToTarget(bits) {
|
||||||
@ -352,26 +397,31 @@ function bitsToTarget(bits) {
|
|||||||
var hi = (bits >>> 16) & 0xff;
|
var hi = (bits >>> 16) & 0xff;
|
||||||
var mid = (bits >>> 8) & 0xff;
|
var mid = (bits >>> 8) & 0xff;
|
||||||
var lo = bits & 0xff;
|
var lo = bits & 0xff;
|
||||||
|
|
||||||
var res = new Array(len);
|
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] = 0;
|
||||||
|
|
||||||
res[i++] = lo;
|
res[i++] = lo;
|
||||||
res[i++] = mid;
|
res[i++] = mid;
|
||||||
res[i++] = hi;
|
res[i++] = hi;
|
||||||
|
|
||||||
if (hi === 0)
|
if (hi === 0)
|
||||||
res.pop();
|
res.pop();
|
||||||
|
|
||||||
if (hi === 0 && mid === 0)
|
if (hi === 0 && mid === 0)
|
||||||
res.pop();
|
res.pop();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.bitsToTarget = bitsToTarget;
|
utils.bitsToTarget = bitsToTarget;
|
||||||
|
|
||||||
function testTarget(target, hash) {
|
function testTarget(target, hash) {
|
||||||
if (typeof target === 'number')
|
if (typeof target === 'number')
|
||||||
target = bitsToTarget(target);
|
target = bitsToTarget(target);
|
||||||
|
|
||||||
hash = utils.toArray(hash, 'hex');
|
hash = utils.toArray(hash, 'hex');
|
||||||
|
|
||||||
for (var i = hash.length - 1; i >= target.length; i--)
|
for (var i = hash.length - 1; i >= target.length; i--)
|
||||||
@ -388,13 +438,16 @@ function testTarget(target, hash) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.testTarget = testTarget;
|
utils.testTarget = testTarget;
|
||||||
|
|
||||||
utils.isEqual = function isEqual(a, b) {
|
utils.isEqual = function isEqual(a, b) {
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
if (a.length !== b.length)
|
if (a.length !== b.length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (var i = 0; i < a.length; i++)
|
for (; i < a.length; i++)
|
||||||
if (a[i] !== b[i])
|
if (a[i] !== b[i])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -433,12 +486,14 @@ RequestCache.prototype.add = function add(id, cb) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
RequestCache.prototype.fullfill = function fullfill(id, err, data) {
|
RequestCache.prototype.fullfill = function fullfill(id, err, data) {
|
||||||
|
var cbs = this.map[id];
|
||||||
|
|
||||||
if (!this.map[id])
|
if (!this.map[id])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var cbs = this.map[id];
|
|
||||||
delete this.map[id];
|
delete this.map[id];
|
||||||
this.count--;
|
this.count--;
|
||||||
|
|
||||||
cbs.forEach(function(cb) {
|
cbs.forEach(function(cb) {
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
});
|
});
|
||||||
@ -456,8 +511,11 @@ utils.asyncify = function asyncify(fn) {
|
|||||||
|
|
||||||
utils.revHex = function revHex(s) {
|
utils.revHex = function revHex(s) {
|
||||||
var r = '';
|
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;
|
r = s.slice(i, i + 2) + r;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -472,6 +530,9 @@ utils.assert.equal = function assertEqual(l, r, msg) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.toBTC = function toBTC(satoshi, strict) {
|
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)) {
|
if (typeof satoshi === 'string' && /^\d+(?:\.\d+)?$/.test(satoshi)) {
|
||||||
satoshi = new bn(satoshi, 10);
|
satoshi = new bn(satoshi, 10);
|
||||||
} else if (typeof satoshi === 'number') {
|
} else if (typeof satoshi === 'number') {
|
||||||
@ -483,8 +544,6 @@ utils.toBTC = function toBTC(satoshi, strict) {
|
|||||||
if (!(satoshi instanceof bn))
|
if (!(satoshi instanceof bn))
|
||||||
throw new Error('could not calculate btc');
|
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) {
|
if (lo.cmpn(0) !== 0) {
|
||||||
lo = lo.toString(10);
|
lo = lo.toString(10);
|
||||||
while (lo.length < 8)
|
while (lo.length < 8)
|
||||||
@ -498,10 +557,13 @@ utils.toBTC = function toBTC(satoshi, strict) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
utils.fromBTC = function(btc, strict) {
|
utils.fromBTC = function(btc, strict) {
|
||||||
|
var m = new bn(10000000).mul(new bn(10));
|
||||||
|
|
||||||
var satoshi;
|
var satoshi;
|
||||||
|
var parts;
|
||||||
|
|
||||||
if (typeof btc === 'string' && /^\d+(?:\.\d+)?$/.test(btc)) {
|
if (typeof btc === 'string' && /^\d+(?:\.\d+)?$/.test(btc)) {
|
||||||
var parts = btc.split('.');
|
parts = btc.split('.');
|
||||||
parts[0] = parts[0] || '0';
|
parts[0] = parts[0] || '0';
|
||||||
parts[1] = parts[1] || '0';
|
parts[1] = parts[1] || '0';
|
||||||
while (parts[1].length < 8)
|
while (parts[1].length < 8)
|
||||||
@ -517,8 +579,8 @@ utils.fromBTC = function(btc, strict) {
|
|||||||
if (!(satoshi instanceof bn))
|
if (!(satoshi instanceof bn))
|
||||||
throw new Error('could not calculate satoshis');
|
throw new Error('could not calculate satoshis');
|
||||||
|
|
||||||
var m = new bn(10000000).mul(new bn(10));
|
|
||||||
satoshi.imuln(m);
|
satoshi.imuln(m);
|
||||||
|
|
||||||
return satoshi;
|
return satoshi;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -82,18 +82,19 @@ function Wallet(options, passphrase) {
|
|||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(Wallet, EventEmitter);
|
inherits(Wallet, EventEmitter);
|
||||||
module.exports = Wallet;
|
|
||||||
|
|
||||||
Wallet.prototype._init = function init() {
|
Wallet.prototype._init = function init() {
|
||||||
|
var self = this;
|
||||||
|
var prevBalance = null;
|
||||||
|
|
||||||
if (this.tx._loaded) {
|
if (this.tx._loaded) {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify owners about new accepted transactions
|
// Notify owners about new accepted transactions
|
||||||
var self = this;
|
|
||||||
var prevBalance = null;
|
|
||||||
this.tx.on('update', function(lastTs, tx) {
|
this.tx.on('update', function(lastTs, tx) {
|
||||||
var b = this.balance();
|
var b = this.balance();
|
||||||
if (prevBalance && prevBalance.cmp(b) !== 0)
|
if (prevBalance && prevBalance.cmp(b) !== 0)
|
||||||
@ -203,35 +204,43 @@ Wallet.prototype.derive = function derive() {
|
|||||||
|
|
||||||
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
|
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
|
||||||
var priv = this.key.getPrivate();
|
var priv = this.key.getPrivate();
|
||||||
|
var arr, chk;
|
||||||
|
|
||||||
if (priv)
|
if (priv)
|
||||||
priv = priv.toArray();
|
priv = priv.toArray();
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!enc)
|
if (!enc)
|
||||||
return priv;
|
return priv;
|
||||||
|
|
||||||
if (enc === 'base58') {
|
if (enc === 'base58') {
|
||||||
// We'll be using ncompressed public key as an address
|
// We'll be using ncompressed public key as an address
|
||||||
var arr = [ network.prefixes.privkey ];
|
arr = [ network.prefixes.privkey ];
|
||||||
|
|
||||||
// 0-pad key
|
// 0-pad key
|
||||||
while (arr.length + priv.length < 33)
|
while (arr.length + priv.length < 33)
|
||||||
arr.push(0);
|
arr.push(0);
|
||||||
|
|
||||||
arr = arr.concat(priv);
|
arr = arr.concat(priv);
|
||||||
|
|
||||||
if (this.compressed)
|
if (this.compressed)
|
||||||
arr.push(1);
|
arr.push(1);
|
||||||
var chk = utils.checksum(arr);
|
|
||||||
|
chk = utils.checksum(arr);
|
||||||
|
|
||||||
return utils.toBase58(arr.concat(chk));
|
return utils.toBase58(arr.concat(chk));
|
||||||
} else {
|
|
||||||
return priv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getFullPublicKey = function getFullPublicKey(enc) {
|
Wallet.prototype.getFullPublicKey = function getFullPublicKey(enc) {
|
||||||
var pub = this.getOwnPublicKey();
|
var pub = this.getOwnPublicKey();
|
||||||
|
var keys;
|
||||||
|
|
||||||
if (this.type === 'scripthash') {
|
if (this.type === 'scripthash') {
|
||||||
var keys = this.getPublicKeys();
|
keys = this.getPublicKeys();
|
||||||
pub = bcoin.script.encode(bcoin.script.multisig(keys, this.m, this.n));
|
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) {
|
Wallet.hash2addr = function hash2addr(hash, prefix) {
|
||||||
|
var addr;
|
||||||
|
|
||||||
hash = utils.toArray(hash, 'hex');
|
hash = utils.toArray(hash, 'hex');
|
||||||
|
|
||||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||||
hash = [ prefix ].concat(hash);
|
hash = [ prefix ].concat(hash);
|
||||||
|
|
||||||
var addr = hash.concat(utils.checksum(hash));
|
addr = hash.concat(utils.checksum(hash));
|
||||||
return utils.toBase58(addr);
|
return utils.toBase58(addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -317,6 +328,8 @@ Wallet.__defineGetter__('prefixes', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Wallet.addr2hash = function addr2hash(addr, prefix) {
|
Wallet.addr2hash = function addr2hash(addr, prefix) {
|
||||||
|
var chk;
|
||||||
|
|
||||||
if (prefix == null && typeof addr === 'string')
|
if (prefix == null && typeof addr === 'string')
|
||||||
prefix = Wallet.prefixes[addr[0]];
|
prefix = Wallet.prefixes[addr[0]];
|
||||||
|
|
||||||
@ -330,7 +343,7 @@ Wallet.addr2hash = function addr2hash(addr, prefix) {
|
|||||||
if (addr[0] !== prefix)
|
if (addr[0] !== prefix)
|
||||||
return [];
|
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))
|
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
@ -352,11 +365,11 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
|
|||||||
var keys = this.getPublicKeys();
|
var keys = this.getPublicKeys();
|
||||||
|
|
||||||
var outputs = tx.outputs.filter(function(output, i) {
|
var outputs = tx.outputs.filter(function(output, i) {
|
||||||
|
var s = output.script;
|
||||||
|
|
||||||
if (index !== undefined && index !== i)
|
if (index !== undefined && index !== i)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var s = output.script;
|
|
||||||
|
|
||||||
if (bcoin.script.isPubkey(s, key))
|
if (bcoin.script.isPubkey(s, key))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -386,6 +399,8 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
|
|||||||
var keys = this.getPublicKeys();
|
var keys = this.getPublicKeys();
|
||||||
|
|
||||||
var inputs = tx.inputs.filter(function(input, i) {
|
var inputs = tx.inputs.filter(function(input, i) {
|
||||||
|
var s;
|
||||||
|
|
||||||
if (index !== undefined && index !== i)
|
if (index !== undefined && index !== i)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -404,7 +419,7 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
|
|||||||
if (!input.out.tx)
|
if (!input.out.tx)
|
||||||
return false;
|
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))
|
if (bcoin.script.isPubkey(s, key))
|
||||||
return true;
|
return true;
|
||||||
@ -468,11 +483,11 @@ Wallet.prototype.scriptInputs = function scriptInputs(tx, inputs) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.signInputs = function(tx, type, inputs) {
|
Wallet.prototype.signInputs = function(tx, type, inputs) {
|
||||||
|
var key = this.key;
|
||||||
|
|
||||||
if (!type)
|
if (!type)
|
||||||
type = 'all';
|
type = 'all';
|
||||||
|
|
||||||
var key = this.key;
|
|
||||||
|
|
||||||
inputs = inputs || tx.inputs;
|
inputs = inputs || tx.inputs;
|
||||||
|
|
||||||
inputs = inputs.filter(function(input) {
|
inputs = inputs.filter(function(input) {
|
||||||
@ -531,16 +546,22 @@ Wallet.prototype.balance = function balance() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.fill = function fill(tx, cb) {
|
Wallet.prototype.fill = function fill(tx, cb) {
|
||||||
cb = utils.asyncify(cb);
|
|
||||||
var result = tx.fillUnspent(this.unspent(), this.getAddress());
|
var result = tx.fillUnspent(this.unspent(), this.getAddress());
|
||||||
|
var err;
|
||||||
|
|
||||||
|
cb = utils.asyncify(cb);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
var err = new Error('Not enough funds');
|
err = new Error('Not enough funds');
|
||||||
err.minBalance = tx.total;
|
err.minBalance = tx.total;
|
||||||
cb(err);
|
cb(err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sign(tx);
|
this.sign(tx);
|
||||||
|
|
||||||
cb(null, tx);
|
cb(null, tx);
|
||||||
|
|
||||||
return tx;
|
return tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -590,12 +611,10 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
if (decrypt)
|
if (decrypt)
|
||||||
json.priv = decrypt(json.priv);
|
json.priv = decrypt(json.priv);
|
||||||
|
|
||||||
var priv;
|
var priv, pub, compressed, key, w;
|
||||||
var pub;
|
|
||||||
var compressed;
|
|
||||||
|
|
||||||
if (json.priv) {
|
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(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
|
||||||
assert.equal(key[0], network.prefixes.privkey);
|
assert.equal(key[0], network.prefixes.privkey);
|
||||||
|
|
||||||
@ -621,7 +640,7 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
priv = new hd.priv(json.hd);
|
priv = new hd.priv(json.hd);
|
||||||
}
|
}
|
||||||
|
|
||||||
var w = new Wallet({
|
w = new Wallet({
|
||||||
label: json.label,
|
label: json.label,
|
||||||
priv: priv,
|
priv: priv,
|
||||||
pub: pub,
|
pub: pub,
|
||||||
@ -633,3 +652,5 @@ Wallet.fromJSON = function fromJSON(json, decrypt) {
|
|||||||
|
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Wallet;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user