diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index d3a0fb95..7f093ff8 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -189,6 +189,11 @@ Chain.prototype.open = function open(callback) { this.once('open', callback); }; +Chain.prototype.close = +Chain.prototype.destroy = function destroy(callback) { + this.db.close(utils.ensure(callback)); +}; + Chain.prototype._lock = function _lock(func, args, force) { return this.locker.lock(func, args, force); }; diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index f68765f5..41bef0d1 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -174,7 +174,8 @@ ChainDB.prototype.open = function open(callback) { this.once('open', callback); }; -ChainDB.prototype.close = function close(callback) { +ChainDB.prototype.close = +ChainDB.prototype.destroy = function destroy(callback) { callback = utils.ensure(callback); this.db.close(callback); }; diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index 9ad12c96..24cfe658 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -193,6 +193,17 @@ Fullnode.prototype.open = function open(callback) { this.once('open', callback); }; +Fullnode.prototype.close = +Fullnode.prototype.destroy = function destroy(callback) { + utils.parallel([ + this.pool.close.bind(this.pool), + this.http.close.bind(this.http), + this.mempool.close.bind(this.mempool), + this.walletdb.close.bind(this.walletdb), + this.chain.close.bind(this.chain) + ], callback); +}; + Fullnode.prototype.createWallet = function createWallet(options, callback) { var self = this; callback = utils.ensure(callback); diff --git a/lib/bcoin/http/http.js b/lib/bcoin/http/http.js index 44227ad1..31cb93cd 100644 --- a/lib/bcoin/http/http.js +++ b/lib/bcoin/http/http.js @@ -245,6 +245,10 @@ HTTPServer.prototype.listen = function listen(port, host, callback) { }); }; +HTTPServer.prototype.close = function close(callback) { + this.server.close(callback); +}; + /** * Helpers */ diff --git a/lib/bcoin/http/server.js b/lib/bcoin/http/server.js index fb5a18f9..82f00f21 100644 --- a/lib/bcoin/http/server.js +++ b/lib/bcoin/http/server.js @@ -421,6 +421,11 @@ NodeServer.prototype.open = function open(callback) { this.once('open', callback); }; +NodeServer.prototype.close = +NodeServer.prototype.destroy = function destroy(callback) { + this.server.close(callback); +}; + NodeServer.prototype._initIO = function _initIO() { var self = this; diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 5ca8c14e..7bb041cf 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -127,6 +127,11 @@ Mempool.prototype.open = function open(callback) { return this.once('open', callback); }; +Mempool.prototype.close = +Mempool.prototype.destroy = function destroy(callback) { + this.db.close(utils.ensure(callback)); +}; + Mempool.prototype.addBlock = function addBlock(block, callback, force) { var self = this; var unlock = this._lock(addBlock, [block, callback], force); diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 962d3481..396000fb 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -649,9 +649,6 @@ MTX.prototype.addOutput = function addOutput(obj, value) { this.outputs.push(output); - if (options.script) - output.script = options.script.clone(); - this.scriptOutput(this.outputs.length - 1, options); return this; @@ -670,7 +667,7 @@ MTX.prototype.scriptOutput = function scriptOutput(index, options) { assert(output); if (options.script) - output.script = options.script; + output.script = options.script.clone(); else output.script = Script.createOutputScript(options); }; @@ -955,7 +952,7 @@ MTX.prototype.fill = function fill(coins, options) { } assert(coins); - assert(options.changeAddress); + assert(options.changeAddress, '`changeAddress` is required.'); result = this.selectCoins(coins, options); diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index f2348b73..d066d83c 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -295,17 +295,19 @@ Pool.prototype.getHeaders = function getHeaders(peer, top, stop, callback) { }); }; -Pool.prototype.startServer = function startServer() { +Pool.prototype.startServer = function startServer(callback) { var self = this; var net; + callback = utils.ensure(callback); + if (bcoin.isBrowser) - return; + return utils.nextTick(callback); net = require('n' + 'et'); if (!this.options.listen) - return; + return utils.nextTick(callback); assert(!this.server); @@ -322,17 +324,19 @@ Pool.prototype.startServer = function startServer() { data.address, data.port); }); - this.server.listen(network.port, '0.0.0.0'); + this.server.listen(network.port, '0.0.0.0', callback); }; -Pool.prototype.stopServer = function stopServer() { +Pool.prototype.stopServer = function stopServer(callback) { + callback = utils.ensure(callback); + if (bcoin.isBrowser) - return; + return utils.nextTick(callback); if (!this.server) - return; + return utils.nextTick(callback); - this.server.close(); + this.server.close(callback); delete this.server; }; @@ -1706,29 +1710,42 @@ Pool.prototype.broadcast = function broadcast(msg, callback) { return e; }; -Pool.prototype.destroy = function destroy() { +Pool.prototype.close = +Pool.prototype.destroy = function destroy(callback) { + callback = utils.ensure(callback); + if (this.destroyed) - return; + return utils.nextTick(callback); this.destroyed = true; - if (this.peers.load) - this.peers.load.destroy(); + this.stopSync(); this.inv.list.forEach(function(entry) { clearTimeout(entry.timer); entry.timer = null; }); - this.peers.pending.slice().forEach(function(peer) { - peer.destroy(); - }); + Object.keys(this.request.map).forEach(function(hash) { + this.request.map[hash].finish(); + }, this); + + if (this.peers.load) + this.peers.load.destroy(); this.peers.regular.slice().forEach(function(peer) { peer.destroy(); }); - this.stopServer(); + this.peers.pending.slice().forEach(function(peer) { + peer.destroy(); + }); + + this.peers.leeches.slice().forEach(function(peer) { + peer.destroy(); + }); + + this.stopServer(callback); }; Pool.prototype.getPeer = function getPeer(addr) { diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index 2869ca6b..176c500d 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -182,6 +182,8 @@ exports.opcodes = { OP_INVALIDOPCODE: 0xff }; +Object.freeze(exports.opcodes); + exports.opcodesByVal = new Array(256); Object.keys(exports.opcodes).forEach(function(name) { var val = exports.opcodes[name]; diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 6250a5dd..95edb9e9 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -11,6 +11,9 @@ var utils = require('./utils'); var assert = utils.assert; var BufferWriter = require('./writer'); var opcodes = constants.opcodes; +var STACK_TRUE = new Buffer([1]); +var STACK_FALSE = new Buffer([]); +var STACK_NEGATE = new Buffer([0xff]); function Witness(items) { if (!(this instanceof Witness)) @@ -103,11 +106,11 @@ Witness.fromString = function fromString(items) { op = items[i]; if (op === '-1' || op === '1negate') { - op = new Buffer([0xff]); + op = STACK_NEGATE; } else if (op === '0' || op === 'false') { - op = new Buffer([]); + op = STACK_FALSE; } else if (op === 'true') { - op = new Buffer([1]); + op = STACK_TRUE; } else if (+op >= 1 && +op <= 16) { op = new Buffer([+op]); } else { @@ -148,11 +151,11 @@ Witness.fromSymbolic = function fromSymbolic(items) { op = op.slice(3); if (+op === -1) - op = new Buffer([0xff]); + op = STACK_NEGATE; else if (+op === 0 || op === 'false') - op = new Buffer([]); + op = STACK_FALSE; else if (+op === 1 || op === 'true') - op = new Buffer([1]); + op = STACK_TRUE; else if (+op >= 1 && +op <= 16) op = new Buffer([+op]); else @@ -573,7 +576,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version } if (op === opcodes.OP_0) { - stack.push(new Buffer([])); + stack.push(STACK_FALSE); continue; } @@ -597,7 +600,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version break; } case opcodes.OP_1NEGATE: { - stack.push(new Buffer([0xff])); + stack.push(STACK_NEGATE); break; } case opcodes.OP_IF: @@ -752,7 +755,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version break; case opcodes.OP_ABS: if (n.cmpn(0) < 0) - n = n.neg(); + n.ineg(); break; case opcodes.OP_NOT: n = n.cmpn(0) === 0; @@ -860,7 +863,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version n2 = Script.num(stack.pop(), flags); n1 = Script.num(stack.pop(), flags); val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0; - stack.push(val.cmpn(0) !== 0 ? new Buffer([1]) : new Buffer([])); + stack.push(val.cmpn(0) !== 0 ? STACK_TRUE : STACK_FALSE); break; } @@ -909,7 +912,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version if (!res) throw new ScriptError('Equal verification failed.', op, ip); } else { - stack.push(res ? new Buffer([1]) : new Buffer([])); + stack.push(res ? STACK_TRUE : STACK_FALSE); } break; } @@ -942,7 +945,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version if (!res) throw new ScriptError('Signature verification failed.', op, ip); } else { - stack.push(res ? new Buffer([1]) : new Buffer([])); + stack.push(res ? STACK_TRUE : STACK_FALSE); } break; @@ -1023,7 +1026,7 @@ Script.prototype.interpret = function interpret(stack, flags, tx, index, version if (!res) throw new ScriptError('Signature verification failed.', op, ip); } else { - stack.push(res ? new Buffer([1]) : new Buffer([])); + stack.push(res ? STACK_TRUE : STACK_FALSE); } break; @@ -1236,7 +1239,7 @@ Script.array = function(value) { } if (value.cmpn(0) === 0) - return new Buffer([]); + return STACK_FALSE; return value.toBuffer('le'); }; @@ -1270,7 +1273,7 @@ Script.checkPush = function checkPush(value, flags) { return false; if (value.length <= 75) - return pushdata.opcode == null && pushdata.len === value.length; + return pushdata.opcode == null && pushdata.size === value.length; if (value.length <= 255) return pushdata.opcode === opcodes.OP_PUSHDATA1; @@ -1859,7 +1862,7 @@ Script.isMultisigInput = function isMultisigInput(code, keys, isWitness) { if (!Script.isDummy(code[0])) return false; } else { - if (code[0] !== 0) + if (code[0] !== opcodes.OP_0) return false; } @@ -1991,11 +1994,11 @@ Script.isDummy = function isDummy(data) { return data.length === 0; }; -Script.isZero = function isZero(data) { - if (data === 0) +Script.isZero = function isZero(op) { + if (op === opcodes.OP_0) return true; - return Script.isDummy(data); + return Script.isDummy(op); }; Script.isData = function isData(data) { @@ -2234,13 +2237,16 @@ Script.format = function format(code) { if (Buffer.isBuffer(chunk)) return '[' + utils.toHex(chunk) + ']'; - if (typeof chunk === 'number') { - if (constants.opcodeByVal[chunk]) - return constants.opcodesByVal[chunk].slice(3).toLowerCase(); - return chunk; - } + assert(typeof chunk === 'number'); - return chunk; + if (constants.opcodeByVal[chunk]) + return constants.opcodesByVal[chunk]; + + chunk = chunk.toString(16); + if (chunk.length < 2) + chunk = '0' + chunk; + + return 'UNKNOWN(' + chunk + ')'; }).join(' '); }; @@ -2298,9 +2304,9 @@ Script.prototype.getArgs = function getArgs() { return 2; if (this.isMultisig()) { - keys = this.code.slice(1, -2); + keys = this.code.length - 3; m = Script.getSmall(this.code[0]); - if (keys.length < 1 || m < 1) + if (keys < 1 || m < 1) return -1; return m + 1; } @@ -2367,6 +2373,13 @@ Script.fromString = function fromString(code) { return new Script(code); }; +Script.prototype.getSmall = function getSmall(i) { + if (i < 0) + i = this.code.length + i; + + return Script.getSmall(this.code[i]); +}; + Script.getSmall = function getSmall(op) { if (typeof op !== 'number') return null; @@ -2595,17 +2608,17 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) { }; Script.concat = function concat(scripts) { - var s = []; + var code = []; var i; - s = s.concat(scripts[0].code); + code = code.concat(scripts[0].code); for (i = 1; i < scripts.length; i++) { - s.push(opcodes.OP_CODESEPARATOR); - s = s.concat(scripts[i].code); + code.push(opcodes.OP_CODESEPARATOR); + code = code.concat(scripts[i].code); } - return s; + return code; }; Script.checksig = function checksig(msg, sig, key, flags) { @@ -2646,7 +2659,7 @@ Script.sign = function sign(msg, key, type) { Script.decode = function decode(buf) { var code = []; var off = 0; - var op, len; + var op, size; assert(Buffer.isBuffer(buf)); @@ -2667,7 +2680,7 @@ Script.decode = function decode(buf) { if (off > buf.length) { utils.hidden(code[code.length - 1], 'pushdata', { opcode: null, - len: op + size: op }); } continue; @@ -2679,36 +2692,36 @@ Script.decode = function decode(buf) { } if (op === opcodes.OP_PUSHDATA1) { - len = buf[off]; + size = buf[off]; off += 1; - code.push(buf.slice(off, off + len)); - off += len; - if (len <= 0x4b || off > buf.length) { + code.push(buf.slice(off, off + size)); + off += size; + if (size <= 0x4b || off > buf.length) { utils.hidden(code[code.length - 1], 'pushdata', { opcode: op, - len: len + size: size }); } } else if (op === opcodes.OP_PUSHDATA2) { - len = utils.readU16(buf, off); + size = utils.readU16(buf, off); off += 2; - code.push(buf.slice(off, off + len)); - off += len; - if (len <= 0xff || off > buf.length) { + code.push(buf.slice(off, off + size)); + off += size; + if (size <= 0xff || off > buf.length) { utils.hidden(code[code.length - 1], 'pushdata', { opcode: op, - len: len + size: size }); } } else if (op === opcodes.OP_PUSHDATA4) { - len = utils.readU32(buf, off); + size = utils.readU32(buf, off); off += 4; - code.push(buf.slice(off, off + len)); - off += len; - if (len <= 0xffff || off > buf.length) { + code.push(buf.slice(off, off + size)); + off += size; + if (size <= 0xffff || off > buf.length) { utils.hidden(code[code.length - 1], 'pushdata', { opcode: op, - len: len + size: size }); } } else { @@ -2735,19 +2748,19 @@ Script.encode = function encode(code) { // may have been decoded from before. if (op.pushdata) { if (op.pushdata.opcode === null) { - p.writeU8(op.pushdata.len); + p.writeU8(op.pushdata.size); p.writeBytes(op); } else if (op.pushdata.opcode === opcodes.OP_PUSHDATA1) { p.writeU8(opcodes.OP_PUSHDATA1); - p.writeU8(op.pushdata.len); + p.writeU8(op.pushdata.size); p.writeBytes(op); } else if (op.pushdata.opcode === opcodes.OP_PUSHDATA2) { p.writeU8(opcodes.OP_PUSHDATA2); - p.writeU16(op.pushdata.len); + p.writeU16(op.pushdata.size); p.writeBytes(op); } else if (op.pushdata.opcode === opcodes.OP_PUSHDATA4) { p.writeU8(opcodes.OP_PUSHDATA4); - p.writeU32(op.pushdata.len); + p.writeU32(op.pushdata.size); p.writeBytes(op); } continue; @@ -2756,15 +2769,6 @@ Script.encode = function encode(code) { if (op.length === 0) { p.writeU8(opcodes.OP_0); } else if (op.length <= 0x4b) { - if (op.length === 1) { - if (op[0] === 0xff) { - p.writeU8(opcodes.OP_1NEGATE); - continue; - } else if (op[0] >= 0 && op[0] <= 16) { - p.writeU8(op[0] === 0 ? 0 : op[0] + 0x50); - continue; - } - } p.writeU8(op.length); p.writeBytes(op); } else if (op.length <= 0xff) { diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index b72d0c09..0884a082 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -176,6 +176,12 @@ WalletDB.prototype.open = function open(callback) { this.once('open', callback); }; +WalletDB.prototype.close = +WalletDB.prototype.destroy = function destroy(callback) { + callback = utils.ensure(callback); + this.db.close(callback); +}; + WalletDB.prototype.syncOutputDepth = function syncOutputDepth(id, tx, callback) { var self = this; diff --git a/test/node-test.js b/test/node-test.js index 3818a81d..597dc62f 100644 --- a/test/node-test.js +++ b/test/node-test.js @@ -143,4 +143,8 @@ describe('Wallet', function() { }); }); }); + + it('should destroy pool', function(cb) { + node.close(cb); + }); });