diff --git a/lib/bcoin.js b/lib/bcoin.js index 3d5fe6e7..9c5a8c94 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -5,6 +5,7 @@ bcoin.ecdsa = elliptic.ecdsa(elliptic.nist.secp256k1); bcoin.utils = require('./bcoin/utils'); bcoin.bloom = require('./bcoin/bloom'); bcoin.protocol = require('./bcoin/protocol'); +bcoin.script = require('./bcoin/script'); bcoin.tx = require('./bcoin/tx'); bcoin.block = require('./bcoin/block'); bcoin.chain = require('./bcoin/chain'); diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 08c08091..e7f870fa 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -12,6 +12,11 @@ function Block(data) { this.ts = data.ts; this.bits = data.bits; this.nonce = data.nonce; + this.totalTx = data.totalTx; + this.hashes = data.hashes.map(function(hash) { + return utils.toHex(hash); + }); + this.flags = data.flags; this._hash = null; } diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index c004b59a..38018abc 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -12,7 +12,7 @@ function Pool(options) { EventEmitter.call(this); this.options = options || {}; - this.size = options.size || 2; + this.size = options.size || 3; this.parallel = options.parallel || 2000; this.load = { timeout: options.loadTimeout || 10000, @@ -23,7 +23,7 @@ function Pool(options) { hiReached: false }; this.maxRetries = options.maxRetries || 300; - this.requestTimeout = options.requestTimeout || 30000; + this.requestTimeout = options.requestTimeout || 10000; this.chain = new bcoin.chain(); this.bloom = new bcoin.bloom(8 * 10 * 1024, 10, diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index 2ff2e874..54640e0e 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -1,6 +1,7 @@ var assert = require('assert'); var util = require('util'); var EventEmitter = require('events').EventEmitter; +var bn = require('bn.js'); var bcoin = require('../../bcoin'); var utils = bcoin.utils; @@ -54,7 +55,8 @@ Parser.prototype.parse = function parse(chunk) { return this.emit('error', new Error('Invalid checksum')); this.packet.payload = this.parsePayload(this.packet.cmd, this.packet.payload); - this.emit('packet', this.packet); + if (this.packet.payload) + this.emit('packet', this.packet); this.waiting = 24; this.packet = null; @@ -92,6 +94,8 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) { return this.parseMerkleBlock(p); else if (cmd === 'block') return this.parseBlock(p); + else if (cmd === 'tx') + return this.parseTx(p); else return p; }; @@ -144,7 +148,7 @@ function readIntv(p, off) { bytes = 9; } - return { off: bytes, r: r }; + return { off: off + bytes, r: r }; } Parser.prototype.parseInvList = function parseInvList(p) { @@ -165,9 +169,29 @@ Parser.prototype.parseInvList = function parseInvList(p) { }; Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) { - if (p.length < 84) + if (p.length < 86) return this.emit('error', new Error('Invalid merkleblock size')); + var hashCount = readIntv(p, 84); + var off = hashCount.off; + hashCount = hashCount.r; + if (off + 32 * hashCount + 1 > p.length) + return this.emit('error', new Error('Invalid hash count')); + + var hashes = new Array(hashCount); + for (var i = 0; i < hashCount; i++) + hashes[i] = p.slice(off + i * 32, off + (i + 1) * 32); + + off = off + 32 * hashCount; + var flagCount = readIntv(p, off); + off = flagCount.off; + flagCount = flagCount.r; + + if (off + flagCount > p.length) + return this.emit('error', new Error('Invalid flag count')); + + var flags = p.slice(off, off + flagCount); + return { version: readU32(p, 0), prevBlock: p.slice(4, 36), @@ -175,10 +199,9 @@ Parser.prototype.parseMerkleBlock = function parseMerkleBlock(p) { ts: readU32(p, 68), bits: readU32(p, 72), nonce: readU32(p, 76), - totalTx: readU32(p, 80) - - // hashes: - // flags: + totalTx: readU32(p, 80), + hashes: hashes, + flags: flags }; }; @@ -194,8 +217,96 @@ Parser.prototype.parseBlock = function parseBlock(p) { bits: readU32(p, 72), nonce: readU32(p, 76), totalTx: readU32(p, 80) - - // hashes: - // flags: + }; +}; + +Parser.prototype.parseTxIn = function parseTxIn(p) { + if (p.length < 41) + return this.emit('error', new Error('Invalid tx_in size')); + + var scriptLen = readIntv(p, 36); + var off = scriptLen.off; + scriptLen = scriptLen.r; + if (off + scriptLen + 4 > p.length) + return this.emit('error', new Error('Invalid tx_in script length')); + + return { + size: off + scriptLen + 4, + out: { + hash: p.slice(0, 32), + index: readU32(p, 32) + }, + script: p.slice(off, off + scriptLen), + seq: readU32(p, off + scriptLen) + }; +}; + +Parser.prototype.parseTxOut = function parseTxOut(p) { + if (p.length < 9) + return this.emit('error', new Error('Invalid tx_out size')); + + var scriptLen = readIntv(p, 8); + var off = scriptLen.off; + scriptLen = scriptLen.r; + if (off + scriptLen > p.length) + return this.emit('error', new Error('Invalid tx_out script length')); + + return { + size: off + scriptLen, + value: new bn(p.slice(0, 8).reverse()), + script: p.slice(off, off + scriptLen) + }; +}; + +Parser.prototype.parseTx = function parseTx(p) { + if (p.length < 60) + return this.emit('error', new Error('Invalid tx size')); + + var inCount = readIntv(p, 4); + var off = inCount.off; + inCount = inCount.r; + if (inCount <= 0) + return this.emit('error', new Error('Invalid tx_in count')); + if (off + 41 * inCount + 14 > p.length) + return this.emit('error', new Error('Invalid tx_in count')); + + var txIn = new Array(inCount); + for (var i = 0; i < inCount; i++) { + var tx = this.parseTxIn(p.slice(off)); + if (!tx) + return; + txIn[i] = tx; + off += tx.size; + + if (off + 14 > p.length) + return this.emit('error', new Error('Invalid tx_in offset')); + } + + var outCount = readIntv(p, off); + var off = outCount.off; + outCount = outCount.r; + if (outCount <= 0) + return this.emit('error', new Error('Invalid tx_out count')); + if (off + 9 * outCount + 4 > p.length) + return this.emit('error', new Error('Invalid tx_out count')); + + var txOut = new Array(outCount); + for (var i = 0; i < outCount; i++) { + var tx = this.parseTxOut(p.slice(off)); + if (!tx) + return; + txOut[i] = tx; + off += tx.size; + + if (off + 4 > p.length) + return this.emit('error', new Error('Invalid tx_out offset')); + } + + return { + _raw: p, + version: readU32(p, 0), + inputs: txIn, + outputs: txOut, + lock: readU32(p, off) }; }; diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js new file mode 100644 index 00000000..2e4a4e63 --- /dev/null +++ b/lib/bcoin/script.js @@ -0,0 +1,5 @@ +var script = exports; + +script.parse = function parse(s) { + return s; +}; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index b81ecbcc..4e20c148 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -6,8 +6,26 @@ function TX(data) { return new TX(data); this.type = 'tx'; + this.version = data.version; + this.inputs = data.inputs.map(function(input) { + return { + out: { + hash: utils.toHex(input.out.hash), + index: bcoin.script.parse(input.out.index) + }, + script: input.script, + seq: input.seq + }; + }); + this.outputs = data.outputs.map(function(output) { + return { + value: output.value, + script: bcoin.script.parse(output.script) + }; + }); + this._hash = null; - this._raw = data || null; + this._raw = data._raw || null; } module.exports = TX;