/** * Copyright 2011 Google Inc. * * 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.bitcoin.core; import com.google.bitcoin.core.Transaction.SigHash; import com.google.bitcoin.bouncycastle.util.Arrays; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Stack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.bitcoin.core.Utils.bytesToHexString; /** * BitCoin transactions don't specify what they do directly. Instead <a href="https://en.bitcoin.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> * * This implementation of the scripting language is incomplete. It contains enough support to run standard * transactions generated by the official client, but non-standard transactions will fail. */ public class Script { private static Logger log = LoggerFactory.getLogger(Script.class); // Some constants used for decoding the scripts. public static final int OP_PUSHDATA1 = 76; public static final int OP_PUSHDATA2 = 77; public static final int OP_PUSHDATA4 = 78; public static final int OP_DUP = 118; public static final int OP_HASH160 = 169; public static final int OP_EQUALVERIFY = 136; public static final int OP_CHECKSIG = 172; byte[] program; private int cursor; // The stack consists of an ordered series of data buffers growing from zero up. private final Stack<byte[]> stack; // The program is a set of byte[]s where each element is either [opcode] or [data, data, data ...] private List<byte[]> chunks; private boolean tracing; byte[] programCopy; // TODO: remove this private final NetworkParameters params; /** Concatenates two scripts to form a new one. This is used when verifying transactions. */ public static Script join(Script a, Script b) throws ScriptException { assert a.params == b.params; byte[] fullProg = new byte[a.programCopy.length + b.programCopy.length]; System.arraycopy(a.programCopy, 0, fullProg, 0, a.programCopy.length); System.arraycopy(b.programCopy, 0, fullProg, a.programCopy.length, b.programCopy.length); return new Script(a.params, fullProg, 0, fullProg.length); } /** * 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; stack = new Stack<byte[]>(); parse(programBytes, offset, length); } /** If true, running a program will log its instructions. */ public void setTracing(boolean value) { this.tracing = value; } /** Returns the program opcodes as a string, for example "[1234] DUP HAHS160" */ public String toString() { StringBuffer buf = new StringBuffer(); for (byte[] chunk : chunks) { if (chunk.length == 1) { String opName; int opcode = 0xFF & chunk[0]; switch (opcode) { case OP_DUP: opName = "DUP"; break; case OP_HASH160: opName = "HASH160"; break; case OP_CHECKSIG: opName = "CHECKSIG"; break; case OP_EQUALVERIFY: opName = "EQUALVERIFY"; break; default: opName = "?(" + opcode + ")"; break; } buf.append(opName); buf.append(" "); } else { // Data chunk buf.append("["); buf.append(chunk.length); buf.append("]"); buf.append(bytesToHexString(chunk)); buf.append(" "); } } return buf.toString(); } private byte[] getData(int len) throws ScriptException { 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); } } private int readByte() { return 0xFF & program[cursor++]; } /** * 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. * * 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 programCopy = new byte[length]; System.arraycopy(programBytes, offset, programCopy, 0, length); program = programCopy; offset = 0; chunks = new ArrayList<byte[]>(10); // Arbitrary choice of initial size. cursor = offset; while (cursor < offset + length) { int opcode = readByte(); if (opcode >= 0xF0) { // Not a single byte opcode. opcode = (opcode << 8) | readByte(); } if (opcode > 0 && opcode < OP_PUSHDATA1) { // Read some bytes of data, where how many is the opcode value itself. chunks.add(getData(opcode)); // opcode == len here. } else if (opcode == OP_PUSHDATA1) { int len = readByte(); chunks.add(getData(len)); } else if (opcode == OP_PUSHDATA2) { // Read a short, then read that many bytes of data. int len = readByte() | (readByte() << 8); chunks.add(getData(len)); } else if (opcode == OP_PUSHDATA4) { // Read a uint32, then read that many bytes of data. log.error("PUSHDATA4: Unimplemented"); } else { chunks.add(new byte[] { (byte) opcode }); } } } /** * Returns true if this transaction is of a format that means it was a direct IP to IP transaction. These * transactions are deprecated and no longer used, support for creating them has been removed from the official * client. */ public boolean isSentToIP() { if (chunks.size() != 2) return false; return (0xFF & chunks.get(1)[0]) == OP_CHECKSIG && chunks.get(0).length > 1; } /** * 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. * * This is useful for fetching the destination address of a transaction. */ public byte[] getPubKeyHash() throws ScriptException { if (chunks.size() != 5) throw new ScriptException("Script not of right size to be a scriptPubKey, " + "expecting 5 but got " + chunks.size()); if ((0xFF & chunks.get(0)[0]) != OP_DUP || (0xFF & chunks.get(1)[0]) != OP_HASH160 || (0xFF & chunks.get(3)[0]) != OP_EQUALVERIFY || (0xFF & chunks.get(4)[0]) != OP_CHECKSIG) throw new ScriptException("Script not in the standard scriptPubKey form"); // Otherwise, the third element is the hash of the public key, ie the bitcoin address. return chunks.get(2); } /** * If a program has two data buffers (constants) and nothing else, the second one is returned. * For a scriptSig this should be the public key of the sender. * * This is useful for fetching the source address of a transaction. */ public byte[] getPubKey() throws ScriptException { if (chunks.size() == 1) { // Direct IP to IP transactions only have the public key in their scriptSig. return chunks.get(0); } if (chunks.size() != 2) throw new ScriptException("Script not of right size to be a scriptSig, expecting 2" + " but got " + chunks.size()); if (!(chunks.get(0).length > 1) && (chunks.get(1).length > 1)) throw new ScriptException("Script not in the standard scriptSig form: " + chunks.size() + " chunks"); return chunks.get(1); } /** * 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()); } /** * Runs the script with the given Transaction as the "context". Some operations like CHECKSIG * require a transaction to operate on (eg to hash). The context transaction is typically * the transaction having its inputs verified, ie the one where the scriptSig comes from. */ public boolean run(Transaction context) throws ScriptException { for (byte[] chunk : chunks) { if (chunk.length == 1) { int opcode = 0xFF & chunk[0]; switch (opcode) { case OP_DUP: opDup(); break; case OP_HASH160: opHash160(); break; case OP_EQUALVERIFY: opEqualVerify(); break; case OP_CHECKSIG: opCheckSig(context); break; default: log.debug("Unknown/unimplemented opcode: {}", opcode); } } else { // Data block, push it onto the stack. log.debug("Push {}", Utils.bytesToHexString(chunk)); stack.add(chunk); } } byte[] result = stack.pop(); if (result.length != 1) throw new ScriptException("Script left junk at the top of the stack: " + Utils.bytesToHexString(result)); return result[0] == 1; } void logStack() { for (int i = 0; i < stack.size(); i++) { log.debug("Stack[{}]: {}",i , Utils.bytesToHexString(stack.get(i))); } } // WARNING: Unfinished and untested! @SuppressWarnings("unused") private void opCheckSig( Transaction context) throws ScriptException { byte[] pubkey = stack.pop(); byte[] sigAndHashType = stack.pop(); // The signature has an extra byte on the end to indicate the type of hash. The signature // is over the contents of the program, minus the signature itself of course. byte hashType = sigAndHashType[sigAndHashType.length - 1]; // The high bit of the hashType byte is set to indicate "anyone can pay". boolean anyoneCanPay = hashType < 0; // Mask out the top bit. hashType &= (byte)-1 >>> 1; Transaction.SigHash sigHash; switch (hashType) { case 1: sigHash = SigHash.ALL; break; case 2: sigHash = SigHash.NONE; break; case 3: sigHash = SigHash.SINGLE; break; default: // TODO: This should probably not be an exception. throw new ScriptException("Unknown sighash byte: " + sigAndHashType[sigAndHashType.length - 1]); } byte[] sig = new byte[sigAndHashType.length - 1]; System.arraycopy(sigAndHashType, 0, sig, 0, sig.length); log.debug("CHECKSIG: hashtype={} anyoneCanPay={}", sigHash, anyoneCanPay); if (context == null) { // TODO: Fix the unit tests to run scripts in transaction context then remove this. pushBool(true); return; } // TODO: Implement me! // Transaction tx = context.simplify(sigHash, 0, anyoneCanPay); // The steps to do so are as follows: // - Use the hashtype to fiddle the transaction as appropriate // - Serialize the transaction and hash it // - Use EC code to verify the hash matches the signature pushBool(true); } @SuppressWarnings({"SameParameterValue"}) private void pushBool(boolean val) { stack.push(new byte[] { val ? (byte)1 : (byte)0 }); } private void opEqualVerify() throws ScriptException { log.debug("EQUALVERIFY"); byte[] a = stack.pop(); byte[] b = stack.pop(); if (!Arrays.areEqual(a, b)) throw new ScriptException("EQUALVERIFY failed: " + Utils.bytesToHexString(a) + " vs " + Utils.bytesToHexString(b)); } /** Replaces the top item in the stack with a hash160 of it */ private void opHash160() { byte[] buf = stack.pop(); byte[] hash = Utils.sha256hash160(buf); stack.add(hash); log.debug("HASH160: output is {}", Utils.bytesToHexString(hash)); } /** Duplicates the top item on the stack */ private void opDup() { log.debug("DUP"); stack.add(Arrays.clone(stack.lastElement())); } ////////////////////// Interface for writing scripts from scratch //////////////////////////////// /** Writes out the given byte buffer to the output stream with the correct opcode prefix */ 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)); } else { throw new RuntimeException("Unimplemented"); } } 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 ByteArrayOutputStream(); 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. } } 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 ByteArrayOutputStream(); writeBytes(bits, signature); writeBytes(bits, pubkey); return bits.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } }