From 314c14f422bd2c5023abf17f7e1e70a93415fd69 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 9 Jan 2016 13:19:26 -0800 Subject: [PATCH] input/output consistency. misc. --- lib/bcoin/chain.js | 10 +++- lib/bcoin/hd.js | 4 +- lib/bcoin/input.js | 125 +++++++++++++++++++++++++++++--------------- lib/bcoin/output.js | 116 ++++++++++++++++++++++++++-------------- lib/bcoin/pool.js | 21 ++++++-- lib/bcoin/tx.js | 45 +++++++++------- lib/bcoin/utils.js | 4 +- lib/bcoin/wallet.js | 11 ++-- 8 files changed, 217 insertions(+), 119 deletions(-) diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 109e1229..36b9977f 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -174,7 +174,12 @@ Chain.prototype._addIndex = function _addIndex(entry, save) { // could be used if you want to be on the overly // safe (see: paranoid) side. // this.resetLastCheckpoint(entry.height); - this.emit('fork', entry.height, entry.hash, checkpoint); + this.emit('fork', { + height: entry.height, + expected: checkpoint, + received: entry.hash, + checkpoint: true + }); return Chain.codes.badCheckpoint; } } @@ -359,7 +364,8 @@ Chain.prototype.add = function add(block, peer) { this.resetHeight(entry.height - 1); this.emit('fork', { height: prevHeight + 1, - blocks: [tip.hash, entry.hash], + expected: tip.hash, + received: entry.hash, checkpoint: null }); code = Chain.codes.forked; diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index c5952c54..f8ab2ffe 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -202,7 +202,7 @@ HDPrivateKey.prototype._normalize = function _normalize(data, version) { if (data.privateKey.getPrivate) data.privateKey = data.privateKey.getPrivate().toArray(); else if (typeof data.privateKey === 'string') - data.privateKey = utils.toKeyArray(data.privateKey); + data.privateKey = utils.toBuffer(data.privateKey); } // public key = 33 bytes @@ -210,7 +210,7 @@ HDPrivateKey.prototype._normalize = function _normalize(data, version) { if (data.publicKey.getPublic) data.publicKey = data.privateKey.getPublic(true, 'array'); else if (typeof data.publicKey === 'string') - data.publicKey = utils.toKeyArray(data.publicKey); + data.publicKey = utils.toBuffer(data.publicKey); } // checksum = 4 bytes diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index 2e9b3746..99db8473 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -68,40 +68,40 @@ Input.prototype.__defineGetter__('type', function() { return this.data.type; }); -Input.prototype.__defineGetter__('sig', function() { - return this.sigs[0]; +Input.prototype.__defineGetter__('signature', function() { + return this.signatures[0]; }); -Input.prototype.__defineGetter__('pub', function() { - return this.data.redeem || this.pubs[0]; +Input.prototype.__defineGetter__('key', function() { + return this.data.redeem || this.keys[0]; }); Input.prototype.__defineGetter__('hash', function() { return this.data.scripthash || this.hashes[0]; }); -Input.prototype.__defineGetter__('addr', function() { - return this.data.scriptaddr || this.addrs[0] || this._id; +Input.prototype.__defineGetter__('address', function() { + return this.data.scriptaddress || this.addresses[0] || this._id; }); -Input.prototype.__defineGetter__('sigs', function() { - return this.data.sigs || []; +Input.prototype.__defineGetter__('signatures', function() { + return this.data.signatures || []; }); -Input.prototype.__defineGetter__('pubs', function() { - return this.data.pubs || []; +Input.prototype.__defineGetter__('keys', function() { + return this.data.keys || []; }); Input.prototype.__defineGetter__('hashes', function() { return this.data.hashes || []; }); -Input.prototype.__defineGetter__('addrs', function() { - return this.data.addrs || []; +Input.prototype.__defineGetter__('addresses', function() { + return this.data.addresses || []; }); -Input.prototype.__defineGetter__('scriptaddr', function() { - return this.data.scriptaddr; +Input.prototype.__defineGetter__('scriptaddress', function() { + return this.data.scriptaddress; }); Input.prototype.__defineGetter__('m', function() { @@ -149,17 +149,45 @@ Input.prototype.__defineGetter__('_id', function() { return '[' + this.type + ':' + hash.slice(0, 7) + ']'; }); +Input.prototype.__defineGetter__('addr', function() { + return this.address; +}); + +Input.prototype.__defineGetter__('addrs', function() { + return this.addresses; +}); + +Input.prototype.__defineGetter__('pub', function() { + return this.key; +}); + +Input.prototype.__defineGetter__('pubs', function() { + return this.keys; +}); + +Input.prototype.__defineGetter__('sig', function() { + return this.signature; +}); + +Input.prototype.__defineGetter__('sigs', function() { + return this.signatures; +}); + +Input.prototype.__defineGetter__('scriptaddr', function() { + return this.scriptaddress; +}); + // Schema and defaults for data object: // { // type: null, // side: 'input', -// sigs: [], -// pubs: [], +// signatures: [], +// keys: [], // hashes: [], -// addrs: [], +// addresses: [], // redeem: null, // scripthash: null, -// scriptaddr: null, +// scriptaddress: null, // m: 0, // n: 0, // height: -1, @@ -176,12 +204,25 @@ Input.prototype.__defineGetter__('_id', function() { // } Input.getData = function getData(input) { + var s, sub, def, signature, key, hash, address, redeem, data, output, val; + + if (Array.isArray(input)) { + input = { + out: { + tx: null, + hash: utils.toHex(constants.oneHash), + index: 0 + }, + script: input, + seq: 0xffffffff + }; + } + if (!input || !input.script) return; - var s = input.script; - var sub = bcoin.script.subscript(input.script); - var def, sig, pub, hash, addr, redeem, data, output, val; + s = input.script; + sub = bcoin.script.subscript(input.script); def = { side: 'input', @@ -213,12 +254,12 @@ Input.getData = function getData(input) { output = input.out.tx.outputs[input.out.index]; data = bcoin.output.getData(output); if (data.type === 'pubkey' ) { - data.sigs = [sub[0]]; + data.signatures = [sub[0]]; } else if (data.type === 'pubkeyhash') { - data.sigs = [sub[0]]; - data.pubs = [sub[1]]; + data.signatures = [sub[0]]; + data.keys = [sub[1]]; } else if (data.type === 'multisig') { - data.sigs = sub.slice(1); + data.signatures = sub.slice(1); } else if (data.type === 'scripthash') { // We work backwards here: scripthash is one of the few cases // where we get more data from the input than the output. @@ -243,39 +284,39 @@ Input.getData = function getData(input) { if (bcoin.script.isPubkeyInput(s)) { return utils.merge(def, { type: 'pubkey', - sigs: [sub[0]], + signatures: [sub[0]], none: true }); } if (bcoin.script.isPubkeyhashInput(s)) { - pub = sub[1]; - hash = utils.ripesha(pub); - addr = bcoin.wallet.hash2addr(hash); + key = sub[1]; + hash = utils.ripesha(key); + address = bcoin.wallet.hash2addr(hash); return utils.merge(def, { type: 'pubkeyhash', - sigs: [sub[0]], - pubs: [pub], + signatures: [sub[0]], + keys: [key], hashes: [hash], - addrs: [addr] + addresses: [address] }); } if (bcoin.script.isMultisigInput(s)) { - sig = sub.slice(1); + signature = sub.slice(1); return utils.merge(def, { type: 'multisig', - sigs: sig, - m: sig.length, + signatures: signature, + m: signature.length, none: true }); } if (bcoin.script.isScripthashInput(s)) { - sig = sub.slice(1, -1); + signature = sub.slice(1, -1); redeem = sub[sub.length - 1]; hash = utils.ripesha(redeem); - addr = bcoin.wallet.hash2addr(hash, 'scripthash'); + address = bcoin.wallet.hash2addr(hash, 'scripthash'); redeem = bcoin.script.decode(redeem); data = bcoin.output.getData({ script: redeem, @@ -284,10 +325,10 @@ Input.getData = function getData(input) { return utils.merge(data, { type: 'scripthash', side: 'input', - sigs: sig, + signatures: signature, redeem: redeem, scripthash: hash, - scriptaddr: addr, + scriptaddress: address, script: s, seq: input.seq }); @@ -310,9 +351,9 @@ Input.prototype.inspect = function inspect() { return { type: this.type, - addr: this.addr, - sigs: this.sigs.map(utils.toHex), - pubs: this.pubs.map(utils.toHex), + address: this.address, + signatures: this.signatures.map(utils.toHex), + keys: this.keys.map(utils.toHex), text: this.text, lock: this.lock, value: utils.btc(output.value), diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index 12cb3f21..220d5922 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -52,40 +52,40 @@ Output.prototype.__defineGetter__('type', function() { return this.data.type; }); -Output.prototype.__defineGetter__('sig', function() { - return this.sigs[0]; +Output.prototype.__defineGetter__('signature', function() { + return this.signatures[0]; }); -Output.prototype.__defineGetter__('pub', function() { - return this.data.redeem || this.pubs[0]; +Output.prototype.__defineGetter__('key', function() { + return this.data.redeem || this.keys[0]; }); Output.prototype.__defineGetter__('hash', function() { return this.data.scripthash || this.hashes[0]; }); -Output.prototype.__defineGetter__('addr', function() { - return this.data.scriptaddr || this.addrs[0] || this._id; +Output.prototype.__defineGetter__('address', function() { + return this.data.scriptaddress || this.addresses[0] || this._id; }); -Output.prototype.__defineGetter__('sigs', function() { - return this.data.sigs || []; +Output.prototype.__defineGetter__('signatures', function() { + return this.data.signatures || []; }); -Output.prototype.__defineGetter__('pubs', function() { - return this.data.pubs || []; +Output.prototype.__defineGetter__('keys', function() { + return this.data.keys || []; }); Output.prototype.__defineGetter__('hashes', function() { return this.data.hashes || []; }); -Output.prototype.__defineGetter__('addrs', function() { - return this.data.addrs || []; +Output.prototype.__defineGetter__('addresses', function() { + return this.data.addresses || []; }); -Output.prototype.__defineGetter__('scriptaddr', function() { - return this.data.scriptaddr; +Output.prototype.__defineGetter__('scriptaddress', function() { + return this.data.scriptaddress; }); Output.prototype.__defineGetter__('m', function() { @@ -116,17 +116,45 @@ Output.prototype.__defineGetter__('_id', function() { return '[' + this.type + ':' + hash.slice(0, 7) + ']'; }); +Output.prototype.__defineGetter__('addr', function() { + return this.address; +}); + +Output.prototype.__defineGetter__('addrs', function() { + return this.addresses; +}); + +Output.prototype.__defineGetter__('pub', function() { + return this.key; +}); + +Output.prototype.__defineGetter__('pubs', function() { + return this.keys; +}); + +Output.prototype.__defineGetter__('sig', function() { + return this.signature; +}); + +Output.prototype.__defineGetter__('sigs', function() { + return this.signatures; +}); + +Output.prototype.__defineGetter__('scriptaddr', function() { + return this.scriptaddress; +}); + // Schema and defaults for data object: // { // type: null, // side: 'output', -// sigs: [], -// pubs: [], +// signatures: [], +// keys: [], // hashes: [], -// addrs: [], +// addresses: [], // redeem: null, // scripthash: null, -// scriptaddr: null, +// scriptaddress: null, // m: 0, // n: 0, // height: -1, @@ -143,13 +171,21 @@ Output.prototype.__defineGetter__('_id', function() { // } Output.getData = function getData(output) { + var s, sub, lock, def, key, hash, address, keys, data; + + if (Array.isArray(output)) { + output = { + script: output, + value: new bn(0) + }; + } + if (!output || !output.script) return; - var s = output.script; - var sub = bcoin.script.subscript(output.script); - var lock = bcoin.script.lockTime(s); - var def, pub, hash, addr, pubs, data; + s = output.script; + sub = bcoin.script.subscript(output.script); + lock = bcoin.script.lockTime(s); def = { side: 'output', @@ -163,41 +199,41 @@ Output.getData = function getData(output) { } if (bcoin.script.isPubkey(s)) { - pub = sub[0]; - hash = utils.ripesha(pub); - addr = bcoin.wallet.hash2addr(hash); + key = sub[0]; + hash = utils.ripesha(key); + address = bcoin.wallet.hash2addr(hash); return utils.merge(def, { type: 'pubkey', - pubs: [pub], + keys: [key], hashes: [hash], - addrs: [addr] + addresses: [address] }); } if (bcoin.script.isPubkeyhash(s)) { hash = sub[2]; - addr = bcoin.wallet.hash2addr(hash); + address = bcoin.wallet.hash2addr(hash); return utils.merge(def, { type: 'pubkeyhash', side: 'output', hashes: [hash], - addrs: [addr] + addresses: [address] }); } - pubs = bcoin.script.isMultisig(s); - if (pubs) { - hash = pubs.map(function(key) { + keys = bcoin.script.isMultisig(s); + if (keys) { + hash = keys.map(function(key) { return utils.ripesha(key); }); - addr = hash.map(function(hash) { + address = hash.map(function(hash) { return bcoin.wallet.hash2addr(hash); }); return utils.merge(def, { type: 'multisig', - pubs: pubs, + keys: keys, hashes: hash, - addrs: addr, + addresses: address, m: new bn(sub[0]).toNumber(), n: new bn(sub[sub.length - 2]).toNumber() }); @@ -205,11 +241,11 @@ Output.getData = function getData(output) { if (bcoin.script.isScripthash(s)) { hash = sub[1]; - addr = bcoin.wallet.hash2addr(hash, 'scripthash'); + address = bcoin.wallet.hash2addr(hash, 'scripthash'); return utils.merge(def, { type: 'scripthash', scripthash: hash, - scriptaddr: addr + scriptaddress: address }); } @@ -232,10 +268,10 @@ Output.getData = function getData(output) { Output.prototype.inspect = function inspect() { return { type: this.type, - addr: this.addr, - pubs: this.pubs.map(utils.toHex), + address: this.address, + keys: this.keys.map(utils.toHex), hashes: this.hashes.map(utils.toHex), - addrs: this.addrs, + addresses: this.addresses, redeem: this.type === 'scripthash' ? bcoin.script.format(this.data.redeem)[0] : null, diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index f4d8f9f5..5cfda7a2 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -173,10 +173,16 @@ Pool.prototype._init = function _init() { self.emit('block', block, peer); }); - this.chain.on('fork', function(height, hash, checkpoint) { + this.chain.on('fork', function(data) { var peer = self.peers.load; - this.emit('debug', 'Checkpoint %s failed at height %d', hash, height); + this.emit('debug', + 'Fork at height %d: expected=%s received=%s checkpoint=%s', + data.height, + utils.revHex(data.expected), + utils.revHex(data.received), + data.checkpoint + ); if (!peer) return; @@ -498,13 +504,18 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { // Make sure the block is valid if (!block.verify()) { - this.emit('debug', 'Block verification failed for %s (%s)', block.rhash, peer.host); + this.emit('debug', + 'Block verification failed for %s (%s)', + block.rhash, peer.host); return; } // Someone is sending us blocks without us requesting them. - if (!requested) - this.emit('debug', 'Recieved unrequested block: %s (%s)', block.rhash, peer.host); + if (!requested) { + this.emit('debug', + 'Recieved unrequested block: %s (%s)', + block.rhash, peer.host); + } // Resolve orphan chain if (!this.options.headers) { diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 3d146325..f700a445 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -372,7 +372,7 @@ TX.prototype.out = TX.prototype.output; TX.prototype.scriptOutput = function scriptOutput(output, options) { var script = output.script; - var keys, m, n, hash, locktime; + var keys, m, n, hash, locktime, flags; options = options || output; @@ -401,7 +401,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { } if (options.color) { - options.nulldata = options.color; + options.flags = options.color; delete options.color; } @@ -414,18 +414,14 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { } if (Array.isArray(options.keys)) { - // Raw multisig transaction + // Bare Multisig Transaction // https://github.com/bitcoin/bips/blob/master/bip-0010.mediawiki // https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki // https://github.com/bitcoin/bips/blob/master/bip-0019.mediawiki - // [required-sigs] [pubkey-hash1] [pubkey-hash2] ... [number-of-keys] checkmultisig + // m [key1] [key2] ... n checkmultisig keys = options.keys; - keys = keys.map(function(key) { - if (typeof key === 'string') - return utils.toKeyArray(key); - return key; - }); + keys = keys.map(utils.toBuffer); m = options.m || keys.length; n = options.n || keys.length; @@ -438,7 +434,8 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { script = bcoin.script.redeem(keys, m, n); - // make it p2sh + // P2SH Transaction + // hash160 [hash] eq if (options.scripthash) { hash = utils.ripesha(bcoin.script.encode(script)); script = [ @@ -448,7 +445,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { ]; } } else if (bcoin.wallet.validateAddress(options.address, 'scripthash')) { - // p2sh transaction + // P2SH Transaction // https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki // hash160 [20-byte-redeemscript-hash] equal script = [ @@ -457,7 +454,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { 'eq' ]; } else if (options.address) { - // p2pkh transaction + // P2PKH Transaction // dup hash160 [pubkey-hash] equalverify checksig script = [ 'dup', @@ -466,14 +463,24 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { 'eqverify', 'checksig' ]; - } else if (options.nulldata) { - nulldata = options.nulldata; - if (typeof nulldata === 'string') - nulldata = utils.ascii2array(nulldata); - assert(nulldata.length <= constants.script.maxOpReturn); + } else if (options.key) { + // P2PK Transaction + // [pubkey] checksig + script = [ + utils.toBuffer(options.key), + 'checksig' + ]; + } else if (options.flags) { + // Nulldata Transaction + // ret [data] + flags = options.flags; + if (typeof flags === 'string') + flags = utils.ascii2array(flags); + assert(utils.isBuffer(flags)); + assert(flags.length <= constants.script.maxOpReturn); script = [ 'ret', - nulldata + flags ]; } @@ -763,7 +770,7 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) { this.changeAddress = changeAddress || this.changeAddress - || result.inputs[0].output.addr; + || result.inputs[0].output.address; result.inputs.forEach(function(input) { this.input(input); diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 0d60ae75..1495f421 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -683,7 +683,7 @@ utils.isBuffer = function isBuffer(msg) { return Array.isArray(msg); }; -utils.toKeyArray = function toKeyArray(msg) { +utils.toBuffer = function toBuffer(msg) { if (Array.isArray(msg)) return msg; @@ -696,7 +696,7 @@ utils.toKeyArray = function toKeyArray(msg) { if (utils.isBase58(msg)) return utils.fromBase58(msg); - throw new Error('Cannot ensure array'); + throw new Error('Cannot ensure buffer'); }; utils._inspect = function inspect(obj) { diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index a9fffb8a..832cb093 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -165,7 +165,7 @@ Wallet.prototype.multisig = function multisig(options) { }; Wallet.prototype.addKey = function addKey(key) { - key = utils.toKeyArray(key); + key = utils.toBuffer(key); var has = this.keys.some(function(k) { return utils.isEqual(k, key); @@ -180,7 +180,7 @@ Wallet.prototype.addKey = function addKey(key) { }; Wallet.prototype.removeKey = function removeKey(key) { - key = utils.toKeyArray(key); + key = utils.toBuffer(key); var index = this.keys.map(function(key, i) { return utils.isEqual(key, pub) ? i : null; @@ -294,8 +294,7 @@ Wallet.prototype.getAddress = function getAddress() { }; Wallet.key2hash = function key2hash(key) { - if (typeof key === 'string') - key = utils.toKeyArray(key); + key = utils.toBuffer(key); return utils.ripesha(key); }; @@ -347,7 +346,7 @@ Wallet.addr2hash = function addr2hash(addr, prefix) { return addr.slice(1, -4); }; -Wallet.prototype.validateAddress = function validateAddress(addr, prefix) { +Wallet.validateAddress = function validateAddress(addr, prefix) { if (!addr) return false; @@ -356,8 +355,6 @@ Wallet.prototype.validateAddress = function validateAddress(addr, prefix) { return p.length !== 0; }; -Wallet.validateAddress = Wallet.prototype.validateAddress; - Wallet.prototype.ownOutput = function ownOutput(tx, index) { var scriptHash = this.getFullHash(); var hash = this.getOwnHash();