/** * Copyright 2011 Google Inc. * Copyright 2012 Matt Corallo. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.feathercoin.core; import org.spongycastle.crypto.digests.RIPEMD160Digest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; import static com.google.feathercoin.core.Utils.bytesToHexString; /** * A chunk in a script */ class ScriptChunk { public boolean isOpCode; public byte[] data; public int startLocationInProgram; public ScriptChunk(boolean isOpCode, byte[] data, int startLocationInProgram) { this.isOpCode = isOpCode; this.data = data; this.startLocationInProgram = startLocationInProgram; } public boolean equalsOpCode(int opCode) { return isOpCode && data.length == 1 && (0xFF & data[0]) == opCode; } } /** * <p>Programs embedded inside transactions that control redemption of payments.</p> * * <p>Feathercoin transactions don't specify what they do directly. Instead <a href="https://en.feathercoin.it/wiki/Script">a * small binary stack language</a> is used to define programs that when evaluated return whether the transaction * "accepts" or rejects the other transactions connected to it.</p> * * <p>In SPV mode, scripts are not run, because that would require all transactions to be available and lightweight * clients don't have that data. In full mode, this class is used to run the interpreted language. It also has * static methods for building scripts.</p> */ public class Script { // Some constants used for decoding the scripts, copied from the reference client // push value public static final int OP_0 = 0x00; public static final int OP_FALSE = OP_0; public static final int OP_PUSHDATA1 = 0x4c; public static final int OP_PUSHDATA2 = 0x4d; public static final int OP_PUSHDATA4 = 0x4e; public static final int OP_1NEGATE = 0x4f; public static final int OP_RESERVED = 0x50; public static final int OP_1 = 0x51; public static final int OP_TRUE=OP_1; public static final int OP_2 = 0x52; public static final int OP_3 = 0x53; public static final int OP_4 = 0x54; public static final int OP_5 = 0x55; public static final int OP_6 = 0x56; public static final int OP_7 = 0x57; public static final int OP_8 = 0x58; public static final int OP_9 = 0x59; public static final int OP_10 = 0x5a; public static final int OP_11 = 0x5b; public static final int OP_12 = 0x5c; public static final int OP_13 = 0x5d; public static final int OP_14 = 0x5e; public static final int OP_15 = 0x5f; public static final int OP_16 = 0x60; // control public static final int OP_NOP = 0x61; public static final int OP_VER = 0x62; public static final int OP_IF = 0x63; public static final int OP_NOTIF = 0x64; public static final int OP_VERIF = 0x65; public static final int OP_VERNOTIF = 0x66; public static final int OP_ELSE = 0x67; public static final int OP_ENDIF = 0x68; public static final int OP_VERIFY = 0x69; public static final int OP_RETURN = 0x6a; // stack ops public static final int OP_TOALTSTACK = 0x6b; public static final int OP_FROMALTSTACK = 0x6c; public static final int OP_2DROP = 0x6d; public static final int OP_2DUP = 0x6e; public static final int OP_3DUP = 0x6f; public static final int OP_2OVER = 0x70; public static final int OP_2ROT = 0x71; public static final int OP_2SWAP = 0x72; public static final int OP_IFDUP = 0x73; public static final int OP_DEPTH = 0x74; public static final int OP_DROP = 0x75; public static final int OP_DUP = 0x76; public static final int OP_NIP = 0x77; public static final int OP_OVER = 0x78; public static final int OP_PICK = 0x79; public static final int OP_ROLL = 0x7a; public static final int OP_ROT = 0x7b; public static final int OP_SWAP = 0x7c; public static final int OP_TUCK = 0x7d; // splice ops public static final int OP_CAT = 0x7e; public static final int OP_SUBSTR = 0x7f; public static final int OP_LEFT = 0x80; public static final int OP_RIGHT = 0x81; public static final int OP_SIZE = 0x82; // bit logic public static final int OP_INVERT = 0x83; public static final int OP_AND = 0x84; public static final int OP_OR = 0x85; public static final int OP_XOR = 0x86; public static final int OP_EQUAL = 0x87; public static final int OP_EQUALVERIFY = 0x88; public static final int OP_RESERVED1 = 0x89; public static final int OP_RESERVED2 = 0x8a; // numeric public static final int OP_1ADD = 0x8b; public static final int OP_1SUB = 0x8c; public static final int OP_2MUL = 0x8d; public static final int OP_2DIV = 0x8e; public static final int OP_NEGATE = 0x8f; public static final int OP_ABS = 0x90; public static final int OP_NOT = 0x91; public static final int OP_0NOTEQUAL = 0x92; public static final int OP_ADD = 0x93; public static final int OP_SUB = 0x94; public static final int OP_MUL = 0x95; public static final int OP_DIV = 0x96; public static final int OP_MOD = 0x97; public static final int OP_LSHIFT = 0x98; public static final int OP_RSHIFT = 0x99; public static final int OP_BOOLAND = 0x9a; public static final int OP_BOOLOR = 0x9b; public static final int OP_NUMEQUAL = 0x9c; public static final int OP_NUMEQUALVERIFY = 0x9d; public static final int OP_NUMNOTEQUAL = 0x9e; public static final int OP_LESSTHAN = 0x9f; public static final int OP_GREATERTHAN = 0xa0; public static final int OP_LESSTHANOREQUAL = 0xa1; public static final int OP_GREATERTHANOREQUAL = 0xa2; public static final int OP_MIN = 0xa3; public static final int OP_MAX = 0xa4; public static final int OP_WITHIN = 0xa5; // crypto public static final int OP_RIPEMD160 = 0xa6; public static final int OP_SHA1 = 0xa7; public static final int OP_SHA256 = 0xa8; public static final int OP_HASH160 = 0xa9; public static final int OP_HASH256 = 0xaa; public static final int OP_CODESEPARATOR = 0xab; public static final int OP_CHECKSIG = 0xac; public static final int OP_CHECKSIGVERIFY = 0xad; public static final int OP_CHECKMULTISIG = 0xae; public static final int OP_CHECKMULTISIGVERIFY = 0xaf; // expansion public static final int OP_NOP1 = 0xb0; public static final int OP_NOP2 = 0xb1; public static final int OP_NOP3 = 0xb2; public static final int OP_NOP4 = 0xb3; public static final int OP_NOP5 = 0xb4; public static final int OP_NOP6 = 0xb5; public static final int OP_NOP7 = 0xb6; public static final int OP_NOP8 = 0xb7; public static final int OP_NOP9 = 0xb8; public static final int OP_NOP10 = 0xb9; public static final int OP_INVALIDOPCODE = 0xff; byte[] program; private int cursor; // The program is a set of byte[]s where each element is either [opcode] or [data, data, data ...] List<ScriptChunk> chunks; private final NetworkParameters params; // Only for internal use private Script() { params = null; } /** * Construct a Script using the given network parameters and a range of the programBytes array. * * @param params Network parameters. * @param programBytes Array of program bytes from a transaction. * @param offset How many bytes into programBytes to start reading from. * @param length How many bytes to read. * @throws ScriptException */ public Script(NetworkParameters params, byte[] programBytes, int offset, int length) throws ScriptException { this.params = params; parse(programBytes, offset, length); } /** * Returns the program opcodes as a string, for example "[1234] DUP HAHS160" */ public String toString() { StringBuffer buf = new StringBuffer(); for (ScriptChunk chunk : chunks) { if (chunk.isOpCode) { buf.append(getOpCodeName(chunk.data[0])); buf.append(" "); } else { // Data chunk buf.append("["); buf.append(bytesToHexString(chunk.data)); buf.append("] "); } } return buf.toString(); } /** * Converts the given OpCode into a string (eg "0", "PUSHDATA", or "NON_OP(10)") */ public static String getOpCodeName(byte opCode) { int opcode = opCode & 0xff; switch (opcode) { case OP_0: return "0"; case OP_PUSHDATA1: return "PUSHDATA1"; case OP_PUSHDATA2: return "PUSHDATA1"; case OP_PUSHDATA4: return "PUSHDATA4"; case OP_1NEGATE: return "1NEGATE"; case OP_RESERVED: return "RESERVED"; case OP_1: return "1"; case OP_2: return "2"; case OP_3: return "3"; case OP_4: return "4"; case OP_5: return "5"; case OP_6: return "6"; case OP_7: return "7"; case OP_8: return "8"; case OP_9: return "9"; case OP_10: return "10"; case OP_11: return "11"; case OP_12: return "12"; case OP_13: return "13"; case OP_14: return "14"; case OP_15: return "15"; case OP_16: return "16"; case OP_NOP: return "NOP"; case OP_VER: return "VER"; case OP_IF: return "IF"; case OP_NOTIF: return "NOTIF"; case OP_VERIF: return "VERIF"; case OP_VERNOTIF: return "VERNOTIF"; case OP_ELSE: return "ELSE"; case OP_ENDIF: return "ENDIF"; case OP_VERIFY: return "VERIFY"; case OP_RETURN: return "RETURN"; case OP_TOALTSTACK: return "TOALTSTACK"; case OP_FROMALTSTACK: return "FROMALTSTACK"; case OP_2DROP: return "2DROP"; case OP_2DUP: return "2DUP"; case OP_3DUP: return "3DUP"; case OP_2OVER: return "2OVER"; case OP_2ROT: return "2ROT"; case OP_2SWAP: return "2SWAP"; case OP_IFDUP: return "IFDUP"; case OP_DEPTH: return "DEPTH"; case OP_DROP: return "DROP"; case OP_DUP: return "DUP"; case OP_NIP: return "NIP"; case OP_OVER: return "OVER"; case OP_PICK: return "PICK"; case OP_ROLL: return "ROLL"; case OP_ROT: return "ROT"; case OP_SWAP: return "SWAP"; case OP_TUCK: return "TUCK"; case OP_CAT: return "CAT"; case OP_SUBSTR: return "SUBSTR"; case OP_LEFT: return "LEFT"; case OP_RIGHT: return "RIGHT"; case OP_SIZE: return "SIZE"; case OP_INVERT: return "INVERT"; case OP_AND: return "AND"; case OP_OR: return "OR"; case OP_XOR: return "XOR"; case OP_EQUAL: return "EQUAL"; case OP_EQUALVERIFY: return "EQUALVERIFY"; case OP_RESERVED1: return "RESERVED1"; case OP_RESERVED2: return "RESERVED2"; case OP_1ADD: return "1ADD"; case OP_1SUB: return "1SUB"; case OP_2MUL: return "2MUL"; case OP_2DIV: return "2DIV"; case OP_NEGATE: return "NEGATE"; case OP_ABS: return "ABS"; case OP_NOT: return "NOT"; case OP_0NOTEQUAL: return "0NOTEQUAL"; case OP_ADD: return "ADD"; case OP_SUB: return "SUB"; case OP_MUL: return "MUL"; case OP_DIV: return "DIV"; case OP_MOD: return "MOD"; case OP_LSHIFT: return "LSHIFT"; case OP_RSHIFT: return "RSHIFT"; case OP_BOOLAND: return "BOOLAND"; case OP_BOOLOR: return "BOOLOR"; case OP_NUMEQUAL: return "NUMEQUAL"; case OP_NUMEQUALVERIFY: return "NUMEQUALVERIFY"; case OP_NUMNOTEQUAL: return "NUMNOTEQUAL"; case OP_LESSTHAN: return "LESSTHAN"; case OP_GREATERTHAN: return "GREATERTHAN"; case OP_LESSTHANOREQUAL: return "LESSTHANOREQUAL"; case OP_GREATERTHANOREQUAL: return "GREATERTHANOREQUAL"; case OP_MIN: return "MIN"; case OP_MAX: return "MAX"; case OP_WITHIN: return "WITHIN"; case OP_RIPEMD160: return "RIPEMD160"; case OP_SHA1: return "SHA1"; case OP_SHA256: return "SHA256"; case OP_HASH160: return "HASH160"; case OP_HASH256: return "HASH256"; case OP_CODESEPARATOR: return "CODESEPARATOR"; case OP_CHECKSIG: return "CHECKSIG"; case OP_CHECKSIGVERIFY: return "CHECKSIGVERIFY"; case OP_CHECKMULTISIG: return "CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY: return "CHECKMULTISIGVERIFY"; case OP_NOP1: return "NOP1"; case OP_NOP2: return "NOP2"; case OP_NOP3: return "NOP3"; case OP_NOP4: return "NOP4"; case OP_NOP5: return "NOP5"; case OP_NOP6: return "NOP6"; case OP_NOP7: return "NOP7"; case OP_NOP8: return "NOP8"; case OP_NOP9: return "NOP9"; case OP_NOP10: return "NOP10"; default: return "NON_OP(" + opcode + ")"; } } private byte[] getData(int len) throws ScriptException { if (len > program.length - cursor) throw new ScriptException("Failed read of " + len + " bytes"); try { byte[] buf = new byte[len]; System.arraycopy(program, cursor, buf, 0, len); cursor += len; return buf; } catch (ArrayIndexOutOfBoundsException e) { // We want running out of data in the array to be treated as a handleable script parsing exception, // not something that abnormally terminates the app. throw new ScriptException("Failed read of " + len + " bytes", e); } catch (NegativeArraySizeException e) { // We want running out of data in the array to be treated as a handleable script parsing exception, // not something that abnormally terminates the app. throw new ScriptException("Failed read of " + len + " bytes", e); } } private int readByte() throws ScriptException { try { return 0xFF & program[cursor++]; } catch (ArrayIndexOutOfBoundsException e) { throw new ScriptException("Attempted to read outside of script boundaries"); } } /** * To run a script, first we parse it which breaks it up into chunks representing pushes of * data or logical opcodes. Then we can run the parsed chunks. * <p/> * The reason for this split, instead of just interpreting directly, is to make it easier * to reach into a programs structure and pull out bits of data without having to run it. * This is necessary to render the to/from addresses of transactions in a user interface. * The official client does something similar. */ private void parse(byte[] programBytes, int offset, int length) throws ScriptException { // TODO: this is inefficient program = new byte[length]; System.arraycopy(programBytes, offset, program, 0, length); offset = 0; chunks = new ArrayList<ScriptChunk>(10); // Arbitrary choice of initial size. cursor = offset; while (cursor < offset + length) { int startLocationInProgram = cursor - offset; int opcode = readByte(); if (opcode >= 0 && opcode < OP_PUSHDATA1) { // Read some bytes of data, where how many is the opcode value itself. chunks.add(new ScriptChunk(false, getData(opcode), startLocationInProgram)); // opcode == len here. } else if (opcode == OP_PUSHDATA1) { int len = readByte(); chunks.add(new ScriptChunk(false, getData(len), startLocationInProgram)); } else if (opcode == OP_PUSHDATA2) { // Read a short, then read that many bytes of data. int len = readByte() | (readByte() << 8); chunks.add(new ScriptChunk(false, getData(len), startLocationInProgram)); } else if (opcode == OP_PUSHDATA4) { // Read a uint32, then read that many bytes of data. // Though this is allowed, because its value cannot be > 520, it should never actually be used long len = readByte() | (readByte() << 8) | (readByte() << 16) | (readByte() << 24); chunks.add(new ScriptChunk(false, getData((int)len), startLocationInProgram)); } else { chunks.add(new ScriptChunk(true, new byte[]{(byte) opcode}, startLocationInProgram)); } } } /** * Returns true if this script is of the form <sig> OP_CHECKSIG. This form was originally intended for transactions * where the peers talked to each other directly via TCP/IP, but has fallen out of favor with time due to that mode * of operation being susceptible to man-in-the-middle attacks. It is still used in coinbase outputs and can be * useful more exotic types of transaction, but today most payments are to addresses. */ public boolean isSentToRawPubKey() { if (chunks.size() != 2) return false; return chunks.get(1).equalsOpCode(OP_CHECKSIG) && !chunks.get(0).isOpCode && chunks.get(0).data.length > 1; } /** * Returns true if this script is of the form DUP HASH160 <pubkey hash> EQUALVERIFY CHECKSIG, ie, payment to an * address like 1VayNert3x1KzbpzMGt2qdqrAThiRovi8. This form was originally intended for the case where you wish * to send somebody money with a written code because their node is offline, but over time has become the standard * way to make payments due to the short and recognizable base58 form addresses come in. */ public boolean isSentToAddress() { if (chunks.size() != 5) return false; return chunks.get(0).equalsOpCode(OP_DUP) && chunks.get(1).equalsOpCode(OP_HASH160) && chunks.get(2).data.length == Address.LENGTH && chunks.get(3).equalsOpCode(OP_EQUALVERIFY) && chunks.get(4).equalsOpCode(OP_CHECKSIG); } /** * If a program matches the standard template DUP HASH160 <pubkey hash> EQUALVERIFY CHECKSIG * then this function retrieves the third element, otherwise it throws a ScriptException.<p> * * This is useful for fetching the destination address of a transaction. */ public byte[] getPubKeyHash() throws ScriptException { if (!isSentToAddress()) throw new ScriptException("Script not in the standard scriptPubKey form"); // Otherwise, the third element is the hash of the public key, ie the feathercoin address. return chunks.get(2).data; } /** * Returns the public key in this script. If a script contains two constants and nothing else, it is assumed to * be a scriptSig (input) for a pay-to-address output and the second constant is returned (the first is the * signature). If a script contains a constant and an OP_CHECKSIG opcode, the constant is returned as it is * assumed to be a direct pay-to-key scriptPubKey (output) and the first constant is the public key. * * @throws ScriptException if the script is none of the named forms. */ public byte[] getPubKey() throws ScriptException { if (chunks.size() != 2) { throw new ScriptException("Script not of right size, expecting 2 but got " + chunks.size()); } if (chunks.get(0).data.length > 2 && chunks.get(1).data.length > 2) { // If we have two large constants assume the input to a pay-to-address output. return chunks.get(1).data; } else if (chunks.get(1).data.length == 1 && chunks.get(1).equalsOpCode(OP_CHECKSIG) && chunks.get(0).data.length > 2) { // A large constant followed by an OP_CHECKSIG is the key. return chunks.get(0).data; } else { throw new ScriptException("Script did not match expected form: " + toString()); } } /** * Convenience wrapper around getPubKey. Only works for scriptSigs. */ public Address getFromAddress() throws ScriptException { return new Address(params, Utils.sha256hash160(getPubKey())); } /** * Gets the destination address from this script, if it's in the required form (see getPubKey). * * @throws ScriptException */ public Address getToAddress() throws ScriptException { return new Address(params, getPubKeyHash()); } ////////////////////// Interface for writing scripts from scratch //////////////////////////////// /** * Writes out the given byte buffer to the output stream with the correct opcode prefix * To write an integer call writeBytes(out, Utils.reverseBytes(Utils.encodeMPI(val, false))); */ static void writeBytes(OutputStream os, byte[] buf) throws IOException { if (buf.length < OP_PUSHDATA1) { os.write(buf.length); os.write(buf); } else if (buf.length < 256) { os.write(OP_PUSHDATA1); os.write(buf.length); os.write(buf); } else if (buf.length < 65536) { os.write(OP_PUSHDATA2); os.write(0xFF & (buf.length)); os.write(0xFF & (buf.length >> 8)); os.write(buf); } else { throw new RuntimeException("Unimplemented"); } } public static byte[] createOutputScript(Address to) { try { // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes. ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(24); bits.write(OP_DUP); bits.write(OP_HASH160); writeBytes(bits, to.getHash160()); bits.write(OP_EQUALVERIFY); bits.write(OP_CHECKSIG); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } /** * Create a script that sends coins directly to the given public key (eg in a coinbase transaction). */ public static byte[] createOutputScript(byte[] pubkey) { try { // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes. ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(pubkey.length + 1); writeBytes(bits, pubkey); bits.write(OP_CHECKSIG); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } /** * Creates a script that sends coins directly to the given public key. Same as * {@link Script#createOutputScript(byte[])} but more type safe. */ public static byte[] createOutputScript(ECKey pubkey) { return createOutputScript(pubkey.getPubKey()); } public static byte[] createInputScript(byte[] signature, byte[] pubkey) { try { // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes. ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(signature.length + pubkey.length + 2); writeBytes(bits, signature); writeBytes(bits, pubkey); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } public static byte[] createInputScript(byte[] signature) { try { // TODO: Do this by creating a Script *first* then having the script reassemble itself into bytes. ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(signature.length + 2); writeBytes(bits, signature); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } ////////////////////// Interface used during verification of transactions/blocks //////////////////////////////// private static int getSigOpCount(List<ScriptChunk> chunks, boolean accurate) throws ScriptException { int sigOps = 0; int lastOpCode = OP_INVALIDOPCODE; for (ScriptChunk chunk : chunks) { if (chunk.isOpCode) { int opcode = 0xFF & chunk.data[0]; switch (opcode) { case OP_CHECKSIG: case OP_CHECKSIGVERIFY: sigOps++; break; case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: if (accurate && lastOpCode >= OP_1 && lastOpCode <= OP_16) sigOps += getOpNValue(lastOpCode); else sigOps += 20; break; default: break; } lastOpCode = opcode; } } return sigOps; } /** * Convince method to get the int value of OP_N */ private static int getOpNValue(int opcode) throws ScriptException { if (opcode == OP_0) return 0; if (opcode < OP_1 || opcode > OP_16) // This should absolutely never happen throw new ScriptException("getOpNValue called on non OP_N opcode"); return opcode + 1 - OP_1; } /** * Gets the count of regular SigOps in the script program (counting multisig ops as 20) */ public static int getSigOpCount(byte[] program) throws ScriptException { Script script = new Script(); try { script.parse(program, 0, program.length); } catch (ScriptException e) { // Ignore errors and count up to the parse-able length } return getSigOpCount(script.chunks, false); } /** * Gets the count of P2SH Sig Ops in the Script scriptSig */ public static long getP2SHSigOpCount(byte[] scriptSig) throws ScriptException { Script script = new Script(); try { script.parse(scriptSig, 0, scriptSig.length); } catch (ScriptException e) { // Ignore errors and count up to the parse-able length } for (int i = script.chunks.size() - 1; i >= 0; i--) if (!script.chunks.get(i).isOpCode) { Script subScript = new Script(); subScript.parse(script.chunks.get(i).data, 0, script.chunks.get(i).data.length); return getSigOpCount(subScript.chunks, true); } return 0; } /** * <p>Whether or not this is a scriptPubKey representing a pay-to-script-hash output. In such outputs, the logic that * controls reclamation is not actually in the output at all. Instead there's just a hash, and it's up to the * spending input to provide a program matching that hash. This rule is "soft enforced" by the network as it does * not exist in Satoshis original implementation. It means blocks containing P2SH transactions that don't match * correctly are considered valid, but won't be mined upon, so they'll be rapidly re-orgd out of the chain. This * logic is defined by <a href="https://en.feathercoin.it/wiki/BIP_0016">BIP 16</a>.</p> * * <p>feathercoinj does not support creation of P2SH transactions today. The goal of P2SH is to allow short addresses * even for complex scripts (eg, multi-sig outputs) so they are convenient to work with in things like QRcodes or * with copy/paste, and also to minimize the size of the unspent output set (which improves performance of the * Feathercoin system).</p> */ public boolean isPayToScriptHash() { return program.length == 23 && (program[0] & 0xff) == OP_HASH160 && (program[1] & 0xff) == 0x14 && (program[22] & 0xff) == OP_EQUAL; } private static boolean equalsRange(byte[] a, int start, byte[] b) { if (start + b.length > a.length) return false; for (int i = 0; i < b.length; i++) if (a[i + start] != b[i]) return false; return true; } /** * Returns the script bytes of inputScript with all instances of the specified script object removed */ public static byte[] removeAllInstancesOf(byte[] inputScript, byte[] chunkToRemove) { // We usually don't end up removing anything UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(inputScript.length); int cursor = 0; while (cursor < inputScript.length) { boolean skip = equalsRange(inputScript, cursor, chunkToRemove); int opcode = inputScript[cursor++] & 0xFF; int additionalBytes = 0; if (opcode >= 0 && opcode < OP_PUSHDATA1) { additionalBytes = opcode; } else if (opcode == OP_PUSHDATA1) { additionalBytes = inputScript[cursor] + 1; } else if (opcode == OP_PUSHDATA2) { additionalBytes = ((0xFF & inputScript[cursor]) | ((0xFF & inputScript[cursor+1]) << 8)) + 2; } else if (opcode == OP_PUSHDATA4) { additionalBytes = ((0xFF & inputScript[cursor]) | ((0xFF & inputScript[cursor+1]) << 8) | ((0xFF & inputScript[cursor+1]) << 16) | ((0xFF & inputScript[cursor+1]) << 24)) + 4; } if (!skip) { try { bos.write(opcode); bos.write(Arrays.copyOfRange(inputScript, cursor, cursor + additionalBytes)); } catch (IOException e) { throw new RuntimeException(e); } } cursor += additionalBytes; } return bos.toByteArray(); } /** * Returns the script bytes of inputScript with all instances of the given op code removed */ public static byte[] removeAllInstancesOfOp(byte[] inputScript, int opCode) { return removeAllInstancesOf(inputScript, new byte[] {(byte)opCode}); } ////////////////////// Script verification and helpers //////////////////////////////// private static boolean castToBool(byte[] data) { for (int i = 0; i < data.length; i++) { // "Can be negative zero" -reference client (see OpenSSL's BN_bn2mpi) if (data[i] != 0) return !(i == data.length - 1 && (data[i] & 0xFF) == 0x80); } return false; } private static BigInteger castToBigInteger(byte[] chunk) throws ScriptException { if (chunk.length > 4) throw new ScriptException("Script attempted to use an integer larger than 4 bytes"); return Utils.decodeMPI(Utils.reverseBytes(chunk), false); } private static void executeScript(Transaction txContainingThis, long index, Script script, LinkedList<byte[]> stack) throws ScriptException { int opCount = 0; int lastCodeSepLocation = 0; LinkedList<byte[]> altstack = new LinkedList<byte[]>(); LinkedList<Boolean> ifStack = new LinkedList<Boolean>(); for (ScriptChunk chunk : script.chunks) { boolean shouldExecute = !ifStack.contains(false); if (!chunk.isOpCode) { if (chunk.data.length > 520) throw new ScriptException("Attempted to push a data string larger than 520 bytes"); if (!shouldExecute) continue; stack.add(chunk.data); } else { int opcode = 0xFF & chunk.data[0]; if (opcode > OP_16) { opCount++; if (opCount > 201) throw new ScriptException("More script operations than is allowed"); } if (opcode == OP_VERIF || opcode == OP_VERNOTIF) throw new ScriptException("Script included OP_VERIF or OP_VERNOTIF"); if (opcode == OP_CAT || opcode == OP_SUBSTR || opcode == OP_LEFT || opcode == OP_RIGHT || opcode == OP_INVERT || opcode == OP_AND || opcode == OP_OR || opcode == OP_XOR || opcode == OP_2MUL || opcode == OP_2DIV || opcode == OP_MUL || opcode == OP_DIV || opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) throw new ScriptException("Script included a disabled Script Op."); switch (opcode) { case OP_IF: if (!shouldExecute) { ifStack.add(false); continue; } if (stack.size() < 1) throw new ScriptException("Attempted OP_IF on an empty stack"); ifStack.add(castToBool(stack.pollLast())); continue; case OP_NOTIF: if (!shouldExecute) { ifStack.add(false); continue; } if (stack.size() < 1) throw new ScriptException("Attempted OP_NOTIF on an empty stack"); ifStack.add(!castToBool(stack.pollLast())); continue; case OP_ELSE: if (ifStack.isEmpty()) throw new ScriptException("Attempted OP_ELSE without OP_IF/NOTIF"); ifStack.add(!ifStack.pollLast()); continue; case OP_ENDIF: if (ifStack.isEmpty()) throw new ScriptException("Attempted OP_ENDIF without OP_IF/NOTIF"); ifStack.pollLast(); continue; } if (!shouldExecute) continue; switch(opcode) { //case OP_0: dont know why this isnt also here in the reference client case OP_1NEGATE: stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ONE.negate(), false))); break; case OP_1: case OP_2: case OP_3: case OP_4: case OP_5: case OP_6: case OP_7: case OP_8: case OP_9: case OP_10: case OP_11: case OP_12: case OP_13: case OP_14: case OP_15: case OP_16: stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(getOpNValue(opcode)), false))); break; case OP_NOP: break; case OP_VERIFY: if (stack.size() < 1) throw new ScriptException("Attempted OP_VERIFY on an empty stack"); if (!castToBool(stack.pollLast())) throw new ScriptException("OP_VERIFY failed"); break; case OP_RETURN: throw new ScriptException("Script called OP_RETURN"); case OP_TOALTSTACK: if (stack.size() < 1) throw new ScriptException("Attempted OP_TOALTSTACK on an empty stack"); altstack.add(stack.pollLast()); break; case OP_FROMALTSTACK: if (altstack.size() < 1) throw new ScriptException("Attempted OP_TOALTSTACK on an empty altstack"); stack.add(altstack.pollLast()); break; case OP_2DROP: if (stack.size() < 2) throw new ScriptException("Attempted OP_2DROP on a stack with size < 2"); stack.pollLast(); stack.pollLast(); break; case OP_2DUP: if (stack.size() < 2) throw new ScriptException("Attempted OP_2DUP on a stack with size < 2"); Iterator<byte[]> it2DUP = stack.descendingIterator(); byte[] OP2DUPtmpChunk2 = it2DUP.next(); stack.add(it2DUP.next()); stack.add(OP2DUPtmpChunk2); break; case OP_3DUP: if (stack.size() < 3) throw new ScriptException("Attempted OP_3DUP on a stack with size < 3"); Iterator<byte[]> it3DUP = stack.descendingIterator(); byte[] OP3DUPtmpChunk3 = it3DUP.next(); byte[] OP3DUPtmpChunk2 = it3DUP.next(); stack.add(it3DUP.next()); stack.add(OP3DUPtmpChunk2); stack.add(OP3DUPtmpChunk3); break; case OP_2OVER: if (stack.size() < 4) throw new ScriptException("Attempted OP_2OVER on a stack with size < 4"); Iterator<byte[]> it2OVER = stack.descendingIterator(); it2OVER.next(); it2OVER.next(); byte[] OP2OVERtmpChunk2 = it2OVER.next(); stack.add(it2OVER.next()); stack.add(OP2OVERtmpChunk2); break; case OP_2ROT: if (stack.size() < 6) throw new ScriptException("Attempted OP_2ROT on a stack with size < 6"); byte[] OP2ROTtmpChunk6 = stack.pollLast(); byte[] OP2ROTtmpChunk5 = stack.pollLast(); byte[] OP2ROTtmpChunk4 = stack.pollLast(); byte[] OP2ROTtmpChunk3 = stack.pollLast(); byte[] OP2ROTtmpChunk2 = stack.pollLast(); byte[] OP2ROTtmpChunk1 = stack.pollLast(); stack.add(OP2ROTtmpChunk3); stack.add(OP2ROTtmpChunk4); stack.add(OP2ROTtmpChunk5); stack.add(OP2ROTtmpChunk6); stack.add(OP2ROTtmpChunk1); stack.add(OP2ROTtmpChunk2); break; case OP_2SWAP: if (stack.size() < 4) throw new ScriptException("Attempted OP_2SWAP on a stack with size < 4"); byte[] OP2SWAPtmpChunk4 = stack.pollLast(); byte[] OP2SWAPtmpChunk3 = stack.pollLast(); byte[] OP2SWAPtmpChunk2 = stack.pollLast(); byte[] OP2SWAPtmpChunk1 = stack.pollLast(); stack.add(OP2SWAPtmpChunk3); stack.add(OP2SWAPtmpChunk4); stack.add(OP2SWAPtmpChunk1); stack.add(OP2SWAPtmpChunk2); break; case OP_IFDUP: if (stack.size() < 1) throw new ScriptException("Attempted OP_IFDUP on an empty stack"); if (castToBool(stack.getLast())) stack.add(stack.getLast()); break; case OP_DEPTH: stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(stack.size()), false))); break; case OP_DROP: if (stack.size() < 1) throw new ScriptException("Attempted OP_DROP on an empty stack"); stack.pollLast(); break; case OP_DUP: if (stack.size() < 1) throw new ScriptException("Attempted OP_DUP on an empty stack"); stack.add(stack.getLast()); break; case OP_NIP: if (stack.size() < 2) throw new ScriptException("Attempted OP_NIP on a stack with size < 2"); byte[] OPNIPtmpChunk = stack.pollLast(); stack.pollLast(); stack.add(OPNIPtmpChunk); break; case OP_OVER: if (stack.size() < 2) throw new ScriptException("Attempted OP_OVER on a stack with size < 2"); Iterator<byte[]> itOVER = stack.descendingIterator(); itOVER.next(); stack.add(itOVER.next()); break; case OP_PICK: case OP_ROLL: if (stack.size() < 1) throw new ScriptException("Attempted OP_PICK/OP_ROLL on an empty stack"); long val = castToBigInteger(stack.pollLast()).longValue(); if (val < 0 || val >= stack.size()) throw new ScriptException("OP_PICK/OP_ROLL attempted to get data deeper than stack size"); Iterator<byte[]> itPICK = stack.descendingIterator(); for (long i = 0; i < val; i++) itPICK.next(); byte[] OPROLLtmpChunk = itPICK.next(); if (opcode == OP_ROLL) itPICK.remove(); stack.add(OPROLLtmpChunk); break; case OP_ROT: if (stack.size() < 3) throw new ScriptException("Attempted OP_ROT on a stack with size < 3"); byte[] OPROTtmpChunk3 = stack.pollLast(); byte[] OPROTtmpChunk2 = stack.pollLast(); byte[] OPROTtmpChunk1 = stack.pollLast(); stack.add(OPROTtmpChunk2); stack.add(OPROTtmpChunk3); stack.add(OPROTtmpChunk1); break; case OP_SWAP: case OP_TUCK: if (stack.size() < 2) throw new ScriptException("Attempted OP_SWAP on a stack with size < 2"); byte[] OPSWAPtmpChunk2 = stack.pollLast(); byte[] OPSWAPtmpChunk1 = stack.pollLast(); stack.add(OPSWAPtmpChunk2); stack.add(OPSWAPtmpChunk1); if (opcode == OP_TUCK) stack.add(OPSWAPtmpChunk2); break; case OP_CAT: case OP_SUBSTR: case OP_LEFT: case OP_RIGHT: throw new ScriptException("Attempted to use disabled Script Op."); case OP_SIZE: if (stack.size() < 1) throw new ScriptException("Attempted OP_SIZE on an empty stack"); stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(stack.getLast().length), false))); break; case OP_INVERT: case OP_AND: case OP_OR: case OP_XOR: throw new ScriptException("Attempted to use disabled Script Op."); case OP_EQUAL: if (stack.size() < 2) throw new ScriptException("Attempted OP_EQUALVERIFY on a stack with size < 2"); stack.add(Arrays.equals(stack.pollLast(), stack.pollLast()) ? new byte[] {1} : new byte[] {0}); break; case OP_EQUALVERIFY: if (stack.size() < 2) throw new ScriptException("Attempted OP_EQUALVERIFY on a stack with size < 2"); if (!Arrays.equals(stack.pollLast(), stack.pollLast())) throw new ScriptException("OP_EQUALVERIFY: non-equal data"); break; case OP_1ADD: case OP_1SUB: case OP_NEGATE: case OP_ABS: case OP_NOT: case OP_0NOTEQUAL: if (stack.size() < 1) throw new ScriptException("Attempted a numeric op on an empty stack"); BigInteger numericOPnum = castToBigInteger(stack.pollLast()); switch (opcode) { case OP_1ADD: numericOPnum = numericOPnum.add(BigInteger.ONE); break; case OP_1SUB: numericOPnum = numericOPnum.subtract(BigInteger.ONE); break; case OP_NEGATE: numericOPnum = numericOPnum.negate(); break; case OP_ABS: if (numericOPnum.compareTo(BigInteger.ZERO) < 0) numericOPnum = numericOPnum.negate(); break; case OP_NOT: if (numericOPnum.equals(BigInteger.ZERO)) numericOPnum = BigInteger.ONE; else numericOPnum = BigInteger.ZERO; break; case OP_0NOTEQUAL: if (numericOPnum.equals(BigInteger.ZERO)) numericOPnum = BigInteger.ZERO; else numericOPnum = BigInteger.ONE; break; default: throw new AssertionError("Unreachable"); } stack.add(Utils.reverseBytes(Utils.encodeMPI(numericOPnum, false))); break; case OP_2MUL: case OP_2DIV: throw new ScriptException("Attempted to use disabled Script Op."); case OP_ADD: case OP_SUB: case OP_BOOLAND: case OP_BOOLOR: case OP_NUMEQUAL: case OP_NUMNOTEQUAL: case OP_LESSTHAN: case OP_GREATERTHAN: case OP_LESSTHANOREQUAL: case OP_GREATERTHANOREQUAL: case OP_MIN: case OP_MAX: if (stack.size() < 2) throw new ScriptException("Attempted a numeric op on a stack with size < 2"); BigInteger numericOPnum2 = castToBigInteger(stack.pollLast()); BigInteger numericOPnum1 = castToBigInteger(stack.pollLast()); BigInteger numericOPresult; switch (opcode) { case OP_ADD: numericOPresult = numericOPnum1.add(numericOPnum2); break; case OP_SUB: numericOPresult = numericOPnum1.subtract(numericOPnum2); break; case OP_BOOLAND: if (!numericOPnum1.equals(BigInteger.ZERO) && !numericOPnum2.equals(BigInteger.ZERO)) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_BOOLOR: if (!numericOPnum1.equals(BigInteger.ZERO) || !numericOPnum2.equals(BigInteger.ZERO)) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_NUMEQUAL: if (numericOPnum1.equals(numericOPnum2)) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_NUMNOTEQUAL: if (!numericOPnum1.equals(numericOPnum2)) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_LESSTHAN: if (numericOPnum1.compareTo(numericOPnum2) < 0) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_GREATERTHAN: if (numericOPnum1.compareTo(numericOPnum2) > 0) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_LESSTHANOREQUAL: if (numericOPnum1.compareTo(numericOPnum2) <= 0) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_GREATERTHANOREQUAL: if (numericOPnum1.compareTo(numericOPnum2) >= 0) numericOPresult = BigInteger.ONE; else numericOPresult = BigInteger.ZERO; break; case OP_MIN: if (numericOPnum1.compareTo(numericOPnum2) < 0) numericOPresult = numericOPnum1; else numericOPresult = numericOPnum2; break; case OP_MAX: if (numericOPnum1.compareTo(numericOPnum2) > 0) numericOPresult = numericOPnum1; else numericOPresult = numericOPnum2; break; default: throw new RuntimeException("Opcode switched at runtime?"); } stack.add(Utils.reverseBytes(Utils.encodeMPI(numericOPresult, false))); break; case OP_MUL: case OP_DIV: case OP_MOD: case OP_LSHIFT: case OP_RSHIFT: throw new ScriptException("Attempted to use disabled Script Op."); case OP_NUMEQUALVERIFY: if (stack.size() < 2) throw new ScriptException("Attempted OP_NUMEQUALVERIFY on a stack with size < 2"); BigInteger OPNUMEQUALVERIFYnum2 = castToBigInteger(stack.pollLast()); BigInteger OPNUMEQUALVERIFYnum1 = castToBigInteger(stack.pollLast()); if (!OPNUMEQUALVERIFYnum1.equals(OPNUMEQUALVERIFYnum2)) throw new ScriptException("OP_NUMEQUALVERIFY failed"); break; case OP_WITHIN: if (stack.size() < 3) throw new ScriptException("Attempted OP_WITHIN on a stack with size < 3"); BigInteger OPWITHINnum3 = castToBigInteger(stack.pollLast()); BigInteger OPWITHINnum2 = castToBigInteger(stack.pollLast()); BigInteger OPWITHINnum1 = castToBigInteger(stack.pollLast()); if (OPWITHINnum2.compareTo(OPWITHINnum1) <= 0 && OPWITHINnum1.compareTo(OPWITHINnum3) < 0) stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ONE, false))); else stack.add(Utils.reverseBytes(Utils.encodeMPI(BigInteger.ZERO, false))); break; case OP_RIPEMD160: if (stack.size() < 1) throw new ScriptException("Attempted OP_RIPEMD160 on an empty stack"); RIPEMD160Digest digest = new RIPEMD160Digest(); byte[] dataToHash = stack.pollLast(); digest.update(dataToHash, 0, dataToHash.length); byte[] ripmemdHash = new byte[20]; digest.doFinal(ripmemdHash, 0); stack.add(ripmemdHash); break; case OP_SHA1: if (stack.size() < 1) throw new ScriptException("Attempted OP_SHA1 on an empty stack"); try { stack.add(MessageDigest.getInstance("SHA-1").digest(stack.pollLast())); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); // Cannot happen. } break; case OP_SHA256: if (stack.size() < 1) throw new ScriptException("Attempted OP_SHA256 on an empty stack"); try { stack.add(MessageDigest.getInstance("SHA-256").digest(stack.pollLast())); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); // Cannot happen. } break; case OP_HASH160: if (stack.size() < 1) throw new ScriptException("Attempted OP_HASH160 on an empty stack"); stack.add(Utils.sha256hash160(stack.pollLast())); break; case OP_HASH256: if (stack.size() < 1) throw new ScriptException("Attempted OP_SHA256 on an empty stack"); stack.add(Utils.doubleDigest(stack.pollLast())); break; case OP_CODESEPARATOR: lastCodeSepLocation = chunk.startLocationInProgram + 1; break; case OP_CHECKSIG: case OP_CHECKSIGVERIFY: if (stack.size() < 2) throw new ScriptException("Attempted OP_CHECKSIG(VERIFY) on a stack with size < 2"); byte[] CHECKSIGpubKey = stack.pollLast(); byte[] CHECKSIGsig = stack.pollLast(); byte[] CHECKSIGconnectedScript = Arrays.copyOfRange(script.program, lastCodeSepLocation, script.program.length); UnsafeByteArrayOutputStream OPCHECKSIGOutStream = new UnsafeByteArrayOutputStream(CHECKSIGsig.length + 1); try { writeBytes(OPCHECKSIGOutStream, CHECKSIGsig); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen } CHECKSIGconnectedScript = removeAllInstancesOf(CHECKSIGconnectedScript, OPCHECKSIGOutStream.toByteArray()); // TODO: Use int for indexes everywhere, we can't have that many inputs/outputs Sha256Hash CHECKSIGhash = txContainingThis.hashTransactionForSignature((int)index, CHECKSIGconnectedScript, CHECKSIGsig[CHECKSIGsig.length - 1]); boolean CHECKSIGsigValid; try { CHECKSIGsigValid = ECKey.verify(CHECKSIGhash.getBytes(), Arrays.copyOf(CHECKSIGsig, CHECKSIGsig.length - 1), CHECKSIGpubKey); } catch (Exception e1) { // There is (at least) one exception that could be hit here (EOFException, if the sig is too short) // Because I can't verify there aren't more, we use a very generic Exception catch CHECKSIGsigValid = false; } if (opcode == OP_CHECKSIG) stack.add(CHECKSIGsigValid ? new byte[] {1} : new byte[] {0}); else if (opcode == OP_CHECKSIGVERIFY) if (!CHECKSIGsigValid) throw new ScriptException("Script failed OP_CHECKSIGVERIFY"); break; case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: if (stack.size() < 2) throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < 2"); int CHECKMULTISIGpubKeyCount = castToBigInteger(stack.pollLast()).intValue(); if (CHECKMULTISIGpubKeyCount < 0 || CHECKMULTISIGpubKeyCount > 20) throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with pubkey count out of range"); opCount += CHECKMULTISIGpubKeyCount; if (opCount > 201) throw new ScriptException("Total op count > 201 during OP_CHECKMULTISIG(VERIFY)"); if (stack.size() < CHECKMULTISIGpubKeyCount + 1) throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + 2"); LinkedList<byte[]> CHECKMULTISIGpubkeys = new LinkedList<byte[]>(); for (int i = 0; i < CHECKMULTISIGpubKeyCount; i++) CHECKMULTISIGpubkeys.add(stack.pollLast()); int CHECKMULTISIGsigCount = castToBigInteger(stack.pollLast()).intValue(); if (CHECKMULTISIGsigCount < 0 || CHECKMULTISIGsigCount > CHECKMULTISIGpubKeyCount) throw new ScriptException("OP_CHECKMULTISIG(VERIFY) with sig count out of range"); if (stack.size() < CHECKMULTISIGsigCount + 1) throw new ScriptException("Attempted OP_CHECKMULTISIG(VERIFY) on a stack with size < num_of_pubkeys + num_of_signatures + 3"); LinkedList<byte[]> CHECKMULTISIGsigs = new LinkedList<byte[]>(); for (int i = 0; i < CHECKMULTISIGsigCount; i++) CHECKMULTISIGsigs.add(stack.pollLast()); byte[] CHECKMULTISIGconnectedScript = Arrays.copyOfRange(script.program, lastCodeSepLocation, script.program.length); for (byte[] CHECKMULTISIGsig : CHECKMULTISIGsigs) { UnsafeByteArrayOutputStream OPCHECKMULTISIGOutStream = new UnsafeByteArrayOutputStream(CHECKMULTISIGsig.length + 1); try { writeBytes(OPCHECKMULTISIGOutStream, CHECKMULTISIGsig); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen } CHECKMULTISIGconnectedScript = removeAllInstancesOf(CHECKMULTISIGconnectedScript, OPCHECKMULTISIGOutStream.toByteArray()); } boolean CHECKMULTISIGValid = true; while (CHECKMULTISIGsigs.size() > 0) { byte[] CHECKMULTISIGsig = CHECKMULTISIGsigs.getFirst(); byte[] CHECKMULTISIGpubKey = CHECKMULTISIGpubkeys.pollFirst(); // We could reasonably move this out of the loop, // but because signature verification is significantly more expensive than hashing, its not a big deal Sha256Hash CHECKMULTISIGhash = txContainingThis.hashTransactionForSignature((int)index, CHECKMULTISIGconnectedScript, CHECKMULTISIGsig[CHECKMULTISIGsig.length - 1]); try { if (ECKey.verify(CHECKMULTISIGhash.getBytes(), Arrays.copyOf(CHECKMULTISIGsig, CHECKMULTISIGsig.length - 1), CHECKMULTISIGpubKey)) CHECKMULTISIGsigs.pollFirst(); } catch (Exception e) { // There is (at least) one exception that could be hit here (EOFException, if the sig is too short) // Because I can't verify there aren't more, we use a very generic Exception catch } if (CHECKMULTISIGsigs.size() > CHECKMULTISIGpubkeys.size()) { CHECKMULTISIGValid = false; break; } } // We uselessly remove a stack object to emulate a reference client bug stack.pollLast(); if (opcode == OP_CHECKMULTISIG) stack.add(CHECKMULTISIGValid ? new byte[] {1} : new byte[] {0}); else if (opcode == OP_CHECKMULTISIGVERIFY) if (!CHECKMULTISIGValid) throw new ScriptException("Script failed OP_CHECKMULTISIGVERIFY"); break; case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: break; default: throw new ScriptException("Script used a reserved Op Code"); } } if (stack.size() + altstack.size() > 1000 || stack.size() + altstack.size() < 0) throw new ScriptException("Stack size exceeded range"); } if (!ifStack.isEmpty()) throw new ScriptException("OP_IF/OP_NOTIF without OP_ENDIF"); } /** * Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey. * @param txContainingThis The transaction in which this input scriptSig resides. * @param scriptSigIndex The index in txContainingThis of the scriptSig (note: NOT the index of the scriptPubKey). * @param scriptPubKey The connected scriptPubKey containing the conditions needed to claim the value. * @param enforceP2SH Whether "pay to script hash" rules should be enforced. If in doubt, set to true. * @throws VerificationException if this script does not correctly spend the scriptPubKey */ public void correctlySpends(Transaction txContainingThis, long scriptSigIndex, Script scriptPubKey, boolean enforceP2SH) throws ScriptException { if (program.length > 10000 || scriptPubKey.program.length > 10000) throw new ScriptException("Script larger than 10,000 bytes"); LinkedList<byte[]> stack = new LinkedList<byte[]>(); LinkedList<byte[]> p2shStack = null; executeScript(txContainingThis, scriptSigIndex, this, stack); if (enforceP2SH) p2shStack = new LinkedList<byte[]>(stack); executeScript(txContainingThis, scriptSigIndex, scriptPubKey, stack); if (stack.size() == 0) throw new ScriptException("Stack empty at end of script execution."); if (!castToBool(stack.pollLast())) throw new ScriptException("Script resulted in a non-true stack"); // P2SH is pay to script hash. It means that the scriptPubKey has a special form which is a valid // program but it has "useless" form that if evaluated as a normal program always returns true. // Instead, miners recognize it as special based on its template - it provides a hash of the real scriptPubKey // and that must be provided by the input. The goal of this bizarre arrangement is twofold: // // (1) You can sum up a large, complex script (like a CHECKMULTISIG script) with an address that's the same // size as a regular address. This means it doesn't overload scannable QR codes/NFC tags or become // un-wieldy to copy/paste. // (2) It allows the working set to be smaller: nodes perform best when they can store as many unspent outputs // in RAM as possible, so if the outputs are made smaller and the inputs get bigger, then it's better for // overall scalability and performance. // TODO: Check if we can take out enforceP2SH if there's a checkpoint at the enforcement block. if (enforceP2SH && scriptPubKey.isPayToScriptHash()) { for (ScriptChunk chunk : chunks) if (chunk.isOpCode && (chunk.data[0] & 0xff) > OP_16) throw new ScriptException("Attempted to spend a P2SH scriptPubKey with a script that contained script ops"); byte[] scriptPubKeyBytes = p2shStack.pollLast(); Script scriptPubKeyP2SH = new Script(params, scriptPubKeyBytes, 0, scriptPubKeyBytes.length); executeScript(txContainingThis, scriptSigIndex, scriptPubKeyP2SH, p2shStack); if (p2shStack.size() == 0) throw new ScriptException("P2SH stack empty at end of script execution."); if (!castToBool(p2shStack.pollLast())) throw new ScriptException("P2SH script execution resulted in a non-true stack"); } } }