All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bccapi.bitlib.model.Script Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
package com.bccapi.bitlib.model;

import com.bccapi.bitlib.util.ByteReader;
import com.bccapi.bitlib.util.ByteReader.InsufficientBytesException;
import com.bccapi.bitlib.util.HexUtils;

import java.util.HashMap;
import java.util.Map;

public abstract class Script {

   public static class ScriptParsingException extends Exception {
      private static final long serialVersionUID = 1L;

      public ScriptParsingException(byte[] script) {
         super("Unable to parse script: " + HexUtils.toHex(script));
      }

      public ScriptParsingException(String message) {
         super(message);
      }

   }

   public static final int OP_FALSE = 0;
   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_1NEGATE = 79;
   public static final int OP_TRUE = 81;
   public static final int OP_2 = 82;
   public static final int OP_3 = 83;
   public static final int OP_NOP = 97;
   public static final int OP_IF = 99;
   public static final int OP_VERIFY = 105;
   public static final int OP_IFDUP = 115;
   public static final int OP_DEPTH = 116;
   public static final int OP_DROP = 117;
   public static final int OP_DUP = 118;
   public static final int OP_EQUAL = 135;
   public static final int OP_EQUALVERIFY = 136;
   public static final int OP_MIN = 163;
   public static final int OP_SHA256 = 168;
   public static final int OP_HASH160 = 169;
   public static final int OP_CHECKSIG = 172;
   public static final int OP_CHECKSIGVERIFY = 173;
   public static final int OP_CHECKMULTISIG = 174;
   public static final int OP_CHECKMULTISIGVERIFY = 175;
   public static final int OP_NOP1 = 176;
   public static final int OP_NOP2 = 177;

   public static final Map OP_CODE_MAP;
   static {
      OP_CODE_MAP = new HashMap();
      OP_CODE_MAP.put(OP_FALSE, "OP_FALSE");
      OP_CODE_MAP.put(OP_PUSHDATA1, "OP_PUSHDATA1");
      OP_CODE_MAP.put(OP_PUSHDATA2, "OP_PUSHDATA2");
      OP_CODE_MAP.put(OP_PUSHDATA4, "OP_PUSHDATA4");
      OP_CODE_MAP.put(OP_1NEGATE, "OP_1NEGATE");
      OP_CODE_MAP.put(OP_TRUE, "OP_TRUE");
      OP_CODE_MAP.put(OP_2, "OP_2");
      OP_CODE_MAP.put(OP_3, "OP_3");
      OP_CODE_MAP.put(OP_NOP, "OP_NOP");
      OP_CODE_MAP.put(OP_IF, "OP_IF");
      OP_CODE_MAP.put(OP_VERIFY, "OP_VERIFY");
      OP_CODE_MAP.put(OP_IFDUP, "OP_IFDUP");
      OP_CODE_MAP.put(OP_DEPTH, "OP_DEPTH");
      OP_CODE_MAP.put(OP_DROP, "OP_DROP");
      OP_CODE_MAP.put(OP_DUP, "OP_DUP");
      OP_CODE_MAP.put(OP_EQUAL, "OP_EQUAL");
      OP_CODE_MAP.put(OP_EQUALVERIFY, "OP_EQUALVERIFY");
      OP_CODE_MAP.put(OP_MIN, "OP_MIN");
      OP_CODE_MAP.put(OP_SHA256, "OP_SHA256");
      OP_CODE_MAP.put(OP_HASH160, "OP_HASH160");
      OP_CODE_MAP.put(OP_CHECKSIG, "OP_CHECKSIG");
      OP_CODE_MAP.put(OP_CHECKSIGVERIFY, "OP_CHECKSIGVERIFY");
      OP_CODE_MAP.put(OP_CHECKMULTISIG, "OP_CHECKMULTISIG");
      OP_CODE_MAP.put(OP_CHECKMULTISIGVERIFY, "OP_CHECKMULTISIGVERIFY");
      OP_CODE_MAP.put(OP_NOP1, "OP_NOP1");
      OP_CODE_MAP.put(OP_NOP2, "OP_NOP2");
   }
   // protected byte[][] _chunks;
   protected byte[] _scriptBytes;
   private boolean _isCoinbase;

