Enforce rule that the coinbase starts with serialized block height.
[NovacoinLibrary.git] / Novacoin / CScript.cs
index 70cf412..abdad9c 100644 (file)
-\feffusing System;
+\feff/**
+ *  Novacoin classes library
+ *  Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
+
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using System;
+using System.Linq;
 using System.Text;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Numerics;
 
 namespace Novacoin
 {
-       /** Script opcodes */
-       enum opcodetype
-       {
-               // push value
-               OP_0 = 0x00,
-               OP_FALSE = OP_0,
-               OP_PUSHDATA1 = 0x4c,
-               OP_PUSHDATA2 = 0x4d,
-               OP_PUSHDATA4 = 0x4e,
-               OP_1NEGATE = 0x4f,
-               OP_RESERVED = 0x50,
-               OP_1 = 0x51,
-               OP_TRUE=OP_1,
-               OP_2 = 0x52,
-               OP_3 = 0x53,
-               OP_4 = 0x54,
-               OP_5 = 0x55,
-               OP_6 = 0x56,
-               OP_7 = 0x57,
-               OP_8 = 0x58,
-               OP_9 = 0x59,
-               OP_10 = 0x5a,
-               OP_11 = 0x5b,
-               OP_12 = 0x5c,
-               OP_13 = 0x5d,
-               OP_14 = 0x5e,
-               OP_15 = 0x5f,
-               OP_16 = 0x60,
-
-               // control
-               OP_NOP = 0x61,
-               OP_VER = 0x62,
-               OP_IF = 0x63,
-               OP_NOTIF = 0x64,
-               OP_VERIF = 0x65,
-               OP_VERNOTIF = 0x66,
-               OP_ELSE = 0x67,
-               OP_ENDIF = 0x68,
-               OP_VERIFY = 0x69,
-               OP_RETURN = 0x6a,
-
-               // stack ops
-               OP_TOALTSTACK = 0x6b,
-               OP_FROMALTSTACK = 0x6c,
-               OP_2DROP = 0x6d,
-               OP_2DUP = 0x6e,
-               OP_3DUP = 0x6f,
-               OP_2OVER = 0x70,
-               OP_2ROT = 0x71,
-               OP_2SWAP = 0x72,
-               OP_IFDUP = 0x73,
-               OP_DEPTH = 0x74,
-               OP_DROP = 0x75,
-               OP_DUP = 0x76,
-               OP_NIP = 0x77,
-               OP_OVER = 0x78,
-               OP_PICK = 0x79,
-               OP_ROLL = 0x7a,
-               OP_ROT = 0x7b,
-               OP_SWAP = 0x7c,
-               OP_TUCK = 0x7d,
-
-               // splice ops
-               OP_CAT = 0x7e,
-               OP_SUBSTR = 0x7f,
-               OP_LEFT = 0x80,
-               OP_RIGHT = 0x81,
-               OP_SIZE = 0x82,
-
-               // bit logic
-               OP_INVERT = 0x83,
-               OP_AND = 0x84,
-               OP_OR = 0x85,
-               OP_XOR = 0x86,
-               OP_EQUAL = 0x87,
-               OP_EQUALVERIFY = 0x88,
-               OP_RESERVED1 = 0x89,
-               OP_RESERVED2 = 0x8a,
-
-               // numeric
-               OP_1ADD = 0x8b,
-               OP_1SUB = 0x8c,
-               OP_2MUL = 0x8d,
-               OP_2DIV = 0x8e,
-               OP_NEGATE = 0x8f,
-               OP_ABS = 0x90,
-               OP_NOT = 0x91,
-               OP_0NOTEQUAL = 0x92,
-
-               OP_ADD = 0x93,
-               OP_SUB = 0x94,
-               OP_MUL = 0x95,
-               OP_DIV = 0x96,
-               OP_MOD = 0x97,
-               OP_LSHIFT = 0x98,
-               OP_RSHIFT = 0x99,
-
-               OP_BOOLAND = 0x9a,
-               OP_BOOLOR = 0x9b,
-               OP_NUMEQUAL = 0x9c,
-               OP_NUMEQUALVERIFY = 0x9d,
-               OP_NUMNOTEQUAL = 0x9e,
-               OP_LESSTHAN = 0x9f,
-               OP_GREATERTHAN = 0xa0,
-               OP_LESSTHANOREQUAL = 0xa1,
-               OP_GREATERTHANOREQUAL = 0xa2,
-               OP_MIN = 0xa3,
-               OP_MAX = 0xa4,
-
-               OP_WITHIN = 0xa5,
-
-               // crypto
-               OP_RIPEMD160 = 0xa6,
-               OP_SHA1 = 0xa7,
-               OP_SHA256 = 0xa8,
-               OP_HASH160 = 0xa9,
-               OP_HASH256 = 0xaa,
-               OP_CODESEPARATOR = 0xab,
-               OP_CHECKSIG = 0xac,
-               OP_CHECKSIGVERIFY = 0xad,
-               OP_CHECKMULTISIG = 0xae,
-               OP_CHECKMULTISIGVERIFY = 0xaf,
-
-               // expansion
-               OP_NOP1 = 0xb0,
-               OP_NOP2 = 0xb1,
-               OP_NOP3 = 0xb2,
-               OP_NOP4 = 0xb3,
-               OP_NOP5 = 0xb4,
-               OP_NOP6 = 0xb5,
-               OP_NOP7 = 0xb6,
-               OP_NOP8 = 0xb7,
-               OP_NOP9 = 0xb8,
-               OP_NOP10 = 0xb9,
-
-
-               // template matching params
-               OP_SMALLDATA = 0xf9,
-               OP_SMALLINTEGER = 0xfa,
-               OP_PUBKEYS = 0xfb,
-               OP_PUBKEYHASH = 0xfd,
-               OP_PUBKEY = 0xfe,
-
-               OP_INVALIDOPCODE = 0xff,
-       };
-
+    /// <summary>
+    /// Representation of script code
+    /// </summary>
        public class CScript
        {
-               private byte[] scriptCode = {};
+        private List<byte> codeBytes;
 
+        /// <summary>
+        /// Initializes an empty instance of CScript
+        /// </summary>
                public CScript ()
                {
+            codeBytes = new List<byte>();
                }
 
-               private string GetOpName(opcodetype opcode)
-               {
-                       switch (opcode) {
-                       // push value
-                       case opcodetype.OP_0:
-                               return "0";
-                       case opcodetype.OP_PUSHDATA1:
-                               return "OP_PUSHDATA1";
-                       case opcodetype.OP_PUSHDATA2:
-                               return "OP_PUSHDATA2";
-                       case opcodetype.OP_PUSHDATA4:
-                               return "OP_PUSHDATA4";
-                       case opcodetype.OP_1NEGATE:
-                               return "-1";
-                       case opcodetype.OP_RESERVED:
-                               return "OP_RESERVED";
-                       case opcodetype.OP_1:
-                               return "1";
-                       case opcodetype.OP_2:
-                               return "2";
-                       case opcodetype.OP_3:
-                               return "3";
-                       case opcodetype.OP_4:
-                               return "4";
-                       case opcodetype.OP_5:
-                               return "5";
-                       case opcodetype.OP_6:
-                               return "6";
-                       case opcodetype.OP_7:
-                               return "7";
-                       case opcodetype.OP_8:
-                               return "8";
-                       case opcodetype.OP_9:
-                               return "9";
-                       case opcodetype.OP_10:
-                               return "10";
-                       case opcodetype.OP_11:
-                               return "11";
-                       case opcodetype.OP_12:
-                               return "12";
-                       case opcodetype.OP_13:
-                               return "13";
-                       case opcodetype.OP_14:
-                               return "14";
-                       case opcodetype.OP_15:
-                               return "15";
-                       case opcodetype.OP_16:
-                               return "16";
-
-                       // control
-                       case opcodetype.OP_NOP:
-                               return "OP_NOP";
-                       case opcodetype.OP_VER:
-                               return "OP_VER";
-                       case opcodetype.OP_IF:
-                               return "OP_IF";
-                       case opcodetype.OP_NOTIF:
-                               return "OP_NOTIF";
-                       case opcodetype.OP_VERIF:
-                               return "OP_VERIF";
-                       case opcodetype.OP_VERNOTIF:
-                               return "OP_VERNOTIF";
-                       case opcodetype.OP_ELSE:
-                               return "OP_ELSE";
-                       case opcodetype.OP_ENDIF:
-                               return "OP_ENDIF";
-                       case opcodetype.OP_VERIFY:
-                               return "OP_VERIFY";
-                       case opcodetype.OP_RETURN:
-                               return "OP_RETURN";
-
-                       // stack ops
-                       case opcodetype.OP_TOALTSTACK:
-                               return "OP_TOALTSTACK";
-                       case opcodetype.OP_FROMALTSTACK:
-                               return "OP_FROMALTSTACK";
-                       case opcodetype.OP_2DROP:
-                               return "OP_2DROP";
-                       case opcodetype.OP_2DUP:
-                               return "OP_2DUP";
-                       case opcodetype.OP_3DUP:
-                               return "OP_3DUP";
-                       case opcodetype.OP_2OVER:
-                               return "OP_2OVER";
-                       case opcodetype.OP_2ROT:
-                               return "OP_2ROT";
-                       case opcodetype.OP_2SWAP:
-                               return "OP_2SWAP";
-                       case opcodetype.OP_IFDUP:
-                               return "OP_IFDUP";
-                       case opcodetype.OP_DEPTH:
-                               return "OP_DEPTH";
-                       case opcodetype.OP_DROP:
-                               return "OP_DROP";
-                       case opcodetype.OP_DUP:
-                               return "OP_DUP";
-                       case opcodetype.OP_NIP:
-                               return "OP_NIP";
-                       case opcodetype.OP_OVER:
-                               return "OP_OVER";
-                       case opcodetype.OP_PICK:
-                               return "OP_PICK";
-                       case opcodetype.OP_ROLL:
-                               return "OP_ROLL";
-                       case opcodetype.OP_ROT:
-                               return "OP_ROT";
-                       case opcodetype.OP_SWAP:
-                               return "OP_SWAP";
-                       case opcodetype.OP_TUCK:
-                               return "OP_TUCK";
-
-                       // splice ops
-                       case opcodetype.OP_CAT:
-                               return "OP_CAT";
-                       case opcodetype.OP_SUBSTR:
-                               return "OP_SUBSTR";
-                       case opcodetype.OP_LEFT:
-                               return "OP_LEFT";
-                       case opcodetype.OP_RIGHT:
-                               return "OP_RIGHT";
-                       case opcodetype.OP_SIZE:
-                               return "OP_SIZE";
-
-                       // bit logic
-                       case opcodetype.OP_INVERT:
-                               return "OP_INVERT";
-                       case opcodetype.OP_AND:
-                               return "OP_AND";
-                       case opcodetype.OP_OR:
-                               return "OP_OR";
-                       case opcodetype.OP_XOR:
-                               return "OP_XOR";
-                       case opcodetype.OP_EQUAL:
-                               return "OP_EQUAL";
-                       case opcodetype.OP_EQUALVERIFY:
-                               return "OP_EQUALVERIFY";
-                       case opcodetype.OP_RESERVED1:
-                               return "OP_RESERVED1";
-                       case opcodetype.OP_RESERVED2:
-                               return "OP_RESERVED2";
-
-                       // numeric
-                       case opcodetype.OP_1ADD:
-                               return "OP_1ADD";
-                       case opcodetype.OP_1SUB:
-                               return "OP_1SUB";
-                       case opcodetype.OP_2MUL:
-                               return "OP_2MUL";
-                       case opcodetype.OP_2DIV:
-                               return "OP_2DIV";
-                       case opcodetype.OP_NEGATE:
-                               return "OP_NEGATE";
-                       case opcodetype.OP_ABS:
-                               return "OP_ABS";
-                       case opcodetype.OP_NOT:
-                               return "OP_NOT";
-                       case opcodetype.OP_0NOTEQUAL:
-                               return "OP_0NOTEQUAL";
-                       case opcodetype.OP_ADD:
-                               return "OP_ADD";
-                       case opcodetype.OP_SUB:
-                               return "OP_SUB";
-                       case opcodetype.OP_MUL:
-                               return "OP_MUL";
-                       case opcodetype.OP_DIV:
-                               return "OP_DIV";
-                       case opcodetype.OP_MOD:
-                               return "OP_MOD";
-                       case opcodetype.OP_LSHIFT:
-                               return "OP_LSHIFT";
-                       case opcodetype.OP_RSHIFT:
-                               return "OP_RSHIFT";
-                       case opcodetype.OP_BOOLAND:
-                               return "OP_BOOLAND";
-                       case opcodetype.OP_BOOLOR:
-                               return "OP_BOOLOR";
-                       case opcodetype.OP_NUMEQUAL:
-                               return "OP_NUMEQUAL";
-                       case opcodetype.OP_NUMEQUALVERIFY:
-                               return "OP_NUMEQUALVERIFY";
-                       case opcodetype.OP_NUMNOTEQUAL:
-                               return "OP_NUMNOTEQUAL";
-                       case opcodetype.OP_LESSTHAN:
-                               return "OP_LESSTHAN";
-                       case opcodetype.OP_GREATERTHAN:
-                               return "OP_GREATERTHAN";
-                       case opcodetype.OP_LESSTHANOREQUAL:
-                               return "OP_LESSTHANOREQUAL";
-                       case opcodetype.OP_GREATERTHANOREQUAL:
-                               return "OP_GREATERTHANOREQUAL";
-                       case opcodetype.OP_MIN:
-                               return "OP_MIN";
-                       case opcodetype.OP_MAX:
-                               return "OP_MAX";
-                       case opcodetype.OP_WITHIN:
-                               return "OP_WITHIN";
-
-                       // crypto
-                       case opcodetype.OP_RIPEMD160:
-                               return "OP_RIPEMD160";
-                       case opcodetype.OP_SHA1:
-                               return "OP_SHA1";
-                       case opcodetype.OP_SHA256:
-                               return "OP_SHA256";
-                       case opcodetype.OP_HASH160:
-                               return "OP_HASH160";
-                       case opcodetype.OP_HASH256:
-                               return "OP_HASH256";
-                       case opcodetype.OP_CODESEPARATOR:
-                               return "OP_CODESEPARATOR";
-                       case opcodetype.OP_CHECKSIG:
-                               return "OP_CHECKSIG";
-                       case opcodetype.OP_CHECKSIGVERIFY:
-                               return "OP_CHECKSIGVERIFY";
-                       case opcodetype.OP_CHECKMULTISIG:
-                               return "OP_CHECKMULTISIG";
-                       case opcodetype.OP_CHECKMULTISIGVERIFY:
-                               return "OP_CHECKMULTISIGVERIFY";
-
-                       // expanson
-                       case opcodetype.OP_NOP1:
-                               return "OP_NOP1";
-                       case opcodetype.OP_NOP2:
-                               return "OP_NOP2";
-                       case opcodetype.OP_NOP3:
-                               return "OP_NOP3";
-                       case opcodetype.OP_NOP4:
-                               return "OP_NOP4";
-                       case opcodetype.OP_NOP5:
-                               return "OP_NOP5";
-                       case opcodetype.OP_NOP6:
-                               return "OP_NOP6";
-                       case opcodetype.OP_NOP7:
-                               return "OP_NOP7";
-                       case opcodetype.OP_NOP8:
-                               return "OP_NOP8";
-                       case opcodetype.OP_NOP9:
-                               return "OP_NOP9";
-                       case opcodetype.OP_NOP10:
-                               return "OP_NOP10";
-
-                       // template matching params
-                       case opcodetype.OP_PUBKEYHASH:
-                               return "OP_PUBKEYHASH";
-                       case opcodetype.OP_PUBKEY:
-                               return "OP_PUBKEY";
-                       case opcodetype.OP_SMALLDATA:
-                               return "OP_SMALLDATA";
-
-                       case opcodetype.OP_INVALIDOPCODE:
-                               return "OP_INVALIDOPCODE";
-                       default:
-                               return "OP_UNKNOWN";
-                       }
-               }
+        /// <summary>
+        /// Initializes new instance of CScript and fills it with supplied bytes
+        /// </summary>
+        /// <param name="bytes">Enumerator interface for byte sequence</param>
+        public CScript(byte[] bytes)
+        {
+            codeBytes = new List<byte>(bytes);
+        }
+
+        /// <summary>
+        /// Return a new instance of ByteQueue object for current code bytes
+        /// </summary>
+        /// <returns></returns>
+        public InstructionQueue GetInstructionQueue()
+        {
+             return new InstructionQueue(ref codeBytes);
+        }
+
+        /// <summary>
+        /// Add serialized number to instructions list.
+        /// </summary>
+        /// <param name="n">Number to add.</param>
+        public void AddNumber(int n)
+        {
+            if (n == -1 || (n >= 1 && n <= 16))
+            {
+                codeBytes.Add((byte)ScriptCode.EncodeOP_N(n, true));
+            }
+            else
+            {
+                BigInteger bn = n;
+                PushData(bn.ToByteArray());
+            }
+        }
+
+        /// <summary>
+        /// Adds specified operation to instruction list
+        /// </summary>
+        /// <param name="opcode">Instruction to add.</param>
+        public void AddInstruction(instruction opcode)
+        {
+            Contract.Requires<ArgumentException>(opcode >= instruction.OP_0 && opcode <= instruction.OP_INVALIDOPCODE, "Invalid instruction.");
+
+            codeBytes.Add((byte)opcode);
+        }
+
+        /// <summary>
+        /// Adds hash to instruction list.
+        ///    New items are added in this format:
+        ///    hash_length_byte hash_bytes
+        /// </summary>
+        /// <param name="hash">uint160 instance</param>
+        public void AddHash(uint160 hash)
+        {
+            codeBytes.Add((byte)hash.Size);
+            codeBytes.AddRange((byte[])hash);
+        }
+
+        /// <summary>
+        /// Adds hash to instruction list.
+        ///    New items are added in this format:
+        ///    hash_length_byte hash_bytes
+        /// </summary>
+        /// <param name="hash">uint256 instance</param>
+        public void AddHash(uint256 hash)
+        {
+            codeBytes.Add((byte)hash.Size);
+            codeBytes.AddRange((byte[])hash);
+        }
+
+        /// <summary>
+        /// Create new OP_PUSHDATAn operator and add it to instruction list
+        /// </summary>
+        /// <param name="dataBytes">Set of data bytes</param>
+        public void PushData(byte[] dataBytes)
+        {
+            var nCount = dataBytes.LongLength;
+
+            if (nCount < (int)instruction.OP_PUSHDATA1)
+            {
+                // OP_0 and OP_FALSE
+                codeBytes.Add((byte)nCount);
+            }
+            else if (nCount < 0xff)
+            {
+                // OP_PUSHDATA1 0x01 [0x5a]
+                codeBytes.Add((byte)instruction.OP_PUSHDATA1);
+                codeBytes.Add((byte)nCount);
+            }
+            else if (nCount < 0xffff)
+            {
+                // OP_PUSHDATA1 0x01 0x00 [0x5a]
+                codeBytes.Add((byte)instruction.OP_PUSHDATA2);
+
+                var szBytes = BitConverter.GetBytes((ushort)nCount);
+                codeBytes.AddRange(szBytes);
+            }
+            else if (nCount < 0xffffffff)
+            {
+                // OP_PUSHDATA1 0x01 0x00 0x00 0x00 [0x5a]
+                codeBytes.Add((byte)instruction.OP_PUSHDATA4);
+
+                var szBytes = BitConverter.GetBytes((uint)nCount);
+                codeBytes.AddRange(szBytes);
+            }
+
+            // Add data bytes
+            codeBytes.AddRange(dataBytes);
+        }
+
+        /// <summary>
+        /// Just insert data array without including any prefixes. Please make sure that you know what you're doing, 
+        ///    it is recommended to use AddInstruction, AddHash or PushData instead.
+        /// </summary>
+        /// <param name="dataBytes">Data bytes</param>
+        public void AddRawData(byte[] dataBytes)
+        {
+            // Add data bytes
+            codeBytes.AddRange(dataBytes);
+        }
+
+        /// <summary>
+        /// Scan pushed data bytes for pattern and, in case of exact match, remove it.
+        /// </summary>
+        /// <param name="pattern">Pattern sequence</param>
+        /// <returns>Matches count</returns>
+        public int RemovePattern(byte[] pattern)
+        {
+            // There is no sense to continue if pattern is empty or longer than script itself
+            if (pattern.Length == 0 || pattern.Length > codeBytes.Count)
+            {
+                return 0;
+            }
+
+            var count = 0;
+            var bq1 = new InstructionQueue(ref codeBytes);
+
+            byte[] pushData;
+            instruction opcode;
+
+            var newScript = new CScript();
+
+            while (ScriptCode.GetOp(ref bq1, out opcode, out pushData))
+            {
+                if (pushData.Length == 0)
+                {
+                    // No data, put instruction on its place
+                    newScript.AddInstruction(opcode);
+                }
+                else if (!pushData.SequenceEqual(pattern))
+                {
+                    // No match, create push operator
+                    newScript.PushData(pushData);
+                }
+                else
+                {
+                    count++; // match
+                }
+            }
+
+            if (count > 0)
+            {
+                // Replace current script if any matches were found
+                codeBytes = newScript.codeBytes;
+            }
+
+            return count;
+        }
+
+        /// <summary>
+        /// Scan script for specific instruction and remove it if there are some matches.
+        /// </summary>
+        /// <param name="op">Instruction</param>
+        /// <returns>Matches count</returns>
+        public int RemoveInstruction(instruction op)
+        {
+            byte[] pushData;
+            instruction opcode;
+
+            var count = 0;
+            var newScript = new CScript();
+            var bq1 = new InstructionQueue(ref codeBytes);
+
+            while (ScriptCode.GetOp(ref bq1, out opcode, out pushData))
+            {
+                if (pushData.Length != 0 && op != opcode)
+                {
+                    // If instruction didn't match then push its data again
+                    newScript.PushData(pushData);
+                }
+                else if (Enum.IsDefined(typeof(instruction), op) && op != opcode)
+                {
+                    // Instruction didn't match
+                    newScript.AddInstruction(opcode);
+                }
+                else
+                {
+                    count++; // match
+                }
+            }
+
+            if (count > 0)
+            {
+                // Replace current script if any matches were found
+                codeBytes = newScript.codeBytes;
+            }
+
+            return count;
+        }
+
+        /// <summary>
+        /// Is it true that script doesn't contain anything except push value operations?
+        /// </summary>
+        public bool IsPushOnly
+        {
+            get
+            {
+                var wCodeBytes = new InstructionQueue(ref codeBytes);
+
+                instruction opcode; // Current instruction
+                byte[] pushArgs; // OP_PUSHDATAn argument
+
+                // Scan instructions sequence
+                while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+                {
+                    if (opcode > instruction.OP_16)
+                    {
+                        // We don't allow control instructions here
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// Is it true that script doesn't contain non-canonical push operations?
+        /// </summary>
+        public bool HasOnlyCanonicalPushes
+        {
+            get
+            {
+                var wCodeBytes = new InstructionQueue(ref codeBytes);
 
+                byte[] pushArgs; // OP_PUSHDATAn argument
+                instruction opcode; // Current instruction
+
+                // Scan instructions sequence
+                while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+                {
+                    var data = pushArgs;
+
+                    if (opcode < instruction.OP_PUSHDATA1 && opcode > instruction.OP_0 && (data.Length == 1 && data[0] <= 16))
+                    {
+                        // Could have used an OP_n code, rather than a 1-byte push.
+                        return false;
+                    }
+                    if (opcode == instruction.OP_PUSHDATA1 && data.Length < (int)instruction.OP_PUSHDATA1)
+                    {
+                        // Could have used a normal n-byte push, rather than OP_PUSHDATA1.
+                        return false;
+                    }
+                    if (opcode == instruction.OP_PUSHDATA2 && data.Length <= 0xFF)
+                    {
+                        // Could have used an OP_PUSHDATA1.
+                        return false;
+                    }
+                    if (opcode == instruction.OP_PUSHDATA4 && data.LongLength <= 0xFFFF)
+                    {
+                        // Could have used an OP_PUSHDATA2.
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// Quick test for pay-to-script-hash CScripts
+        /// </summary>
+        public bool IsPayToScriptHash
+        {
+            get
+            {
+                // Sender provides redeem script hash, receiver provides signature list and redeem script
+                // OP_HASH160 20 [20 byte hash] OP_EQUAL
+                return (codeBytes.Count() == 23 &&
+                        codeBytes[0] == (byte)instruction.OP_HASH160 &&
+                        codeBytes[1] == 0x14 && // 20 bytes hash length prefix
+                        codeBytes[22] == (byte)instruction.OP_EQUAL);
+            }
+        }
+
+        /// <summary>
+        /// Quick test for pay-to-pubkeyhash CScripts
+        /// </summary>
+        public bool IsPayToPubKeyHash
+        {
+            get
+            {
+                // Sender provides hash of pubkey, receiver provides signature and pubkey
+                // OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
+                return (codeBytes.Count == 25 &&
+                        codeBytes[0] == (byte)instruction.OP_DUP &&
+                        codeBytes[1] == (byte)instruction.OP_HASH160 &&
+                        codeBytes[2] == 0x14 && // 20 bytes hash length prefix
+                        codeBytes[23] == (byte)instruction.OP_EQUALVERIFY &&
+                        codeBytes[24] == (byte)instruction.OP_CHECKSIG);
+            }
+        }
+
+        /// <summary>
+        /// Quick test for Null destination
+        /// </summary>
+        public bool IsNull
+        {
+            get { return codeBytes.Count == 0; }
+        }
+
+        /// <summary>
+        /// Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
+        /// as 20 sigops. With pay-to-script-hash, that changed:
+        /// CHECKMULTISIGs serialized in scriptSigs are
+        /// counted more accurately, assuming they are of the form
+        ///  ... OP_N CHECKMULTISIG ...
+        /// </summary>
+        /// <param name="fAccurate">Legacy mode flag</param>
+        /// <returns>Amount of sigops</returns>
+        public uint GetSigOpCount(bool fAccurate)
+        {
+            var wCodeBytes = new InstructionQueue(ref codeBytes);
+
+            instruction opcode; // Current instruction
+            byte[] pushArgs; // OP_PUSHDATAn argument
+
+            uint nCount = 0;
+            var lastOpcode = instruction.OP_INVALIDOPCODE;
+
+            // Scan instructions sequence
+            while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+            {
+                if (opcode == instruction.OP_CHECKSIG || opcode == instruction.OP_CHECKSIGVERIFY)
+                {
+                    nCount++;
+                }
+                else if (opcode == instruction.OP_CHECKMULTISIG || opcode == instruction.OP_CHECKMULTISIGVERIFY)
+                {
+                    if (fAccurate && lastOpcode >= instruction.OP_1 && lastOpcode <= instruction.OP_16)
+                    {
+                        nCount += (uint)ScriptCode.DecodeOP_N(lastOpcode);
+                    }
+                    else
+                    {
+                        nCount += 20;
+                    }
+                }
+            }
+
+            return nCount;
+        }
+
+        /// <summary>
+        /// Accurately count sigOps, including sigOps in
+        /// pay-to-script-hash transactions
+        /// </summary>
+        /// <param name="scriptSig">pay-to-script-hash scriptPubKey</param>
+        /// <returns>SigOps count</returns>
+        public uint GetSigOpCount(CScript scriptSig)
+        {
+            if (!IsPayToScriptHash)
+            {
+                return GetSigOpCount(true);
+            }
+
+            // This is a pay-to-script-hash scriptPubKey;
+            // get the last item that the scriptSig
+            // pushes onto the stack:
+            InstructionQueue wScriptSig = scriptSig.GetInstructionQueue();
+            uint nScriptSigSize = scriptSig.Size;
+
+            instruction opcode; // Current instruction
+            byte[] pushArgs = new byte[0]; // OP_PUSHDATAn argument
+
+
+            while (wScriptSig.Index < nScriptSigSize)
+            {
+                if (!ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs))
+                {
+                    return 0;
+                }
+
+                if (opcode > instruction.OP_16)
+                {
+                    return 0;
+                }
+            }
+
+            /// ... and return its opcount:
+            var subScript = new CScript(pushArgs);
+
+            return subScript.GetSigOpCount(true);
+
+        }
+
+        /// <summary>
+        /// Set pay-to-pubkey destination.
+        /// </summary>
+        /// <param name="pubKey">Instance of CPubKey.</param>
+        public void SetDestination(CPubKey pubKey)
+        {
+            codeBytes.Clear();
+            PushData(pubKey);
+            AddInstruction(instruction.OP_CHECKSIG);
+        }
+
+        /// <summary>
+        /// Set pay-to-pubkeyhash destination
+        /// </summary>
+        /// <param name="ID">Public key hash</param>
+        public void SetDestination(CKeyID ID)
+        {
+            codeBytes.Clear();
+            AddInstruction(instruction.OP_DUP);
+            AddInstruction(instruction.OP_HASH160);
+            AddHash(ID);
+            AddInstruction(instruction.OP_EQUALVERIFY);
+            AddInstruction(instruction.OP_CHECKSIG);
+        }
+
+        /// <summary>
+        /// Set pay-to-scripthash destination
+        /// </summary>
+        /// <param name="ID">Script hash</param>
+        public void SetDestination(CScriptID ID)
+        {
+            codeBytes.Clear();
+            AddInstruction(instruction.OP_HASH160);
+            AddHash(ID);
+            AddInstruction(instruction.OP_EQUAL);
+        }
+
+        /// <summary>
+        /// Reset script code buffer.
+        /// </summary>
+        public void SetNullDestination()
+        {
+            codeBytes.Clear();
+        }
+
+        /// <summary>
+        /// Set multisig destination.
+        /// </summary>
+        /// <param name="nRequired">Amount of required signatures.</param>
+        /// <param name="keys">Set of public keys.</param>
+        public void SetMultiSig(int nRequired, CPubKey[] keys)
+        {
+            codeBytes.Clear();
+            AddInstruction(ScriptCode.EncodeOP_N(nRequired));
+
+            foreach (var key in keys)
+            {
+                PushData(key);
+            }
+
+            AddInstruction(ScriptCode.EncodeOP_N(keys.Length));
+            AddInstruction(instruction.OP_CHECKMULTISIG);
+        }
+
+        /// <summary>
+        /// Access to script code.
+        /// </summary>
+        public static implicit operator byte[] (CScript script)
+        {
+            return script.codeBytes.ToArray();
+        }
+
+        /// <summary>
+        /// Implicit cast of byte array to CScript.
+        /// </summary>
+        /// <param name="scriptBytes"></param>
+        public static implicit operator CScript(byte[] scriptBytes)
+        {
+            return new CScript(scriptBytes);
+        }
+
+        /// <summary>
+        /// Script size
+        /// </summary>
+        public uint Size
+        {
+            get { return (uint) codeBytes.Count; }
+        }
+
+        public CScriptID ScriptID
+        {
+            get { return new CScriptID(CryptoUtils.ComputeHash160(codeBytes.ToArray())); }
+        }
+
+        /// <summary>
+        /// Disassemble current script code
+        /// </summary>
+        /// <returns>Code listing</returns>
                public override string ToString()
                {
-                       // TODO: disassembly 
-
-                       StringBuilder sb = new StringBuilder(scriptCode.Length * 2);
-                       foreach (byte b in scriptCode)
-                       {
-                               sb.AppendFormat ("{0:x2}", b);
-                       }
-                       return sb.ToString();
+                       var sb = new StringBuilder();
+            var wCodeBytes = new InstructionQueue(ref codeBytes);
+
+            instruction opcode; // Current instruction
+            byte[] pushArgs; // OP_PUSHDATAn argument
+            while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+            {
+                if (sb.Length != 0)
+                {
+                    sb.Append(" ");
+                }
+
+                if (0 <= opcode && opcode <= instruction.OP_PUSHDATA4)
+                {
+                    sb.Append(ScriptCode.ValueString(pushArgs));
+                }
+                else
+                {
+                    sb.Append(ScriptCode.GetOpName(opcode));
+                }
+            }
+
+            return sb.ToString();
                }
        }
 }
-