diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index e2587c75..3095d51f 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -184,62 +184,43 @@ Input.prototype.__defineGetter__('scriptaddr', function() { // Schema and defaults for data object: // { -// type: null, -// subtype: null, +// type: String, +// subtype: String, // side: 'input', -// signatures: [], -// keys: [], -// hashes: [], -// addresses: [], -// redeem: null, -// scripthash: null, -// scriptaddress: null, -// m: 0, -// n: 0, -// height: -1, -// flags: null, -// text: null, -// lock: 0, -// value: new bn(0), -// script: s, -// seq: input.seq, -// prev: null, -// index: null, -// _script: null, -// none: false +// signatures: Array, +// keys: Array, +// hashes: Array, +// addresses: Array, +// redeem: Array, +// scripthash: Array, +// scriptaddress: String, +// m: Number, +// n: Number, +// height: Number, +// flags: Array, +// text: String, +// lock: Number, +// value: bn, +// script: Array, +// seq: Number, +// prev: String, +// index: Number, +// none: Boolean // } 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 - }; - } + var def, data, output; if (!input || !input.script) return; - s = input.script; - sub = bcoin.script.subscript(input.script); - def = { side: 'input', value: new bn(0), - script: s, + script: input.script, seq: input.seq }; - if (bcoin.script.lockTime(sub)) - sub = sub.slice(3); - if (input.out) { def.prev = input.out.hash; def.index = input.out.index; @@ -247,100 +228,22 @@ Input.getData = function getData(input) { if (input.out && +input.out.hash === 0) { data = bcoin.script.coinbase(input.script); - return utils.merge(def, { + return utils.merge(def, data, { type: 'coinbase', - height: data.height != null ? data.height : -1, - flags: data.flags, - text: data.text, none: true }); } if (input.out && input.out.tx) { output = input.out.tx.outputs[input.out.index]; - data = bcoin.output.getData(output); - if (data.type === 'pubkey' ) { - data.signatures = [sub[0]]; - } else if (data.type === 'pubkeyhash') { - data.signatures = [sub[0]]; - data.keys = [sub[1]]; - } else if (data.type === 'multisig') { - 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. - val = bcoin.input.getData({ - out: { hash: input.out.hash, index: input.out.index }, - script: input.script, - seq: input.seq - }); - val.lock = data.lock; - val.value = data.value; - val.script = data.script; - data = val; + if (output) { + data = bcoin.script.getData(input.script, output.script); + data.value = output.value; + return utils.merge(def, data); } - return utils.merge(data, { - seq: def.seq, - prev: def.prev, - index: def.index, - _script: def.script - }); } - if (bcoin.script.isPubkeyInput(s)) { - return utils.merge(def, { - type: 'pubkey', - signatures: [sub[0]], - none: true - }); - } - - if (bcoin.script.isPubkeyhashInput(s)) { - key = sub[1]; - hash = utils.ripesha(key); - address = bcoin.wallet.hash2addr(hash); - return utils.merge(def, { - type: 'pubkeyhash', - signatures: [sub[0]], - keys: [key], - hashes: [hash], - addresses: [address] - }); - } - - if (bcoin.script.isMultisigInput(s)) { - signature = sub.slice(1); - return utils.merge(def, { - type: 'multisig', - signatures: signature, - m: signature.length, - none: true - }); - } - - if (bcoin.script.isScripthashInput(s)) { - redeem = sub[sub.length - 1]; - hash = utils.ripesha(redeem); - address = bcoin.wallet.hash2addr(hash, 'scripthash'); - redeem = bcoin.script.decode(redeem); - val = bcoin.input.getData(sub.slice(0, -1)); - data = bcoin.output.getData(redeem); - return utils.merge(val, data, { - type: 'scripthash', - subtype: data.type, - side: 'input', - redeem: redeem, - scripthash: hash, - scriptaddress: address, - script: s, - seq: input.seq - }); - } - - return utils.merge(def, { - type: 'unknown', - none: true - }); + return utils.merge(def, bcoin.script.getInputData(input.script)); }; Input.prototype.inspect = function inspect() { @@ -356,6 +259,7 @@ Input.prototype.inspect = function inspect() { type: this.type, subtype: this.data.subtype, address: this.address, + addresses: this.addresses, signatures: this.signatures.map(utils.toHex), keys: this.keys.map(utils.toHex), text: this.text, diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index bf706439..c24eba9c 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -105,10 +105,7 @@ Output.prototype.__defineGetter__('n', function() { }); Output.prototype.__defineGetter__('lock', function() { - var lock = bcoin.script.lockTime(this.script); - if (!lock) - return 0; - return lock.toNumber(); + return bcoin.script.lockTime(this.script); }); Output.prototype.__defineGetter__('text', function() { @@ -154,123 +151,43 @@ Output.prototype.__defineGetter__('scriptaddr', function() { // Schema and defaults for data object: // { -// type: null, -// subtype: null, +// type: String, +// subtype: String, // side: 'output', -// signatures: [], -// keys: [], -// hashes: [], -// addresses: [], -// redeem: null, -// scripthash: null, -// scriptaddress: null, -// m: 0, -// n: 0, -// height: -1, -// flags: null, -// text: null, -// lock: lock ? lock.toNumber() : 0, -// value: output.value, -// script: s, -// seq: null, -// prev: null, -// index: null, -// _script: null, -// none: false +// signatures: Array, +// keys: Array, +// hashes: Array, +// addresses: Array, +// redeem: Array, +// scripthash: Array, +// scriptaddress: String, +// m: Number, +// n: Number, +// height: Number, +// flags: Array, +// text: String, +// lock: Number, +// value: bn, +// script: Array, +// seq: Number, +// prev: String, +// index: Number, +// none: Boolean // } 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) - }; - } + var def; if (!output || !output.script) return; - s = output.script; - sub = bcoin.script.subscript(output.script); - lock = bcoin.script.lockTime(s); - def = { side: 'output', value: output.value, - script: s + script: output.script }; - if (lock) { - sub = sub.slice(3); - def.lock = lock.toNumber(); - } - - if (bcoin.script.isPubkey(s)) { - key = sub[0]; - hash = utils.ripesha(key); - address = bcoin.wallet.hash2addr(hash); - return utils.merge(def, { - type: 'pubkey', - keys: [key], - hashes: [hash], - addresses: [address] - }); - } - - if (bcoin.script.isPubkeyhash(s)) { - hash = sub[2]; - address = bcoin.wallet.hash2addr(hash); - return utils.merge(def, { - type: 'pubkeyhash', - hashes: [hash], - addresses: [address] - }); - } - - if (bcoin.script.isMultisig(s)) { - keys = sub.slice(1, -2); - hash = keys.map(function(key) { - return utils.ripesha(key); - }); - address = hash.map(function(hash) { - return bcoin.wallet.hash2addr(hash); - }); - return utils.merge(def, { - type: 'multisig', - keys: keys, - hashes: hash, - addresses: address, - m: new bn(sub[0]).toNumber(), - n: new bn(sub[sub.length - 2]).toNumber() - }); - } - - if (bcoin.script.isScripthash(s)) { - hash = sub[1]; - address = bcoin.wallet.hash2addr(hash, 'scripthash'); - return utils.merge(def, { - type: 'scripthash', - scripthash: hash, - scriptaddress: address - }); - } - - if (bcoin.script.isNulldata(s)) { - data = bcoin.script.nulldata(s); - return utils.merge(def, { - type: 'nulldata', - flags: data, - text: utils.array2utf8(data), - none: true - }); - } - - return utils.merge(def, { - type: 'unknown', - none: true - }); + return utils.merge(def, bcoin.script.getOutputData(output.script)); }; Output.prototype.inspect = function inspect() { diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index d24a451f..552b2f77 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -168,7 +168,7 @@ script.verify = function verify(input, output, tx, i, flags) { res = script.execute(output, stack, tx, i, flags); // Verify the script did not fail as well as the stack values - if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) + if (!res || stack.length === 0 || script.num(stack.pop()).cmpn(0) === 0) return false; // If the script is P2SH, execute the real output script @@ -196,7 +196,7 @@ script.verify = function verify(input, output, tx, i, flags) { res = script.execute(redeem, stack, tx, i, flags); // Verify the script did not fail as well as the stack values - if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) + if (!res || stack.length === 0 || script.num(stack.pop()).cmpn(0) === 0) return false; } @@ -351,7 +351,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { if (stack.length < 1) return false; v = stack.pop(); - val = new bn(v).cmpn(0) !== 0; + val = script.num(v).cmpn(0) !== 0; if (o === 'notif') val = !val; if_ = pc; @@ -391,7 +391,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { case 'verify': { if (stack.length === 0) return false; - if (new bn(stack.pop()).cmpn(0) === 0) + if (script.num(stack.pop()).cmpn(0) === 0) return false; break; } @@ -413,12 +413,12 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { case 'ifdup': { if (stack.length === 0) return false; - if (new bn(stack[stack.length - 1]).cmpn(0) !== 0) - stack.push(new bn(stack[stack.length - 1]).toArray()); + if (script.num(stack[stack.length - 1]).cmpn(0) !== 0) + stack.push(script.numArray(stack[stack.length - 1])); break; } case 'depth': { - stack.push(new bn(stack.length).toArray()); + stack.push(script.numArray(stack.length)); break; } case 'drop': { @@ -452,7 +452,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { v = stack.pop(); if (v.length > 6) return false; - n = new bn(v).toNumber(); + n = script.num(v, true); if (n < 0 || n >= stack.length) return false; v = stack[-n - 1]; @@ -551,7 +551,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { case 'size': { if (stack.length < 1) return false; - stack.push(new bn(stack[stack.length - 1].length || 0).toArray()); + stack.push(script.numArray(stack[stack.length - 1].length || 0)); break; } case 'add1': @@ -562,7 +562,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { case 'noteq0': { if (stack.length < 1) return false; - n = new bn(stack.pop()); + n = script.num(stack.pop()); switch (o) { case 'add1': n.iadd(1); @@ -587,8 +587,8 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { return false; } if (typeof n === 'boolean') - n = new bn(+n); - stack.push(n.toArray()); + n = script.num(+n); + stack.push(script.numArray(n)); break; } case 'add': @@ -620,9 +620,9 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { case 'max': if (stack.length < 2) return false; - n2 = new bn(stack.pop()); - n1 = new bn(stack.pop()); - n = new bn(0); + n2 = script.num(stack.pop()); + n1 = script.num(stack.pop()); + n = script.num(0); switch (o) { case 'add': n = n1.add(n2); @@ -667,21 +667,21 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { return false; } if (typeof n === 'boolean') - n = new bn(+n); + n = script.num(+n); res = n.cmpn(0) !== 0; if (o === 'numeqverify') { if (!res) return false; } else { - stack.push(n.toArray()); + stack.push(script.numArray(n)); } break; case 'within': if (stack.length < 3) return false; - n3 = new bn(stack.pop()); - n2 = new bn(stack.pop()); - n1 = new bn(stack.pop()); + n3 = script.num(stack.pop()); + n2 = script.num(stack.pop()); + n1 = script.num(stack.pop()); val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0; stack.push(val.cmpn(0) !== 0 ? [ 1 ] : []); break; @@ -778,16 +778,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { if (!tx || stack.length < 3) return false; - n = stack.pop(); + n = script.num(stack.pop(), true); - if (!Array.isArray(n)) + if (!(n >= 1 && n <= 15)) return false; - if (n.length !== 1 || !(n[0] >= 1 && n[0] <= 15)) - return false; - - n = n[0]; - if (stack.length < n + 1) return false; @@ -800,16 +795,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { keys.push(key); } - m = stack.pop(); + m = script.num(stack.pop(), true); - if (!Array.isArray(m)) + if (!(m >= 1 && m <= n)) return false; - if (m.length !== 1 || !(m[0] >= 1 && m[0] <= n)) - return false; - - m = m[0]; - if (stack.length < m + 1) return false; @@ -878,7 +868,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { if (lock.length > 6) return false; - lock = new bn(lock).toNumber(); + lock = script.num(lock, true); if (lock < 0) return false; @@ -948,7 +938,40 @@ script.num = function num(value, useNum) { assert(utils.isBuffer(value)); - value = new bn(value, 'le'); + if (script.requireminimal && value.length > 0) { + // If the low bits on the last byte are unset, + // fail if The value's second to last byte does + // not have the high bit set. A number can't + // justify having the last byte's low bits unset + // unless they ran out of space for the sign bit + // in the second to last bit. This also takes + // care of negative We also fail on [0] to avoid + // negative zero (also avoids positive zero). + if (!(value[value.length - 1] & 0x7f)) { + if (value.length === 1 || !(value[value.length - 2] & 0x80)) { + // We should technically fail here by + // throwing and catching, but return zero for now. + return useNum ? 0 : new bn(0, 'le'); + } + } + } + + // Optimize by avoiding big numbers + if (useNum && value.length <= 1) + return value.length === 0 ? 0 : value[0]; + + // If we are signed, do (~num + 1) to get + // the positive counterpart and set bn's + // negative flag. + if (value[value.length - 1] & 0x80) { + if (utils.isNegZero(value, 'le')) { + value = new bn(0, 'le'); + } else { + value = new bn(value, 'le').notn(64).addn(1).neg(); + } + } else { + value = new bn(value, 'le'); + } if (useNum) { try { @@ -961,6 +984,55 @@ script.num = function num(value, useNum) { return value; }; +script.numArray = function(value) { + if (Array.isArray(value)) + return value.slice(); + + if (utils.isFinite(value)) + value = new bn(value, 'le'); + + assert(value instanceof bn); + + // Convert the number to the + // negative byte representation. + if (value.isNeg()) { + if (value.cmpn(0) === 0) + value = new bn(0); + else + value = value.neg().notn(64).addn(1); + } + + if (value.cmpn(0) === 0) + return []; + + return value.toArray('le'); +}; + +script.checkPush = function checkPush(op, value) { + if (!script.requireminimal) + return true; + + if (value.length === 1 && value[0] === 0) + return op === constants.opcodes['0']; + + if (value.length === 1 && value[0] >= 1 && value[0] <= 16) + return op >= constants.opcodes['1'] && op <= constants.opcodes['16']; + + if (value.length === 1 && value[0] === -1) + return op === constants.opcodes['-1']; + + if (value.length <= 75) + return op === value.length; + + if (value.length <= 255) + return op === constants.opcodes.pushdata1; + + if (value.length <= 65535) + return op === constants.opcodes.pushdata2; + + return true; +}; + script.redeem = function redeem(keys, m, n) { if (keys.length !== n) throw new Error(n + ' keys are required to generate redeem script'); @@ -989,17 +1061,18 @@ script.standard = function standard(s) { }; script.isStandard = function isStandard(s) { - var type = script.standard(s); var m, n; - if (type === 'multisig') { - m = new bn(s[0]).toNumber(); - n = new bn(s[s.length - 2]).toNumber(); + if (script.isMultisig(s)) { + m = s[0]; + n = s[s.length - 2]; + if (n < 1 || n > 3) return false; + if (m < 1 || m > n) return false; - } else if (type === 'nulldata') { + } else if (script.isNulldata(s)) { if (script.size(s) > constants.script.maxOpReturnBytes) return false; } @@ -1010,53 +1083,183 @@ script.isStandard = function isStandard(s) { script.size = function size(s) { if (s._raw) return s._raw.length; - return bcoin.script.encode(s).length; + return script.encode(s).length; }; script.isEncoded = function isEncoded(s) { return utils.isBytes(s); }; -script.normalize = function normalize(s) { - if (script.isEncoded(s)) - s = script.decode(s); - - s = script.subscript(s); - - if (script.lockTime(s)) - s = s.slice(3); - - return s; +script.isLockTime = function isLockTime(s, check) { + return s.length > 4 + && Array.isArray(s[0]) + && s[1] === 'checklocktimeverify' + && s[2] === 'drop' + && s[3] === 'codesep'; }; script.lockTime = function lockTime(s) { - var lock = s[0]; - var res = s.length > 3 - && Array.isArray(s[0]) - && s[1] === 'checklocktimeverify' - && s[2] === 'drop'; - - if (!res) + if (!script.isLockTime(s)) return false; - // Number can only store 6 & 5/8 bytes - if (lock.length > 6) - lock = lock.slice(0, 6); - - return new bn(lock); + return script.num(s[0], true); }; script.spendable = function spendable(s, lockTime) { - if (!script.standard(s)) - return false; - - var lock = script.lockTime(s); - if (lock && lock.toNumber() > lockTime) + if (script.lockTime(s) > lockTime) return false; return true; }; +script.getData = function getData(s, prev) { + var output; + + if (prev && !script.isScripthash(prev)) { + output = script.getOutputData(prev); + output.side = 'input'; + + if (output.type === 'pubkey') { + output.signatures = [s[0]]; + } else if (output.type === 'pubkeyhash') { + output.signatures = [s[0]]; + output.keys = [s[1]]; + } else if (output.type === 'multisig') { + output.signatures = s.slice(1); + } + + return output; + } + + return script.getInputData(s); +}; + +script.getInputData = function getInputData(s) { + var sig, key, hash, raw, redeem, lock, hash, address, input, output; + + if (script.isPubkeyInput(s)) { + key = s[0]; + return { + type: 'pubkey', + side: 'input', + signatures: [sig], + none: true + }; + } + + if (script.isPubkeyhashInput(s)) { + sig = s[0]; + key = s[1]; + hash = bcoin.wallet.key2hash(key); + address = bcoin.wallet.hash2addr(hash, 'pubkeyhash'); + return { + type: 'pubkeyhash', + side: 'input', + signatures: [sig], + keys: [key], + hashes: [hash], + addresses: [address] + }; + } + + if (script.isMultisigInput(s)) { + sig = s.slice(1); + return { + type: 'multisig', + side: 'input', + signatures: sig, + m: sig.length, + none: true + }; + } + + if (script.isScripthashInput(s)) { + raw = s[s.length - 1]; + redeem = script.decode(raw); + lock = script.lockTime(redeem); + hash = bcoin.wallet.key2hash(raw); + address = bcoin.wallet.hash2addr(hash, 'scripthash'); + input = script.getInputData(s.slice(0, -1)); + delete input.none; + output = script.getOutputData(script.subscript(redeem)); + return utils.merge(input, output, { + type: 'scripthash', + side: 'input', + subtype: output.type, + redeem: redeem, + scripthash: hash, + scriptaddress: address, + lock: lock + }); + } + + return { + type: 'unknown', + none: true + }; +}; + +script.getOutputData = function getOutputData(s) { + var key, hash, address; + + if (script.isPubkey(s)) { + key = s[0]; + hash = bcoin.wallet.key2hash(key); + address = bcoin.wallet.hash2addr(hash, 'pubkey'); + return { + type: 'pubkey', + side: 'output', + keys: [key], + hashes: [hash], + addresses: [address] + }; + } + + if (script.isPubkeyhash(s)) { + hash = s[2]; + return { + type: 'pubkeyhash', + side: 'output', + hashes: [hash], + addresses: [bcoin.wallet.hash2addr(hash, 'pubkeyhash')] + }; + } + + if (script.isMultisig(s)) { + key = s.slice(1, -2); + hash = key.map(function(key) { + return bcoin.wallet.key2hash(key); + }); + address = hash.map(function(hash) { + return bcoin.wallet.hash2addr(hash, 'multisig'); + }); + return { + type: 'multisig', + side: 'output', + keys: key, + hashes: hash, + addresses: address, + m: s[0], + n: s[s.length - 2] + }; + } + + if (script.isScripthash(s)) { + hash = s[1]; + return { + type: 'scripthash', + side: 'output', + scripthash: hash, + scriptaddress: bcoin.wallet.hash2addr(hash, 'scripthash') + }; + } + + return { + type: 'unknown', + none: true + }; +}; + script.isPubkey = function isPubkey(s, key) { var res; @@ -1068,10 +1271,12 @@ script.isPubkey = function isPubkey(s, key) { if (!res) return false; - if (key) - return utils.isEqual(s[0], key); + if (key) { + if (!utils.isEqual(s[0], key)) + return false; + } - return true; + return s[0]; }; script.isPubkeyhash = function isPubkeyhash(s, hash) { @@ -1089,10 +1294,12 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) { if (!res) return false; - if (hash) - return utils.isEqual(s[2], hash); + if (hash) { + if (!utils.isEqual(s[2], hash)) + return false; + } - return true; + return s[2]; }; script.isMultisig = function isMultisig(s, keys) { @@ -1107,6 +1314,15 @@ script.isMultisig = function isMultisig(s, keys) { n = s[s.length - 2]; + if (Array.isArray(n)) { + if (n.length !== 0) + return false; + n = 0; + } + + if (typeof n !== 'number') + return false; + // Bitcoind technically doesn't check for the // 15 limit here. It just counts the sigops // later. @@ -1115,6 +1331,15 @@ script.isMultisig = function isMultisig(s, keys) { m = s[0]; + if (Array.isArray(m)) { + if (m.length !== 0) + return false; + m = 0; + } + + if (typeof m !== 'number') + return false; + if (!(m >= 1 && m <= n)) return false; @@ -1126,21 +1351,27 @@ script.isMultisig = function isMultisig(s, keys) { return false; } - if (!keys) - return true; + if (keys) { + keys = utils.sortKeys(keys); - keys = utils.sortKeys(keys); - - for (i = 1; i < n + 1; i++) { - for (j = 0; j < keys.length; j++) { - if (utils.isEqual(s[i], keys[j])) { - total++; - break; + for (i = 1; i < n + 1; i++) { + for (j = 0; j < keys.length; j++) { + if (utils.isEqual(s[i], keys[j])) { + total++; + break; + } } } + + if (total !== n) + return false; } - return total === n; + return { + keys: s.slice(1, n + 1), + m: m, + n: n + }; }; script.isScripthash = function isScripthash(s, hash) { @@ -1156,23 +1387,25 @@ script.isScripthash = function isScripthash(s, hash) { if (!res) return false; - if (hash) - return utils.isEqual(s[1], hash); + if (hash) { + if (!utils.isEqual(s[1], hash)) + return false; + } - return true; + return s[1]; }; script.isNulldata = function isNulldata(s) { + var res; + if (s.length !== 2) return false; - return s[0] === 'ret' + res = s[0] === 'ret' && Array.isArray(s[1]) && s[1].length <= constants.script.maxOpReturn; -}; -script.nulldata = function nulldata(s) { - if (!script.isNulldata(s)) + if (!res) return false; return s[1]; @@ -1187,7 +1420,7 @@ script.standardInput = function standardInput(s) { }; script.isPubkeyInput = function isPubkeyInput(s, key, tx, i) { - if (s.length !== 1 || !Array.isArray(s[0])) + if (s.length !== 1) return false; if (!script.isSignature(s[0])) @@ -1199,14 +1432,15 @@ script.isPubkeyInput = function isPubkeyInput(s, key, tx, i) { if (key) { assert(tx); assert(i != null); - return script.verify(s, [key, 'checksig'], tx, i); + if (!script.verify(s, [key, 'checksig'], tx, i)) + return false; } - return true; + return s[0]; }; script.isPubkeyhashInput = function isPubkeyhashInput(s, key) { - if (s.length !== 2 || !Array.isArray(s[0]) || !Array.isArray(s[1])) + if (s.length !== 2) return false; if (!script.isSignature(s[0])) @@ -1215,10 +1449,12 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) { if (!script.isKey(s[1])) return false; - if (key) - return utils.isEqual(s[1], key); + if (key) { + if (!utils.isEqual(s[1], key)) + return false; + } - return true; + return s[1]; }; script.isMultisigInput = function isMultisigInput(s, keys, tx, i) { @@ -1248,7 +1484,8 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) { assert(tx); assert(i != null); o = script.redeem(keys, s.length - 1, keys.length); - return script.verify(s, o, tx, i); + if (!script.verify(s, o, tx, i)) + return false; } // We also also try to recover the keys from the signatures. @@ -1261,7 +1498,10 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) { // recovered.push(key); // } - return true; + return { + signatures: s.slice(1), + m: s.length - 1 + }; }; script.isScripthashInput = function isScripthashInput(s, data) { @@ -1297,12 +1537,16 @@ script.isScripthashInput = function isScripthashInput(s, data) { // Check data against last array in case // a raw redeem script was passed in. if (data && utils.isEqual(data, raw)) - return true; + return raw; // Test against all other script types - return script.isPubkey(redeem, data) - || script.isPubkeyhash(redeem, data) - || script.isMultisig(redeem, data); + if (!script.isPubkey(redeem, data) + && !script.isPubkeyhash(redeem, data) + && !script.isMultisig(redeem, data)) { + return false; + } + + return raw; }; script.coinbaseBits = function coinbaseBits(s, block) { @@ -1319,7 +1563,7 @@ script.coinbaseBits = function coinbaseBits(s, block) { if (s[0].length > 6) return { type: 'value', value: s[0] }; - value = new bn(s[0].slice().reverse()).toNumber(); + value = script.num(s[0], true); // Test for bits and ts if (block && block.version < 2) { @@ -1366,7 +1610,7 @@ script.coinbase = function coinbase(s, block) { data = script.coinbaseBits(s, block); if (Array.isArray(s[1])) - extraNonce = new bn(s[1]); + extraNonce = script.num(s[1]); flags = s.slice(2); @@ -1377,6 +1621,9 @@ script.coinbase = function coinbase(s, block) { flags.map(utils.array2utf8).join('') .replace(/[\u0000-\u0019\u007f-\u00ff]/g, ''); + if (coinbase.height == null) + coinbase.height = -1; + return coinbase; }; @@ -1684,7 +1931,7 @@ script.args = function args(s) { keys = s.slice(1, -2); if (!pub) return -1; - m = new bn(s[0]).toNumber(); + m = s[0]; if (keys.length < 1 || m < 1) return -1; return m + 1; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index ea4aa477..c0ab667b 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -197,8 +197,6 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) { // Grab `n` value (number of keys). n = s[s.length - 2]; - if (Array.isArray(n)) - n = n[0]; // Fill script with `n` signature slots. for (i = 0; i < n; i++) @@ -315,13 +313,9 @@ TX.prototype.signInput = function signInput(index, key, type) { // Grab `m` value (number of sigs required). m = s[0]; - if (Array.isArray(m)) - m = m[0]; // Grab `n` value (number of keys). n = s[s.length - 2]; - if (Array.isArray(n)) - n = n[0]; // Something is very wrong here. Abort. if (len - 1 > n) @@ -565,9 +559,10 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) { if (options.scripthash) { if (options.lock != null) { script = [ - new bn(options.lock).toArray(), + bcoin.script.numArray(options.lock), 'checklocktimeverify', - 'drop' + 'drop', + 'codesep' ].concat(script); } hash = utils.ripesha(bcoin.script.encode(script)); @@ -757,8 +752,6 @@ TX.prototype.maxSize = function maxSize() { // Bare Multisig // Get the previous m value: m = s[0]; - if (Array.isArray(m)) - m = m[0]; // OP_0 size += 1; // OP_PUSHDATA0 [signature] ... diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 7b199d48..974da2e5 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -704,7 +704,7 @@ utils.isBytes = function isBytes(data) { return false; for (i = 0; i < data.length; i++) { - if (typeof data[i] !== 'number') + if (typeof data[i] !== 'number' || data[i] < 0x00 || data[i] > 0xff) return false; }