   // protected Script(byte[][] chunks) {
   // _chunks = chunks;
   // _isCoinbase = false;
   // }

   protected Script(byte[] scriptBytes, boolean isCoinBase) {
      // We handle coinbase scripts in a special way, as anything can be
      // stored in them, also stuff that does not parse
      // _chunks = new byte[][] { scriptBytes };
      _scriptBytes = scriptBytes;
      _isCoinbase = isCoinBase;
   }

   public boolean isCoinBase() {
      return _isCoinbase;
   }

   protected static final boolean isOP(byte[] chunk, int op) {
      return chunk.length == 1 && (((int) chunk[0]) & 0xFF) == op;
   }

   protected static final byte[][] chunksFromScriptBytes(byte[] script) throws ScriptParsingException {
      try {
         ByteReader reader = new ByteReader(script);
         int numChunks = countChuks(reader);
         if (numChunks == -1) {
            throw new ScriptParsingException(script);
         }
         byte[][] chunks = new byte[numChunks][];
         int index = 0;
         reader.reset();
         while (reader.available() > 0) {

            // Get opcode
            int opcode = reader.get();
            if (opcode >= 0xF0) {
               opcode = (opcode << 8) | reader.get();
            }

            if (opcode > 0 && opcode < OP_PUSHDATA1) {
               chunks[index++] = reader.getBytes(opcode);
            } else if (opcode == OP_PUSHDATA1) {
               int size = ((int) reader.get()) & 0xFF;
               chunks[index++] = reader.getBytes(size);
            } else if (opcode == OP_PUSHDATA2) {
               int size = reader.getShortLE();
               chunks[index++] = reader.getBytes(size);
            } else if (opcode == OP_PUSHDATA4) {
               // We do not support chunks this big
               throw new ScriptParsingException(script);
            } else {
               chunks[index++] = new byte[] { (byte) opcode };
            }
         }
         return chunks;
      } catch (InsufficientBytesException e) {
         throw new ScriptParsingException(script);
      }
   }

   private static int countChuks(ByteReader reader) throws InsufficientBytesException {
      int chunks = 0;
      while (reader.available() > 0) {

         // Get opcode
         int opcode = reader.get();
         if (opcode >= 0xF0) {
            opcode = (opcode << 8) | reader.get();
         }

         if (opcode > 0 && opcode < OP_PUSHDATA1) {
            chunks++;
            reader.skip(opcode);
         } else if (opcode == OP_PUSHDATA1) {
            int size = ((int) reader.get()) & 0xFF;
            chunks++;
            reader.skip(size);
         } else if (opcode == OP_PUSHDATA2) {
            int size = reader.getShortLE();
            chunks++;
            reader.skip(size);
         } else if (opcode == OP_PUSHDATA4) {
            // We do not support chunks this big
            return -1;
         } else {
            chunks++;
         }
      }
      return chunks;
   }

   // private int calculateByteSize() {
   // int size = 0;
   // for (byte[] chunk : _chunks) {
   // if (chunk.length == 1) {
   // size++;
   // } else if (chunk.length < OP_PUSHDATA1) {
   // size += 1 + chunk.length;
   // } else if (chunk.length < 256) {
   // size += 1 + 1 + chunk.length;
   // } else if (chunk.length < 65536) {
   // size += 1 + 1 + 1 + chunk.length;
   // } else {
   // throw new RuntimeException("Chunks larger than 65536 not implemented");
   // }
   // }
   // return size;
   // }

   public String dump(int maxLen) {
      String s = dump();
      if (s.length() > maxLen) {
         if (maxLen > 3) {
            s = s.substring(0, maxLen - 3) + "...";
         } else {
            s = s.substring(0, maxLen);
         }
      }
      return s;

   }

