From f0edd2c944c0e560ef4dfc7adb9bf5da9090217b Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 15 Mar 2016 04:59:52 -0700 Subject: [PATCH] remove old script.js. --- lib/bcoin/script.js | 2575 ------------------------------------------- 1 file changed, 2575 deletions(-) delete mode 100644 lib/bcoin/script.js diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js deleted file mode 100644 index 9f01272d..00000000 --- a/lib/bcoin/script.js +++ /dev/null @@ -1,2575 +0,0 @@ -/** - * script.js - script interpreter for bcoin - * Copyright (c) 2014-2015, Fedor Indutny (MIT License) - * https://github.com/indutny/bcoin - */ - -var bcoin = require('../bcoin'); -var bn = require('bn.js'); -var constants = bcoin.protocol.constants; -var utils = require('./utils'); -var assert = bcoin.utils.assert; -var script = exports; - -/** - * Script - */ - -script.decode = function decode(buf) { - var opcodes = []; - var i = 0; - var b, opcode, len; - - if (!buf) - return []; - - while (i < buf.length) { - b = buf[i++]; - - // Next `b` bytes should be pushed to stack - if (b >= 0x01 && b <= 0x4b) { - opcodes.push(utils.slice(buf, i, i + b)); - i += b; - utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { - opcode: null, - len: b - }); - continue; - } - - // OP_0, OP_FALSE - // Special case: this is an empty array - // because it can be seen as an empty pushdata. - if (b === 0x00) { - opcodes.push(0); - continue; - } - - // OP_1, OP_TRUE, OP_2-OP_16 - // Special case: these get to be number - // literals. Note: 1negate is not included. - if (b >= 0x51 && b <= 0x60) { - opcodes.push(b - 0x50); - continue; - } - - opcode = constants.opcodesByVal[b]; - - if (i >= buf.length) { - opcodes.push(opcode || b); - continue; - } - - if (opcode === 'pushdata1') { - len = buf[i]; - i += 1; - opcodes.push(utils.slice(buf, i, i + len)); - i += len; - utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { - opcode: opcode, - len: len - }); - } else if (opcode === 'pushdata2') { - len = utils.readU16(buf, i); - i += 2; - opcodes.push(utils.slice(buf, i, i + len)); - i += len; - utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { - opcode: opcode, - len: len - }); - } else if (opcode === 'pushdata4') { - len = utils.readU32(buf, i); - i += 4; - opcodes.push(utils.slice(buf, i, i + len)); - i += len; - utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { - opcode: opcode, - len: len - }); - } else { - opcodes.push(opcode || b); - } - } - - utils.hidden(opcodes, '_raw', utils.slice(buf)); - - return opcodes; -}; - -script.encode = function encode(s) { - var opcodes = constants.opcodes; - var i = 0; - var instr; - var total = 0; - var off = 0; - var res; - - if (!s) - return new Buffer([]); - - if (s._raw) - return s._raw; - - for (i = 0; i < s.length; i++) { - instr = s[i]; - - if (Buffer.isBuffer(instr)) { - if (instr.pushdata) { - if (instr.pushdata.opcode === null) { - total += 1 + instr.length; - } else if (instr.pushdata.opcode === 'pushdata1') { - total += 1 + 1 + instr.length; - } else if (instr.pushdata.opcode === 'pushdata2') { - total += 1 + 2 + instr.length; - } else if (instr.pushdata.opcode === 'pushdata4') { - total += 1 + 4 + instr.length; - } - continue; - } - if (instr.length === 0) { - total += 1; - } else if (1 <= instr.length && instr.length <= 0x4b) { - total += 1 + instr.length; - } else if (instr.length <= 0xff) { - total += 1 + 1 + instr.length; - } else if (instr.length <= 0xffff) { - total += 1 + 2 + instr.length; - } else { - total += 1 + 4 + instr.length; - } - continue; - } - - total += 1; - } - - res = new Buffer(total); - - for (i = 0; i < s.length; i++) { - instr = s[i]; - - assert(!Array.isArray(instr)); - - // Push value to stack - if (Buffer.isBuffer(instr)) { - // Check for nonstandard pushdatas that - // may have been decoded from before. - if (instr.pushdata) { - if (instr.pushdata.opcode === null) { - res[off++] = instr.pushdata.len; - off += instr.copy(res, off, 0, instr.length); - } else if (instr.pushdata.opcode === 'pushdata1') { - res[off++] = opcodes.pushdata1; - res[off++] = instr.pushdata.len; - off += instr.copy(res, off, 0, instr.length); - } else if (instr.pushdata.opcode === 'pushdata2') { - res[off++] = opcodes.pushdata2; - off += utils.writeU16(res, instr.pushdata.len, off); - off += instr.copy(res, off, 0, instr.length); - } else if (instr.pushdata.opcode === 'pushdata4') { - res[off++] = opcodes.pushdata4; - off += utils.writeU32(res, instr.pushdata.len, off); - off += instr.copy(res, off, 0, instr.length); - } - continue; - } - if (instr.length === 0) { - res[off++] = opcodes['0']; - } else if (1 <= instr.length && instr.length <= 0x4b) { - res[off++] = instr.length; - off += instr.copy(res, off, 0, instr.length); - } else if (instr.length <= 0xff) { - res[off++] = opcodes.pushdata1; - res[off++] = instr.length; - off += instr.copy(res, off, 0, instr.length); - } else if (instr.length <= 0xffff) { - res[off++] = opcodes.pushdata2; - off += utils.writeU16(res, instr.length, off); - off += instr.copy(res, off, 0, instr.length); - } else { - res[off++] = opcodes.pushdata4; - off += utils.writeU32(res, instr.length, off); - off += instr.copy(res, off, 0, instr.length); - } - continue; - } - - assert(opcodes[instr] != null || typeof instr === 'number'); - - res[off++] = opcodes[instr] || instr; - } - - assert(off === res.length); - - return res; -}; - -// Witnesses aren't scripts, but we still -// want to convert [0] to OP_0, [0xff] to 1negate, etc. -script.decodeWitness = function decodeWitness(witness) { - var s = []; - var chunk, i; - - for (i = 0; i < witness.length; i++) { - chunk = witness[i]; - - if (chunk.length <= 1) { - if (chunk.length === 0) - op = new Buffer([]); - else if (chunk[0] >= 1 && chunk <= 16) - op = chunk[0]; - else if (chunk[0] === 0xff) - op = '1negate'; - } - - s.push(op); - } - - return s; -}; - -script.encodeWitness = function encodeWitness(s) { - var witness = []; - var chunk, i; - - for (i = 0; i < s.length; i++) { - chunk = s[i]; - - if (chunk === 0) - chunk = new Buffer([]); - else if (chunk >= 1 && chunk <= 16) - chunk = new Buffer([chunk]); - else if (chunk === '1negate') - chunk = new Buffer([0xff]); - - assert(Buffer.isBuffer(chunk)); - - witness.push(chunk); - } - - return witness; -}; - -script.normalize = function normalize(s) { - var i, op; - - // Remove OP_ prefixes and lowercase - for (i = 0; i < s.length; i++) { - op = s[i]; - if (typeof op === 'string') { - op = op.toLowerCase(); - if (op.indexOf('op_') === 0) - op = op.slice(3); - } - s[i] = op; - } - - // Convert OP_0 to array, convert OP_1-OP_16 - // to number literals, convert -1 to OP_1NEGATE. - // Convert hex strings to arrays. - for (i = 0; i < s.length; i++) { - op = s[i]; - - if (op === '-1' || op === -1) - op = '1negate'; - else if (op === '0' || op === 0 || op === 'false') - op = []; - else if (op === 'true') - op = 1; - else if (+op >= 1 && +op <= 16) - op = +op; - - if (typeof op === 'string' && constants.opcodes[op] == null) { - if (op[0] === '[') - op = op.slice(1, -1); - op = new Buffer(op, 'hex'); - } - - s[i] = op; - } - - return s; -}; - -script.verify = function verify(input, witness, output, tx, i, flags) { - var copy, res, raw, redeem, hadWitness; - var stack = []; - - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (flags & constants.flags.VERIFY_SIGPUSHONLY) { - if (!script.isPushOnly(input)) - return false; - } - - // Execute the input script - script.execute(input, stack, tx, i, flags, 0); - - // Copy the stack for P2SH - if (flags & constants.flags.VERIFY_P2SH) - copy = stack.slice(); - - // Execute the previous output script - res = script.execute(output, stack, tx, i, flags, 0); - - // Verify the script did not fail as well as the stack values - if (!res || stack.length === 0 || !script.bool(stack.pop())) - return false; - - if ((flags & constants.flags.VERIFY_WITNESS) - && script.isWitnessProgram(output)) { - hadWitness = true; - - // Input script must be empty. - if (input.length !== 0) - return false; - - // Verify the program in the output script - if (!script.verifyProgram(witness, output, tx, i, flags)) - return false; - - // Force a cleanstack - stack.length = 0; - } - - // If the script is P2SH, execute the real output script - if ((flags & constants.flags.VERIFY_P2SH) && script.isScripthash(output)) { - // P2SH can only have push ops in the scriptSig - if (!script.isPushOnly(input)) - return false; - - // Reset the stack - stack = copy; - - // Stack should not be empty at this point - if (stack.length === 0) - return false; - - // Grab the real redeem script - raw = stack.pop(); - - if (!Buffer.isBuffer(raw)) - return false; - - redeem = script.decode(raw); - - // Execute the redeem script - res = script.execute(redeem, stack, tx, i, flags, 0); - - // Verify the script did not fail as well as the stack values - if (!res || stack.length === 0 || !script.bool(stack.pop())) - return false; - - if ((flags & constants.flags.VERIFY_WITNESS) - && script.isWitnessProgram(redeem)) { - hadWitness = true; - - // Input script must be exactly one push of the redeem script. - if (!(input.length === 1 && utils.isEqual(input[0], raw))) - return false; - - // Verify the program in the redeem script - if (!script.verifyProgram(witness, redeem, tx, i, flags)) - return false; - - // Force a cleanstack - stack.length = 0; - } - } - - // Ensure there is nothing left on the stack - if (flags & constants.flags.VERIFY_CLEANSTACK) { - assert((flags & constants.flags.VERIFY_P2SH) !== 0); - // assert((flags & constants.flags.VERIFY_WITNESS) !== 0); - if (stack.length !== 0) - return false; - } - - // If we had a witness but no witness program, fail. - if (flags & constants.flags.VERIFY_WITNESS) { - assert((flags & constants.flags.VERIFY_P2SH) !== 0); - if (!hadWitness && witness.length > 0) - return false; - } - - return true; -}; - -script.verifyProgram = function verifyProgram(witness, output, tx, i, flags) { - var program, witnessScript, redeem, stack, j; - - assert((flags & constants.flags.VERIFY_WITNESS) !== 0); - assert(script.isWitnessProgram(output)); - - program = script.getWitnessProgram(output); - - // Failure on version=0 (bad program data length) - if (!program.type) { - utils.debug('Malformed witness program.'); - return false; - } - - if (program.version > 0) { - utils.debug('Unknown witness program version: %s', program.version); - // Anyone can spend (we can return true here - // if we want to always relay these transactions). - // Otherwise, if we want to act like an "old" - // implementation and only accept them in blocks, - // we can use the regalar output script which will - // succeed in a block, but fail in the mempool - // due to VERIFY_CLEANSTACK. - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) - return false; - return true; - } - - stack = witness.slice(); - - if (program.type === 'witnesspubkeyhash') { - if (stack.length !== 2) - return false; - - redeem = script.createPubkeyhash(program.data); - } else if (program.type === 'witnessscripthash') { - if (stack.length === 0) - return false; - - witnessScript = stack.pop(); - - if (!utils.isEqual(utils.sha256(witnessScript), program.data)) - return false; - - redeem = script.decode(witnessScript); - } else { - assert(false); - } - - for (j = 0; j < stack.length; j++) { - if (stack[j].length > constants.script.maxSize) - return false; - } - - res = script.execute(redeem, stack, tx, i, flags, 1); - - // Verify the script did not fail as well as the stack values - if (!res || stack.length === 0 || !script.bool(stack.pop())) - return false; - - // Witnesses always require cleanstack - if (stack.length !== 0) - return false; - - return true; -}; - -script.getSubscript = function getSubscript(s, lastSep) { - var i, res; - - if (!s) - return []; - - if (lastSep == null) - lastSep = -1; - - assert(lastSep <= 0 || s[lastSep] === 'codeseparator'); - - res = []; - for (i = lastSep + 1; i < s.length; i++) { - if (s[i] !== 'codeseparator') - res.push(s[i]); - } - - if (res.length === s.length) - return s; - - return res; -}; - -script.concat = function concat(scripts) { - var s = []; - var i; - - s = s.concat(scripts[0]); - - for (i = 1; i < scripts.length; i++) { - s.push('codeseparator'); - s = s.concat(scripts[i]); - } - - return s; -}; - -script.checksig = function checksig(msg, sig, key, flags) { - var historical = false; - - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (!Buffer.isBuffer(sig)) - return false; - - if (sig.length === 0) - return false; - - // Attempt to normalize the signature - // length before passing to elliptic. - // Note: We only do this for historical data! - // https://github.com/indutny/elliptic/issues/78 - if (!((flags & constants.flags.VERIFY_DERSIG) - || (flags & constants.flags.VERIFY_LOW_S) - || (flags & constants.flags.VERIFY_STRICTENC))) { - historical = true; - } - - return bcoin.ec.verify(msg, sig.slice(0, -1), key, historical); -}; - -script.sign = function sign(msg, key, type) { - var sig = bcoin.ec.sign(msg, key); - - // Add the sighash type as a single byte - // to the signature. - sig = Buffer.concat([sig, new Buffer([type])]); - - return sig; -}; - -script._next = function _next(to, s, pc) { - var depth = 0; - var o; - - while (s[pc]) { - o = s[pc]; - - if (o === 'if' || o === 'notif') - depth++; - else if (o === 'else') - depth--; - else if (o === 'endif') - depth--; - - if (depth < 0) - break; - - if (depth === 0 && o === to) - return pc; - - if (o === 'else') - depth++; - - pc++; - } - - return -1; -}; - -script.execute = function execute(data, stack, tx, index, flags, version, recurse) { - try { - return script._execute(data, stack, tx, index, flags, version, recurse); - } catch (e) { - utils.debug('Script error: %s.', e.message); - return false; - } -}; - -script._execute = function _execute(data, stack, tx, index, flags, version, recurse) { - var s = data.slice(); - var lastSep = -1; - var pc = 0; - var o, val; - var if_, else_, endif; - var v1, v2, v3, v4; - var n, n1, n2, n3; - var res; - var key, sig, type, subscript, hash; - var keys, i, j, m; - var succ; - var locktime, threshold; - var evalScript; - - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (s.length > constants.script.maxOps) - return false; - - if (!stack.alt) - stack.alt = []; - - for (pc = 0; pc < s.length; pc++) { - o = s[pc]; - - if (Buffer.isBuffer(o)) { - if (o.length > constants.script.maxPush) - return false; - if (!script.checkPush(o, flags)) - return false; - stack.push(o); - continue; - } - - if (o === 0) { - stack.push(new Buffer([])); - continue; - } - - if (o >= 1 && o <= 16) { - stack.push(new Buffer([o])); - continue; - } - - switch (o) { - case 'nop': - case 'nop1': - case 'nop4': - case 'nop5': - case 'nop6': - case 'nop7': - case 'nop8': - case 'nop9': - case 'nop10': { - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) - return false; - break; - } - case '1negate': { - stack.push(new Buffer([0xff])); - break; - } - case 'if': - case 'notif': { - if (stack.length < 1) - return false; - val = script.bool(stack.pop()); - if (o === 'notif') - val = !val; - if_ = pc; - else_ = script._next('else', s, pc); - endif = script._next('endif', s, pc); - // Splice out the statement blocks we don't need - if (val) { - if (endif === -1) - return false; - if (else_ === -1) { - s.splice(endif, 1); - s.splice(if_, 1); - } else { - s.splice(else_, (endif - else_) + 1); - s.splice(if_, 1); - } - } else { - if (endif === -1) - return false; - if (else_ === -1) { - s.splice(if_, (endif - if_) + 1); - } else { - s.splice(endif, 1); - s.splice(if_, (else_ - if_) + 1); - } - } - // Subtract one since we removed the if/notif opcode - pc--; - break; - } - case 'else': { - return false; - } - case 'endif': { - return false; - } - case 'verify': { - if (stack.length === 0) - return false; - if (!script.bool(stack.pop())) - return false; - break; - } - case 'return': { - return false; - } - case 'toaltstack': { - if (stack.length === 0) - return false; - stack.alt.push(stack.pop()); - break; - } - case 'fromaltstack': { - if (stack.alt.length === 0) - return false; - stack.push(stack.alt.pop()); - break; - } - case 'ifdup': { - if (stack.length === 0) - return false; - if (script.bool(stack[stack.length - 1])) - stack.push(script.array(stack[stack.length - 1])); - break; - } - case 'depth': { - stack.push(script.array(stack.length)); - break; - } - case 'drop': { - if (stack.length === 0) - return false; - stack.pop(); - break; - } - case 'dup': { - if (stack.length === 0) - return false; - stack.push(stack[stack.length - 1]); - break; - } - case 'nip': { - if (stack.length < 2) - return false; - stack.splice(stack.length - 2, 1); - break; - } - case 'over': { - if (stack.length < 2) - return false; - stack.push(stack[stack.length - 2]); - break; - } - case 'pick': - case 'roll': { - if (stack.length < 2) - return false; - val = stack.pop(); - n = script.num(val, flags).toNumber(); - if (n < 0 || n >= stack.length) - return false; - val = stack[-n - 1]; - if (o === 'roll') - stack.splice(stack.length - n - 1, 1); - stack.push(val); - break; - } - case 'rot': { - if (stack.length < 3) - return false; - v3 = stack[stack.length - 3]; - v2 = stack[stack.length - 2]; - v1 = stack[stack.length - 1]; - stack[stack.length - 3] = v2; - stack[stack.length - 2] = v3; - v2 = stack[stack.length - 2]; - stack[stack.length - 2] = v1; - stack[stack.length - 1] = v2; - break; - } - case 'swap': { - if (stack.length < 2) - return false; - v1 = stack[stack.length - 2]; - v2 = stack[stack.length - 1]; - stack[stack.length - 2] = v2; - stack[stack.length - 1] = v1; - break; - } - case 'tuck': { - if (stack.length < 2) - return false; - stack.splice(stack.length - 2, 0, stack[stack.length - 1]); - break; - } - case '2drop': { - if (stack.length < 2) - return false; - stack.pop(); - stack.pop(); - break; - } - case '2dup': { - if (stack.length < 2) - return false; - v1 = stack[stack.length - 2]; - v2 = stack[stack.length - 1]; - stack.push(v1); - stack.push(v2); - break; - } - case '3dup': { - if (stack.length < 3) - return false; - v1 = stack[stack.length - 3]; - v2 = stack[stack.length - 2]; - v3 = stack[stack.length - 1]; - stack.push(v1); - stack.push(v2); - stack.push(v3); - break; - } - case '2over': { - if (stack.length < 4) - return false; - v1 = stack[stack.length - 4]; - v2 = stack[stack.length - 3]; - stack.push(v1); - stack.push(v2); - break; - } - case '2rot': { - if (stack.length < 6) - return false; - v1 = stack[stack.length - 6]; - v2 = stack[stack.length - 5]; - stack.splice(stack.length - 6, 2); - stack.push(v1); - stack.push(v2); - break; - } - case '2swap': { - if (stack.length < 4) - return false; - v4 = stack[stack.length - 4]; - v3 = stack[stack.length - 3]; - v2 = stack[stack.length - 2]; - v1 = stack[stack.length - 1]; - stack[stack.length - 4] = v2; - stack[stack.length - 2] = v4; - stack[stack.length - 3] = v1; - stack[stack.length - 1] = v3; - break; - } - case 'size': { - if (stack.length < 1) - return false; - stack.push(script.array(stack[stack.length - 1].length || 0)); - break; - } - case '1add': - case '1sub': - case 'negate': - case 'abs': - case 'not': - case '0notequal': { - if (stack.length < 1) - return false; - n = script.num(stack.pop(), flags); - switch (o) { - case '1add': - n.iadd(1); - break; - case '1sub': - n.isub(1); - break; - case 'negate': - n = n.neg(); - break; - case 'abs': - if (n.cmpn(0) < 0) - n = n.neg(); - break; - case 'not': - n = n.cmpn(0) === 0; - break; - case '0notequal': - n = n.cmpn(0) !== 0; - break; - default: - return false; - } - if (typeof n === 'boolean') - n = new bn(n ? 1 : 0, 'le'); - stack.push(script.array(n)); - break; - } - case 'add': - case 'sub': - case 'booland': - case 'boolor': - case 'numequal': - case 'numequalverify': - case 'numnotequal': - case 'lessthan': - case 'greaterthan': - case 'lessthanorequal': - case 'greaterthanorequal': - case 'min': - case 'max': { - switch (o) { - case 'add': - case 'sub': - case 'booland': - case 'boolor': - case 'numequal': - case 'numequalverify': - case 'numnotequal': - case 'lessthan': - case 'greaterthan': - case 'lessthanorequal': - case 'greaterthanorequal': - case 'min': - case 'max': - if (stack.length < 2) - return false; - n2 = script.num(stack.pop(), flags); - n1 = script.num(stack.pop(), flags); - n = new bn(0, 'le'); - switch (o) { - case 'add': - n = n1.add(n2); - break; - case 'sub': - n = n1.sub(n2); - break; - case 'booland': - n = n1.cmpn(0) !== 0 && n2.cmpn(0) !== 0; - break; - case 'boolor': - n = n1.cmpn(0) !== 0 || n2.cmpn(0) !== 0; - break; - case 'numequal': - n = n1.cmp(n2) === 0; - break; - case 'numequalverify': - n = n1.cmp(n2) === 0; - break; - case 'numnotequal': - n = n1.cmp(n2) !== 0; - break; - case 'lessthan': - n = n1.cmp(n2) < 0; - break; - case 'greaterthan': - n = n1.cmp(n2) > 0; - break; - case 'lessthanorequal': - n = n1.cmp(n2) <= 0; - break; - case 'greaterthanorequal': - n = n1.cmp(n2) >= 0; - break; - case 'min': - n = n1.cmp(n2) < 0 ? n1 : n2; - break; - case 'max': - n = n1.cmp(n2) > 0 ? n1 : n2; - break; - default: - return false; - } - if (typeof n === 'boolean') - n = new bn(n ? 1 : 0, 'le'); - res = script.bool(n); - if (o === 'numequalverify') { - if (!res) - return false; - } else { - stack.push(script.array(n)); - } - break; - case 'within': - if (stack.length < 3) - return false; - n3 = script.num(stack.pop(), flags); - 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([])); - break; - } - - break; - } - case 'codeseparator': { - lastSep = pc; - break; - } - case 'ripemd160': { - if (stack.length === 0) - return false; - stack.push(utils.ripemd160(stack.pop())); - break; - } - case 'sha1': { - if (stack.length === 0) - return false; - stack.push(utils.sha1(stack.pop())); - break; - } - case 'sha256': { - if (stack.length === 0) - return false; - stack.push(utils.sha256(stack.pop())); - break; - } - case 'hash256': { - if (stack.length === 0) - return false; - stack.push(utils.dsha256(stack.pop())); - break; - } - case 'hash160': { - if (stack.length === 0) - return false; - stack.push(utils.ripesha(stack.pop())); - break; - } - case 'equalverify': - case 'equal': { - if (stack.length < 2) - return false; - res = utils.isEqual(stack.pop(), stack.pop()); - if (o === 'equalverify') { - if (!res) - return false; - } else { - stack.push(res ? new Buffer([1]) : new Buffer([])); - } - break; - } - case 'checksigverify': - case 'checksig': { - if (!tx || stack.length < 2) - return false; - - key = stack.pop(); - sig = stack.pop(); - - if (!script.isValidKey(key, flags)) - return false; - - if (!script.isValidSignature(sig, flags)) - return false; - - type = sig[sig.length - 1]; - - subscript = script.getSubscript(data, lastSep); - script.removeData(subscript, sig); - - hash = tx.signatureHash(index, subscript, type, version); - - res = script.checksig(hash, sig, key, flags); - if (o === 'checksigverify') { - if (!res) - return false; - } else { - stack.push(res ? new Buffer([1]) : new Buffer([])); - } - - break; - } - case 'checkmultisigverify': - case 'checkmultisig': { - if (!tx || stack.length < 4) - return false; - - n = script.num(stack.pop(), flags).toNumber(); - - if (!(n >= 1 && n <= 15)) - return false; - - if (stack.length < n + 1) - return false; - - keys = []; - for (i = 0; i < n; i++) { - key = stack.pop(); - - if (!script.isValidKey(key, flags)) - return false; - - keys.push(key); - } - - m = script.num(stack.pop(), flags).toNumber(); - - if (!(m >= 1 && m <= n)) - return false; - - if (stack.length < m) - return false; - - subscript = script.getSubscript(data, lastSep); - - for (i = 0; i < m; i++) { - sig = stack[stack.length - 1 - i]; - script.removeData(subscript, sig); - } - - succ = 0; - for (i = 0, j = 0; i < m; i++) { - sig = stack.pop(); - - if (!script.isValidSignature(sig, flags)) - return false; - - type = sig[sig.length - 1]; - - hash = tx.signatureHash(index, subscript, type, version); - - res = false; - for (; !res && j < n; j++) - res = script.checksig(hash, sig, keys[j], flags); - - if (res) - succ++; - } - - if (stack.length < 1) - return false; - - val = stack.pop(); - - if (flags & constants.flags.VERIFY_NULLDUMMY) { - if (!script.isDummy(val)) - return false; - } - - res = succ >= m; - - if (!res) - utils.debug('checkmultisig failed: succ: %d, m: %d', succ, m); - - if (o === 'checkmultisigverify') { - if (!res) - return false; - } else { - stack.push(res ? new Buffer([1]) : new Buffer([])); - } - - break; - } - case 'checklocktimeverify': { - // OP_CHECKLOCKTIMEVERIFY = OP_NOP2 - if (!(flags & constants.flags.VERIFY_CHECKLOCKTIMEVERIFY)) { - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) - return false; - break; - } - - if (!tx || stack.length === 0) - return false; - - locktime = stack[stack.length - 1]; - - if (!Buffer.isBuffer(locktime)) - return false; - - // NOTE: Bitcoind accepts 5 byte locktimes. - // 4 byte locktimes become useless in 2106 - // (will people still be using bcoin then?). - locktime = script.num(locktime, flags, 4).toNumber(); - - if (locktime < 0) - return false; - - threshold = constants.locktimeThreshold; - if (!( - (tx.locktime < threshold && locktime < threshold) - || (tx.locktime >= threshold && locktime >= threshold) - )) { - return false; - } - - if (locktime > tx.locktime) - return false; - - if (tx.inputs[index].sequence === 0xffffffff) - return false; - - break; - } - case 'checksequenceverify': { - // OP_CHECKSEQUENCEVERIFY = OP_NOP3 - if (!(flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY)) { - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) - return false; - break; - } - - if (!tx || stack.length === 0) - return false; - - locktime = stack[stack.length - 1]; - - if (!Buffer.isBuffer(locktime)) - return false; - - // NOTE: Bitcoind accepts 5 byte locktimes. - // 4 byte locktimes become useless in 2106 - // (will people still be using bcoin then?). - locktime = script.num(locktime, flags, 4).toNumber(); - - if (locktime < 0) - return false; - - if ((locktime & constants.sequenceLocktimeDisableFlag) !== 0) - break; - - if (!script.checkSequence(locktime, tx, index)) - return false; - - break; - } - default: { - // Unknown operation - utils.debug('Unknown opcode "%s" at offset %d', o, pc); - return false; - } - } - } - - if (stack.length + stack.alt.length > constants.script.maxStack) - return false; - - return true; -}; - -script.checkSequence = function checkSequence(sequence, tx, i) { - var txSequence = tx.inputs[i].sequence; - var locktimeMask, txSequenceMasked, sequenceMasked; - - if (tx.version < 2) - return false; - - if (txSequence & constants.sequenceLocktimeDisableFlag) - return false; - - locktimeMask = constants.sequenceLocktimeTypeFlag - | constants.sequenceLocktimeMask; - txSequenceMasked = txSequence & locktimeMask; - sequenceMasked = sequence & locktimeMask; - - if (!( - (txSequenceMasked < constants.sequenceLocktimeTypeFlag - && sequenceMasked < constants.sequenceLocktimeTypeFlag) - || (txSequenceMasked >= constants.sequenceLocktimeTypeFlag - && sequenceMasked >= constants.sequenceLocktimeTypeFlag) - )) { - return false; - } - - if (sequenceMasked > txSequenceMasked) - return false; - - return true; -}; - -script.bool = function bool(value) { - var i; - - // Should never happen: - // if (typeof value === 'boolean') - // return value; - - // Should never happen: - // if (utils.isFinite(value)) - // return value !== 0; - - if (value instanceof bn) - return value.cmpn(0) !== 0; - - assert(Buffer.isBuffer(value)); - - for (i = 0; i < value.length; i++) { - if (value[i] !== 0) { - // Cannot be negative zero - if (i === value.length - 1 && value[i] === 0x80) - return false; - return true; - } - } - - return false; -}; - -script.num = function num(value, flags, size) { - assert(Buffer.isBuffer(value)); - - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (size == null) - size = 4; - - if (value.length > size) - throw new Error('Script number overflow.'); - - if ((flags & constants.flags.VERIFY_MINIMALDATA) && 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. 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)) - throw new Error('Non-minimally encoded script number.'); - } - } - - // 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'); - value = value.notn(value.bitLength()).addn(1).neg(); - } - } else { - value = new bn(value, 'le'); - } - - return value; -}; - -script.array = function(value) { - if (Buffer.isBuffer(value)) - return value; - - 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(value.bitLength()).subn(1); - } - - if (value.cmpn(0) === 0) - return new Buffer([]); - - return new Buffer(value.toArray('le')); -}; - -script.removeData = function removeData(s, data) { - for (var i = s.length - 1; i >= 0; i--) { - if (utils.isEqual(s[i], data)) - s.splice(i, 1); - } -}; - -script.checkPush = function checkPush(value, flags) { - var op; - - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (!(flags & constants.flags.VERIFY_MINIMALDATA)) - return true; - - if (!value.pushdata) - return true; - - op = value.pushdata.opcode - - if (!op) - op = value.pushdata.len; - - if (value.length === 1 && value[0] === 0) - return false; - - if (value.length === 1 && value[0] >= 1 && value[0] <= 16) - return false; - - if (value.length === 1 && value[0] === 0xff) - return false; - - if (value.length <= 75) - return op === value.length; - - if (value.length <= 255) - return op === 'pushdata1'; - - if (value.length <= 65535) - return op === 'pushdata2'; - - return true; -}; - -script.createPubkey = function createPubkey(key) { - return [key, 'checksig']; -}; - -script.createPubkeyhash = function createPubkeyhash(hash) { - return [ - 'dup', - 'hash160', - hash, - 'equalverify', - 'checksig' - ]; -}; - -script.createMultisig = function createMultisig(keys, m, n) { - if (keys.length !== n) - throw new Error(n + ' keys are required to generate multisig script'); - - assert(m >= 1 && m <= n); - assert(n >= 1 && n <= 15); - - return [m].concat( - utils.sortKeys(keys), - [n, 'checkmultisig'] - ); -}; - -script.createScripthash = function createScripthash(hash) { - return [ - 'hash160', - hash, - 'equal' - ]; -}; - -script.createNulldata = function createNulldata(flags) { - return [ - 'return', - flags - ]; -}; - -script.getRedeem = function getRedeem(s) { - var raw, redeem; - - raw = s[s.length - 1]; - - if (!Buffer.isBuffer(raw)) - return; - - if (raw._redeem) - return raw._redeem; - - redeem = script.decode(raw); - - utils.hidden(redeem, '_raw', raw); - utils.hidden(raw, '_redeem', redeem); - - return redeem; -}; - -script.getType = -script.getOutputType = function getOutputType(s) { - var program; - - if (script.isCommitment(s)) - return 'commitment'; - - if (script.isWitnessProgram(s)) { - if (script.isWitnessPubkeyhash(s)) - return 'witnesspubkeyhash'; - if (script.isWitnessScripthash(s)) - return 'witnessscripthash'; - return 'unknown'; - } - - return (script.isPubkey(s) && 'pubkey') - || (script.isPubkeyhash(s) && 'pubkeyhash') - || (script.isMultisig(s) && 'multisig') - || (script.isScripthash(s) && 'scripthash') - || (script.isNulldata(s) && 'nulldata') - || 'unknown'; -}; - -script.isStandard = function isStandard(s) { - var type = script.getType(s); - var m, n; - - if (type === 'multisig') { - 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') { - if (script.getSize(s) > constants.script.maxOpReturnBytes) - return false; - } - - return type !== 'unknown'; -}; - -script.getSize = function getSize(s) { - if (s._raw) - return s._raw.length; - return script.encode(s).length; -}; - -// Legacy -script.size = script.getSize; - -script.isScript = function isScript(s) { - var i, b; - - if (!s) - return false; - - if (!Buffer.isBuffer(s)) - return false; - - s = script.decode(s); - - for (i = 0; i < s.length; i++) { - b = s[i]; - if (Buffer.isBuffer(b)) - continue; - if (constants.opcodes[b] == null) - return false; - } - - return true; -}; - -script._locktime = function _locktime(s) { - if (s.length < 2) - return; - - if (!Buffer.isBuffer(s[0])) - return; - - if (s[1] !== 'checklocktimeverify') - return; - - return s[0]; -}; - -script.isLocktime = function isLocktime(s) { - return script._locktime(s) != null; -}; - -script.getLocktime = function getLocktime(s) { - var locktime = script._locktime(s); - - if (!locktime) - return; - - try { - locktime = script.num(locktime, null, 4).toNumber(); - } catch (e) { - locktime = 0; - } - - if (locktime < constants.locktimeThreshold) - return { type: 'height', value: locktime }; - - return { type: 'time', value: locktime }; -}; - -script.getInputData = function getData(s, prev) { - var output, type; - - if (prev) { - output = script.getOutputData(prev); - output.side = 'input'; - - // We could call _getInputData, but - // we really only need the signatures. - if (output.type === 'pubkey') { - if (s.length >= 1) - output.signatures = [s[0] === 0 ? new Buffer([]) : s[0]]; - } else if (output.type === 'pubkeyhash') { - if (s.length >= 2) { - output.signatures = [s[0] === 0 ? new Buffer([]) : s[0]]; - output.keys = [s[1]]; - } - } else if (output.type === 'multisig') { - if (s.length >= 2) { - output.signatures = s.slice(1).map(function(sig) { - return sig === 0 ? new Buffer([]) : sig; - }); - } - } else if (output.type === 'scripthash') { - // Scripthash is the only case where - // we get more data from the input - // than the output. - output = script._getInputData(s, output.type); - } else { - output.signatures = script.getUnknownData(s).signatures; - } - - return output; - } - - if (script.isPubkeyInput(s)) - type = 'pubkey'; - else if (script.isPubkeyhashInput(s)) - type = 'pubkeyhash'; - else if (script.isMultisigInput(s)) - type = 'multisig'; - else if (script.isScripthashInput(s)) - type = 'scripthash'; - else - type = 'unknown'; - - return script._getInputData(s, type); -}; - -script._getInputData = function _getInputData(s, type) { - var sig, key, hash, raw, redeem, locktime, hash, address, input, output; - - assert(typeof type === 'string'); - - if (s.length === 0) { - return { - type: 'unknown', - side: 'input', - none: true - }; - } - - if (type === 'pubkey') { - sig = s[0]; - if (sig === 0) - sig = new Buffer([]); - return { - type: 'pubkey', - side: 'input', - signatures: [sig], - none: true - }; - } - - if (type === 'pubkeyhash') { - sig = s[0]; - if (sig === 0) - sig = new Buffer([]); - key = s[1]; - hash = bcoin.address.hash160(key); - address = bcoin.address.compileHash(hash, 'pubkeyhash'); - return { - type: 'pubkeyhash', - side: 'input', - signatures: [sig], - keys: [key], - hashes: [hash], - address: address, - addresses: [address] - }; - } - - if (type === 'multisig') { - sig = s.slice(1).map(function(sig) { - return sig === 0 ? new Buffer([]) : sig; - }); - return { - type: 'multisig', - side: 'input', - signatures: sig, - m: sig.length, - none: true - }; - } - - if (type === 'scripthash') { - raw = s[s.length - 1]; - redeem = script.decode(raw); - locktime = script.getLocktime(redeem); - hash = bcoin.address.hash160(raw); - address = bcoin.address.compileHash(hash, 'scripthash'); - output = script.getOutputData(redeem, true); - input = script._getInputData(s.slice(0, -1), output.type); - delete input.none; - return utils.merge(input, output, { - type: 'scripthash', - side: 'input', - subtype: output.type, - redeem: redeem, - address: address, - scriptHash: hash, - scriptAddress: address, - locktime: locktime - }); - } - - return script.getUnknownData(s); -}; - -script.getOutputData = function getOutputData(s, inScriptHash) { - var key, hash, address, mhash, maddress; - - if (script.isPubkey(s)) { - key = s[0]; - hash = bcoin.address.hash160(key); - // Convert p2pk to p2pkh addresses - address = bcoin.address.compileHash(hash, 'pubkeyhash'); - return { - type: 'pubkey', - side: 'output', - keys: [key], - hashes: [hash], - address: address, - addresses: [address] - }; - } - - if (script.isPubkeyhash(s)) { - hash = s[2]; - address = bcoin.address.compileHash(hash, 'pubkeyhash'); - return { - type: 'pubkeyhash', - side: 'output', - hashes: [hash], - address: address, - addresses: [address] - }; - } - - if (script.isMultisig(s)) { - key = s.slice(1, -2); - hash = key.map(function(key) { - return bcoin.address.hash160(key); - }); - // Convert bare multisig to p2pkh addresses - address = hash.map(function(hash) { - return bcoin.address.compileHash(hash, 'pubkeyhash'); - }); - // Convert bare multisig script to scripthash address - if (!inScriptHash) { - mhash = bcoin.address.hash160(s._raw || script.encode(s)); - maddress = bcoin.address.compileHash(mhash, 'scripthash'); - } - return { - type: 'multisig', - side: 'output', - keys: key, - hashes: hash, - address: maddress, - addresses: address, - m: s[0], - n: s[s.length - 2] - }; - } - - if (script.isScripthash(s)) { - hash = s[1]; - address = bcoin.address.compileHash(hash, 'scripthash'); - return { - type: 'scripthash', - side: 'output', - address: address, - scriptHash: hash, - scriptAddress: address - }; - } - - if (script.isNulldata(s)) { - return { - type: 'nulldata', - side: 'output', - nulldata: s[1], - text: s[1].toString('utf8') - }; - } - - return script.getUnknownData(s); -}; - -script.getUnknownData = function getUnknownData(s) { - var sig = []; - var key = []; - var hash, address, i; - - for (i = 0; i < s.length; i++) { - if (script.isSignatureEncoding(s[i])) - sig.push(s[i]); - else if (script.isKeyEncoding(s[i])) - key.push(s[i]); - } - - hash = key.map(function(key) { - return bcoin.address.hash160(key); - }); - - address = hash.map(function(hash) { - return bcoin.address.compileHash(hash, 'pubkeyhash'); - }); - - return { - type: 'unknown', - signatures: sig, - keys: key, - hashes: hash, - addresses: address, - none: key.length === 0 - }; -}; - -script.getInputAddress = function getInputAddress(s, prev, isWitness) { - if (prev) - return script.getOutputAddress(prev); - - if (script.isPubkeyInput(s)) - return; - - if (script.isPubkeyhashInput(s)) { - return bcoin.address.compileData(s[1], - isWitness ? 'witnesspubkeyhash' : 'pubkeyhash'); - } - - if (script.isMultisigInput(s)) - return; - - if (script.isScripthashInput(s)) { - return bcoin.address.compileData(s[s.length - 1], - isWitness ? 'witnessscripthash' : 'scripthash'); - } -}; - -script.getOutputAddress = function getOutputAddress(s) { - var program; - - if (script.isWitnessProgram(s)) { - program = script.getWitnessProgram(s); - if (!program.type || program.type === 'unknown') - return; - return bcoin.address.compileHash(program.data, program.type); - } - - // Convert p2pk to p2pkh addresses - if (script.isPubkey(s)) - return bcoin.address.compileData(s[0], 'pubkeyhash'); - - if (script.isPubkeyhash(s)) - return bcoin.address.compileHash(s[2], 'pubkeyhash') - - // Convert bare multisig to scripthash address - if (script.isMultisig(s)) { - s = s._raw || script.encode(s); - return bcoin.address.compileData(s, 'scripthash'); - } - - if (script.isScripthash(s)) - return bcoin.address.compileHash(s[1], 'scripthash'); -}; - -script.isPubkey = function isPubkey(s, key) { - var res; - - if (s.length !== 2) - return false; - - res = script.isKey(s[0]) && s[1] === 'checksig'; - - if (!res) - return false; - - if (key) { - if (!utils.isEqual(s[0], key)) - return false; - } - - return true; -}; - -script.isPubkeyhash = function isPubkeyhash(s, hash) { - var res; - - if (s.length !== 5) - return false; - - res = s[0] === 'dup' - && s[1] === 'hash160' - && script.isHash(s[2]) - && s[3] === 'equalverify' - && s[4] === 'checksig'; - - if (!res) - return false; - - if (hash) { - if (!utils.isEqual(s[2], hash)) - return false; - } - - return true; -}; - -script.isMultisig = function isMultisig(s, keys) { - var m, n, i, j; - var total = 0; - - if (s.length < 4) - return false; - - if (s[s.length - 1] !== 'checkmultisig') - return false; - - n = s[s.length - 2]; - - if (Buffer.isBuffer(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. - if (!(n >= 1 && n <= 15)) - return false; - - m = s[0]; - - if (Buffer.isBuffer(m)) { - if (m.length !== 0) - return false; - m = 0; - } - - if (typeof m !== 'number') - return false; - - if (!(m >= 1 && m <= n)) - return false; - - if (n + 3 !== s.length) - return false; - - for (i = 1; i < n + 1; i++) { - if (!script.isKey(s[i])) - return false; - } - - if (keys) { - 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 true; -}; - -script.isScripthash = function isScripthash(s, hash) { - var res; - - if (s.length !== 3) - return false; - - res = s[0] === 'hash160' - && script.isHash(s[1]) - && s[2] === 'equal'; - - if (!res) - return false; - - if (hash) { - if (!utils.isEqual(s[1], hash)) - return false; - } - - return true; -}; - -script.isNulldata = function isNulldata(s) { - var res; - - if (s.length !== 2) - return false; - - res = s[0] === 'return' && script.isData(s[1]); - - if (!res) - return false; - - return true; -}; - -script.isCommitment = function isCommitment(s) { - return s.length >= 2 - && s[0] === 'return' - && Buffer.isBuffer(s[1]) - && s[1].length === 36 - && utils.readU32BE(s[1], 0) === 0xaa21a9ed; -}; - -script.getCommitmentHash = function getCommitmentHash(s) { - if (!script.isCommitment(s)) - return; - - return s[1].slice(4, 36); -}; - -script.isWitnessProgram = function isWitnessProgram(s) { - if (s.length !== 2) - return false; - - if (typeof s[0] !== 'number') - return false; - - if (!Buffer.isBuffer(s[1])) - return false; - - return s[0] >= 0 && s[0] <= 16 - && s[1].length >= 2 && s[1].length <= 32; -}; - -script.getWitnessProgram = function getWitnessProgram(s) { - var version, data, type; - - if (!script.isWitnessProgram(s)) - return; - - version = s[0]; - data = s[1]; - - if (version > 0) { - // No interpretation of script (anyone can spend) - type = 'unknown'; - } else if (version === 0 && data.length === 20) { - type = 'witnesspubkeyhash'; - } else if (version === 0 && data.length === 32) { - type = 'witnessscripthash'; - } else { - // Fail on bad version=0 - type = null; - } - - return { - version: version, - type: type, - data: data - }; -}; - -script.isWitnessPubkeyhash = function isWitnessPubkeyhash(s) { - if (!script.isWitnessProgram(s)) - return false; - - return s[0] === 0 && s[1].length === 20; -}; - -script.isWitnessScripthash = function isWitnessScripthash(s) { - if (!script.isWitnessProgram(s)) - return false; - - return s[0] === 0 && s[1].length === 32; -}; - -script.createWitnessProgram = function createWitnessProgram(version, data) { - assert(typeof version === 'number' && version >= 0 && version <= 16); - assert(Buffer.isBuffer(data)); - assert(data.length === 20 || data.length === 32); - return [version, data]; -}; - -script.getInputType = function getInputType(s, prev, isWitness) { - var type; - - if (prev) - return script.getOutputType(prev); - - type = (script.isPubkeyInput(s) && 'pubkey') - || (script.isPubkeyhashInput(s) && 'pubkeyhash') - || (script.isMultisigInput(s) && 'multisig') - || (script.isScripthashInput(s) && 'scripthash') - || 'unknown'; - - if (isWitness) { - if (type === 'pubkeyhash') - return 'witnesspubkeyhash'; - if (type === 'scripthash') - return 'witnessscripthash'; - return 'unknown'; - } - - return type; -}; - -script.isPubkeyInput = function isPubkeyInput(s, key) { - if (s.length !== 1) - return false; - - if (!script.isSignature(s[0])) - return false; - - assert(!key); - - return true; -}; - -script.isPubkeyhashInput = function isPubkeyhashInput(s, key) { - if (s.length !== 2) - return false; - - if (!script.isSignature(s[0])) - return false; - - if (!script.isKey(s[1])) - return false; - - if (key) { - if (!utils.isEqual(s[1], key)) - return false; - } - - return true; -}; - -script.isMultisigInput = function isMultisigInput(s, keys) { - var i; - - // We need to rule out scripthash because - // it may look like multisig. This is - // strange because it's technically a - // recursive call. - if (script.isScripthashInput(s)) - return false; - - if (s.length < 3) - return false; - - if (s[0] !== 0 && !script.isDummy(s[0])) - return false; - - for (i = 1; i < s.length; i++) { - if (!script.isSignature(s[i])) - return false; - } - - assert(!keys); - - return true; -}; - -script.isScripthashInput = function isScripthashInput(s, redeem) { - var raw; - - // Grab the raw redeem script. - raw = s[s.length - 1]; - - // Need at least one data element with - // the redeem script. NOTE: NOT THE CASE FOR SEGWIT! - // if (s.length < 2) - if (s.length < 1) - return false; - - // Last data element should be an array - // for the redeem script. - if (!Buffer.isBuffer(raw)) - return false; - - // Check data against last array in case - // a raw redeem script was passed in. - if (redeem) - return utils.isEqual(redeem, raw); - - // Testing for scripthash inputs requires - // some evil magic to work. We do it by - // ruling things _out_. This test will not - // be correct 100% of the time. We rule - // out that the last data element is: a - // null dummy, a valid signature, a valid - // key, and we ensure that it is at least - // a script that does not use undefined - // opcodes. - if (script.isDummy(raw)) - return false; - - if (script.isSignatureEncoding(raw)) - return false; - - if (script.isKeyEncoding(raw)) - return false; - - if (!script.isScript(raw)) - return false; - - return true; -}; - -script.getCoinbaseData = function getCoinbaseData(s) { - var coinbase, flags; - - coinbase = { - script: s - }; - - if (Buffer.isBuffer(s[0]) && s[0].length <= 6) { - coinbase.height = new bn(s[0], 'le').toNumber(); - } else { - coinbase.height = -1; - } - - if (Buffer.isBuffer(s[1])) - coinbase.extraNonce = new bn(s[1], 'le'); - else - coinbase.extraNonce = new bn(0, 'le'); - - flags = s.slice(2).filter(function(chunk) { - return Buffer.isBuffer(chunk) && chunk.length !== 0; - }); - - coinbase.flags = flags; - coinbase.text = - flags.map(function(flag) { - return flag.toString('utf8'); - }).join('') - .replace(/[\u0000-\u0019\u007f-\u00ff]/g, ''); - - return coinbase; -}; - -// Detect script array types. Note: these functions -// are not mutually exclusive. Only use for -// verification, not detection. - -script.isHash = function isHash(hash) { - if (!Buffer.isBuffer(hash)) - return false; - - return hash.length === 20; -}; - -script.isKey = function isKey(key) { - if (!Buffer.isBuffer(key)) - return false; - - return key.length >= 33 && key.length <= 65; -}; - -script.isSignature = function isSignature(sig) { - if (!Buffer.isBuffer(sig)) - return false; - - return sig.length >= 9 && sig.length <= 73; -}; - -script.isDummy = function isDummy(data) { - if (!Buffer.isBuffer(data)) - return false; - - return data.length === 0; -}; - -script.isZero = function isZero(data) { - if (data === 0) - return true; - - return script.isDummy(data); -}; - -script.isData = function isData(data) { - if (!Buffer.isBuffer(data)) - return false; - - return data.length <= constants.script.maxOpReturn; -}; - -script.isValidKey = function isValidKey(key, flags) { - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (!Buffer.isBuffer(key)) - return false; - - if (flags & constants.flags.VERIFY_STRICTENC) { - if (!script.isKeyEncoding(key)) { - utils.debug('Script failed key encoding test.'); - return false; - } - } - - return true; -}; - -script.isKeyEncoding = function isKeyEncoding(key) { - if (!Buffer.isBuffer(key)) - return false; - - if (key.length < 33) - return false; - - if (key[0] === 0x04) { - if (key.length !== 65) - return false; - } else if (key[0] === 0x02 || key[0] === 0x03) { - if (key.length !== 33) - return false; - } else { - return false; - } - - return true; -}; - -script.isValidSignature = function isValidSignature(sig, flags) { - if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; - - if (!Buffer.isBuffer(sig)) - return false; - - // Allow empty sigs - if (sig.length === 0) - return true; - - if ((flags & constants.flags.VERIFY_DERSIG) - || (flags & constants.flags.VERIFY_LOW_S) - || (flags & constants.flags.VERIFY_STRICTENC)) { - if (!script.isSignatureEncoding(sig)) { - utils.debug('Script does not have a proper signature encoding.'); - return false; - } - } - - if (flags & constants.flags.VERIFY_LOW_S) { - if (!script.isLowDER(sig)) { - utils.debug('Script does not have a low DER.'); - return false; - } - } - - if (flags & constants.flags.VERIFY_STRICTENC) { - if (!script.isHashType(sig)) { - utils.debug('Script does not have a valid hash type.'); - return false; - } - } - - return true; -}; - -// https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki -/** - * A canonical signature exists of: <30> <02> <02> - * Where R and S are not negative (their first byte has its highest bit not set), and not - * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, - * in which case a single 0 byte is necessary and even required). - * - * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 - * - * This function is consensus-critical since BIP66. - */ -script.isSignatureEncoding = function isSignatureEncoding(sig) { - var lenR, lenS; - - if (!Buffer.isBuffer(sig)) - return false; - - // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] - // * total-length: 1-byte length descriptor of everything that follows, - // excluding the sighash byte. - // * R-length: 1-byte length descriptor of the R value that follows. - // * R: arbitrary-length big-endian encoded R value. It must use the shortest - // possible encoding for a positive integers (which means no null bytes at - // the start, except a single one when the next byte has its highest bit set). - // * S-length: 1-byte length descriptor of the S value that follows. - // * S: arbitrary-length big-endian encoded S value. The same rules apply. - // * sighash: 1-byte value indicating what data is hashed (not part of the DER - // signature) - - // Minimum and maximum size constraints. - if (sig.length < 9) - return false; - - if (sig.length > 73) - return false; - - // A signature is of type 0x30 (compound). - if (sig[0] !== 0x30) - return false; - - // Make sure the length covers the entire signature. - if (sig[1] !== sig.length - 3) - return false; - - // Extract the length of the R element. - lenR = sig[3]; - - // Make sure the length of the S element is still inside the signature. - if (5 + lenR >= sig.length) - return false; - - // Extract the length of the S element. - lenS = sig[5 + lenR]; - - // Verify that the length of the signature matches the sum of the length - // of the elements. - if (lenR + lenS + 7 !== sig.length) - return false; - - // Check whether the R element is an integer. - if (sig[2] !== 0x02) - return false; - - // Zero-length integers are not allowed for R. - if (lenR === 0) - return false; - - // Negative numbers are not allowed for R. - if (sig[4] & 0x80) - return false; - - // Null bytes at the start of R are not allowed, unless R would - // otherwise be interpreted as a negative number. - if (lenR > 1 && (sig[4] === 0x00) && !(sig[5] & 0x80)) - return false; - - // Check whether the S element is an integer. - if (sig[lenR + 4] !== 0x02) - return false; - - // Zero-length integers are not allowed for S. - if (lenS === 0) - return false; - - // Negative numbers are not allowed for S. - if (sig[lenR + 6] & 0x80) - return false; - - // Null bytes at the start of S are not allowed, unless S would otherwise be - // interpreted as a negative number. - if (lenS > 1 && (sig[lenR + 6] === 0x00) && !(sig[lenR + 7] & 0x80)) - return false; - - return true; -}; - -script.isHashType = function isHashType(sig) { - if (!Buffer.isBuffer(sig)) - return false; - - if (sig.length === 0) - return false; - - type = sig[sig.length - 1] & ~constants.hashType.anyonecanpay; - - if (!constants.hashTypeByVal[type]) - return false; - - return true; -}; - -script.isLowDER = function isLowDER(sig) { - if (!sig.s) { - if (!Buffer.isBuffer(sig)) - return false; - - if (!script.isSignatureEncoding(sig)) - return false; - - sig = sig.slice(0, -1); - } - - return bcoin.ec.isLowS(sig); -}; - -script.format = function format(s) { - var scripts = []; - - if (Array.isArray(s)) { - scripts.push(s); - } else if (s instanceof bcoin.input) { - scripts.push(s.script); - if (s.output) { - scripts.push(s.output.script); - if (script.isScripthash(s.output.script)) - scripts.push(script.getRedeem(s.script)); - } - } else if (s instanceof bcoin.output) { - scripts.push(s.script); - } - - return script.concat(scripts).map(function(chunk) { - if (Buffer.isBuffer(chunk)) - return '[' + utils.toHex(chunk) + ']'; - - if (typeof chunk === 'number') - return chunk + ''; - - return chunk; - }).join(' '); -}; - -script.isPushOnly = function isPushOnly(s) { - var i, op; - for (i = 0; i < s.length; i++) { - op = s[i]; - if (Buffer.isBuffer(op) || op === '1negate' || (op >= 0 && op <= 16)) - continue; - return false; - } - return true; -}; - -script.getSigops = function getSigops(s, accurate) { - var i, op; - var total = 0; - var lastOp = -1; - - for (i = 0; i < s.length; i++) { - op = s[i]; - - if (Buffer.isBuffer(op)) - continue; - - if (constants.opcodes[op] == null) - return 0; - - if (op === 'checksig' || op === 'checksigverify') { - total++; - } else if (op === 'checkmultisig' || op === 'checkmultisigverify') { - if (accurate && lastOp >= 1 && lastOp <= 16) - total += lastOp; - else - total += constants.script.maxPubkeysPerMultisig; - } - - lastOp = op; - } - - return total; -}; - -script.getArgs = function getArgs(s) { - var keys, m; - - if (script.isPubkey(s)) - return 1; - - if (script.isPubkeyhash(s)) - return 2; - - if (script.isMultisig(s)) { - keys = s.slice(1, -2); - m = s[0]; - if (keys.length < 1 || m < 1) - return -1; - return m + 1; - } - - if (script.isScripthash(s)) - return 1; - - if (script.isNulldata(s)) - return -1; - - if (script.isWitnessScripthash(s)) - return 1; - - if (script.isWitnessPubkeyhash(s)) - return 2; - - return -1; -};