   public String dump() {
      if (_isCoinbase) {
         // coinbase scripts often cannot be parsed, hex dump them instead
         return HexUtils.toHex(_scriptBytes);
      }
      StringBuilder sb = new StringBuilder();
      byte[][] chunks;
      try {
         chunks = chunksFromScriptBytes(_scriptBytes);
      } catch (ScriptParsingException e) {
         return "Invalid script";
      }
      for (byte[] chunk : chunks) {
         if (chunk.length == 1) {
            int opCode = ((int) chunk[0]) & 0xFF;
            String opCodeString = OP_CODE_MAP.get(opCode);
            if (opCodeString == null) {
               sb.append(opCode);
            } else {
               sb.append(opCodeString);
            }
         } else {
            sb.append(HexUtils.toHex(chunk));
         }
         sb.append(' ');
      }
      return sb.toString();
   }

   /**
    * Get the script as an array of bytes
    * 
    * @return The script as an array of bytes
    */
   public byte[] getScriptBytes() {
      return _scriptBytes;
      // if (_isCoinbase) {
      // return _scriptBytes;
      // }
      // byte[] buf = new byte[calculateByteSize()];
      // int index = 0;
      // for (byte[] chunk : _chunks) {
      // if (chunk.length == 1) {
      // buf[index++] = chunk[0];
      // } else if (chunk.length < OP_PUSHDATA1) {
      // buf[index++] = (byte) (0xFF & chunk.length);
      // System.arraycopy(chunk, 0, buf, index, chunk.length);
      // index += chunk.length;
      // } else if (chunk.length < 256) {
      // buf[index++] = (byte) (0xFF & OP_PUSHDATA1);
      // buf[index++] = (byte) (0xFF & chunk.length);
      // System.arraycopy(chunk, 0, buf, index, chunk.length);
      // index += chunk.length;
      // } else if (chunk.length < 65536) {
      // buf[index++] = (byte) (0xFF & OP_PUSHDATA2);
      // buf[index++] = (byte) (0xFF & chunk.length);
      // buf[index++] = (byte) (0xFF & (chunk.length >> 8));
      // System.arraycopy(chunk, 0, buf, index, chunk.length);
      // index += chunk.length;
      // } else {
      // throw new RuntimeException("Chunks larger than 65536 not implemented");
      // }
      // }
      // return buf;
   }

   protected static final byte[] scriptEncodeChunks(byte[][] chunks) {
      byte[] buf = new byte[calculateByteSize(chunks)];
      int index = 0;
      for (byte[] chunk : chunks) {
         if (chunk.length == 1) {
            buf[index++] = chunk[0];
         } else if (chunk.length < OP_PUSHDATA1) {
            buf[index++] = (byte) (0xFF & chunk.length);
            System.arraycopy(chunk, 0, buf, index, chunk.length);
            index += chunk.length;
         } else if (chunk.length < 256) {
            buf[index++] = (byte) (0xFF & OP_PUSHDATA1);
            buf[index++] = (byte) (0xFF & chunk.length);
            System.arraycopy(chunk, 0, buf, index, chunk.length);
            index += chunk.length;
         } else if (chunk.length < 65536) {
            buf[index++] = (byte) (0xFF & OP_PUSHDATA2);
            buf[index++] = (byte) (0xFF & chunk.length);
            buf[index++] = (byte) (0xFF & (chunk.length >> 8));
            System.arraycopy(chunk, 0, buf, index, chunk.length);
            index += chunk.length;
         } else {
            throw new RuntimeException("Chunks larger than 65536 not implemented");
         }
      }
      return buf;
   }

   private static final int calculateByteSize(byte[][] chunks) {
      int size = 0;
      for (byte[] chunk : chunks) {
         if (chunk.length == 1) {
            size++;
         } else if (chunk.length < OP_PUSHDATA1) {
            size += 1 + chunk.length;
         } else if (chunk.length < 256) {
            size += 1 + 1 + chunk.length;
         } else if (chunk.length < 65536) {
            size += 1 + 1 + 1 + chunk.length;
         } else {
            throw new RuntimeException("Chunks larger than 65536 not implemented");
         }
      }
      return size;
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy