Improve CryptoUtils with wrappers for managed implementations of standard hashing...
[NovacoinLibrary.git] / Novacoin / ScriptCode.cs
index 19850c9..14bc2e5 100644 (file)
@@ -18,6 +18,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.Contracts;
 using System.Linq;
 using System.Numerics;
 using System.Text;
@@ -246,72 +247,61 @@ namespace Novacoin
         /// <param name="opcodeRet">Found instruction.</param>
         /// <param name="bytesRet">IEnumerable out param which is used to get the push arguments.</param>
         /// <returns>Result of operation</returns>
-        public static bool GetOp(ref ByteQueue codeBytes, out instruction opcodeRet, out byte[] bytesRet)
+        public static bool GetOp(ref InstructionQueue codeBytes, out instruction opcodeRet, out byte[] bytesRet)
         {
             bytesRet = new byte[0];
-            opcodeRet = instruction.OP_INVALIDOPCODE;
+            instruction opcode = opcodeRet = instruction.OP_INVALIDOPCODE;
 
-            instruction opcode;
-
-            try
+            // Read instruction
+            byte opVal = 0xff;
+            if (!codeBytes.TryGet(ref opVal))
             {
-                // Read instruction
-                opcode = (instruction)codeBytes.Get();
-            }
-            catch (ByteQueueException)
-            {
-                // No instruction found there
                 return false;
             }
+            opcode = (instruction)opVal;
 
             // Immediate operand
             if (opcode <= instruction.OP_PUSHDATA4)
             {
                 var szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length
+                int nSize = 0;
 
                 try
                 {
                     if (opcode < instruction.OP_PUSHDATA1)
                     {
                         // Zero value instructions (OP_0, OP_FALSE)
-                        szBytes[3] = (byte)opcode;
+                        nSize = (int) opcode;
                     }
                     else if (opcode == instruction.OP_PUSHDATA1)
                     {
                         // The next byte contains the number of bytes to be pushed onto the stack, 
                         //    i.e. you have something like OP_PUSHDATA1 0x01 [0x5a]
-                        szBytes[3] = codeBytes.Get();
+                        nSize = codeBytes.Get();
                     }
                     else if (opcode == instruction.OP_PUSHDATA2)
                     {
                         // The next two bytes contain the number of bytes to be pushed onto the stack,
-                        //    i.e. now your operation will seem like this: OP_PUSHDATA2 0x00 0x01 [0x5a]
-                        codeBytes.Get(2).CopyTo(szBytes, 2);
+                        //    i.e. now your operation will seem like this: OP_PUSHDATA2 0x01 0x00 [0x5a]
+                        nSize = BitConverter.ToInt16(codeBytes.Get(2), 0);
                     }
                     else if (opcode == instruction.OP_PUSHDATA4)
                     {
                         // The next four bytes contain the number of bytes to be pushed onto the stack,
-                        //   OP_PUSHDATA4 0x00 0x00 0x00 0x01 [0x5a]
-                        szBytes = codeBytes.Get(4);
+                        //   OP_PUSHDATA4 0x01 0x00 0x00 0x00 [0x5a]
+                        nSize = BitConverter.ToInt32(codeBytes.Get(4), 0);
                     }
                 }
-                catch (ByteQueueException)
+                catch (InstructionQueueException)
                 {
                     // Unable to read operand length
                     return false;
                 }
 
-                int nSize = (int)Interop.BEBytesToUInt32(szBytes);
-
                 if (nSize > 0)
                 {
-                    // If nSize is greater than zero then there is some data available
-                    try
-                    {
-                        // Read found number of bytes into list of OP_PUSHDATAn arguments.
-                        bytesRet = codeBytes.Get(nSize);
-                    }
-                    catch (ByteQueueException)
+                    // Trying to read found number of bytes into list of OP_PUSHDATAn arguments.
+                    if (!codeBytes.TryGet(nSize, ref bytesRet))
                     {
                         // Unable to read data
                         return false;
@@ -337,7 +327,7 @@ namespace Novacoin
 
             if (bytes.Length <= 4)
             {
-                sb.Append(Interop.BEBytesToUInt32(bytes));
+                sb.Append(new BigInteger(bytes));
             }
             else
             {
@@ -370,23 +360,18 @@ namespace Novacoin
         /// <returns>Small integer</returns>
         public static int DecodeOP_N(instruction opcode, bool AllowNegate = false)
         {
-            if (AllowNegate && opcode == instruction.OP_1NEGATE)
-            {
-                return -1;
-            }
-
-            if (opcode == instruction.OP_0)
-            {
-                return 0;
-            }
-
             // Only OP_n instructions are supported, throw exception otherwise.
-            if (opcode < instruction.OP_1 || opcode > instruction.OP_16)
+            Contract.Requires<ArgumentException>((opcode == instruction.OP_1NEGATE && AllowNegate) || (opcode >= instruction.OP_0 && opcode <= instruction.OP_16), "Invalid integer instruction.");
+
+            switch (opcode)
             {
-                throw new ArgumentException("Invalid integer instruction.");
+                case instruction.OP_1NEGATE:
+                    return -1;
+                case instruction.OP_0:
+                    return 0;
+                default:
+                    return (int)opcode - (int)(instruction.OP_1 - 1);
             }
-
-            return (int)opcode - (int)(instruction.OP_1 - 1);
         }
 
         /// <summary>
@@ -396,20 +381,18 @@ namespace Novacoin
         /// <returns>Corresponding instruction.</returns>
         public static instruction EncodeOP_N(int n, bool allowNegate = false)
         {
-            if (allowNegate && n == -1)
-            {
-                return instruction.OP_1NEGATE;
-            }
+            // The n value must be in the range of 1 to 16.
+            Contract.Requires<ArgumentException>((n == -1 && allowNegate) || (n >= 0 && n <= 16), "Invalid integer value.");
 
-            if (n == 0)
+            switch (n)
             {
-                return instruction.OP_0;
+                case -1:
+                    return instruction.OP_1NEGATE;
+                case 0:
+                    return instruction.OP_0;
+                default:
+                    return (instruction.OP_1 + n - 1);
             }
-
-            // The n value must be in the range of 0 to 16.
-            if (n < 0 || n > 16)
-                throw new ArgumentException("Invalid integer value.");
-            return (instruction.OP_1 + n - 1);
         }
 
         public static int ScriptSigArgsExpected(txnouttype t, IList<byte[]> solutions)
@@ -479,6 +462,8 @@ namespace Novacoin
         /// <returns>Result</returns>
         public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList<byte[]> solutions)
         {
+            byte[] scriptBytes = scriptPubKey;
+
             solutions = new List<byte[]>();
 
             // There are shortcuts for pay-to-script-hash and pay-to-pubkey-hash, which are more constrained than the other types.
@@ -489,7 +474,7 @@ namespace Novacoin
                 typeRet = txnouttype.TX_SCRIPTHASH;
 
                 // Take 20 bytes with offset of 2 bytes
-                var hashBytes = scriptPubKey.Bytes.Skip(2).Take(20);
+                var hashBytes = scriptBytes.Skip(2).Take(20);
                 solutions.Add(hashBytes.ToArray());
 
                 return true;
@@ -501,7 +486,7 @@ namespace Novacoin
                 typeRet = txnouttype.TX_PUBKEYHASH;
 
                 // Take 20 bytes with offset of 3 bytes
-                var hashBytes = scriptPubKey.Bytes.Skip(3).Take(20);
+                var hashBytes = scriptBytes.Skip(3).Take(20);
                 solutions.Add(hashBytes.ToArray());
 
                 return true;
@@ -556,17 +541,17 @@ namespace Novacoin
                 instruction opcode1, opcode2;
 
                 // Compare
-                var bq1 = script1.GetByteQUeue();
-                var bq2 = script2.GetByteQUeue();
+                var bq1 = script1.GetInstructionQueue();
+                var bq2 = script2.GetInstructionQueue();
 
                 byte[] args1, args2;
 
-                int last1 = script1.Bytes.Count() -1;
-                int last2 = script2.Bytes.Count() - 1;
+                int last1 = ((byte[])script1).Length - 1;
+                int last2 = ((byte[])script2).Length - 1;
 
                 while (true)
                 {
-                    if (bq1.CurrentIndex == last1 && bq2.CurrentIndex == last2)
+                    if (bq1.Index == last1 && bq2.Index == last2)
                     {
                         // Found a match
                         typeRet = templateTuple.Item1;
@@ -673,12 +658,7 @@ namespace Novacoin
         /// <returns></returns>
         public static Hash256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType)
         {
-            if (nIn >= txTo.vin.Length)
-            {
-                var sb = new StringBuilder();
-                sb.AppendFormat("ERROR: SignatureHash() : nIn={0} out of range\n", nIn);
-                throw new ArgumentOutOfRangeException("nIn", sb.ToString());
-            }
+            Contract.Requires<ArgumentOutOfRangeException>(nIn < txTo.vin.Length, "nIn out of range.");
 
             // Init a copy of transaction
             var txTmp = new CTransaction(txTo);
@@ -744,7 +724,7 @@ namespace Novacoin
             }
 
             // Concatenate and hash
-            var txBytes = txTmp.Bytes;
+            var txBytes = (byte[])txTmp;
             var nHashTypeBytes = BitConverter.GetBytes(nHashType);
 
             return Hash256.Compute256(ref txBytes, ref nHashTypeBytes);
@@ -758,6 +738,7 @@ namespace Novacoin
         /// <summary>
         /// Script machine exception
         /// </summary>
+        [Serializable]
         public class StackMachineException : Exception
         {
             public StackMachineException()
@@ -781,10 +762,9 @@ namespace Novacoin
         /// <param name="stack">Stack reference</param>
         private static void popstack(ref List<byte[]> stack)
         {
-            int nCount = stack.Count;
-            if (nCount == 0)
-                throw new StackMachineException("popstack() : stack empty");
-            stack.RemoveAt(nCount - 1);
+            Contract.Requires<StackMachineException>(stack.Count > 0, "Stack is empty.");
+
+            stack.RemoveAt(stack.Count - 1);
         }
 
         /// <summary>
@@ -795,25 +775,10 @@ namespace Novacoin
         /// <returns>Byte sequence</returns>
         private static byte[] stacktop(ref List<byte[]> stack, int nDepth)
         {
-            int nStackElement = stack.Count + nDepth;
-
-            if (nDepth >= 0)
-            {
-                StringBuilder sb = new StringBuilder();
-                sb.AppendFormat("stacktop() : positive depth ({0}) has no sense.", nDepth);
+            Contract.Requires<StackMachineException>(nDepth < 0, "Positive or zero stack depth makes no sense.");
+            Contract.Requires<StackMachineException>(stack.Count + nDepth >= 0, "Value exceeds real stack depth.");
 
-                throw new StackMachineException(sb.ToString());
-            }
-
-            if (nStackElement < 0)
-            {
-                StringBuilder sb = new StringBuilder();
-                sb.AppendFormat("stacktop() : nDepth={0} exceeds real stack depth ({1})", nDepth, stack.Count);
-
-                throw new StackMachineException(sb.ToString());
-            }
-
-            return stack[nStackElement];
+            return stack[stack.Count + nDepth];
         }
 
         /// <summary>
@@ -847,10 +812,7 @@ namespace Novacoin
         /// <returns></returns>
         private static BigInteger CastToBigInteger(byte[] value)
         {
-            if (value.Length > 4)
-            {
-                throw new StackMachineException("CastToBigInteger() : overflow");
-            }
+            Contract.Requires<StackMachineException>(value.Length <= 4, "Size limit failed.");
 
             return new BigInteger(value);
         }
@@ -867,7 +829,9 @@ namespace Novacoin
         /// <returns></returns>
         public static bool EvalScript(ref List<byte[]> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType)
         {
-            if (script.Bytes.Count() > 10000)
+            var scriptBytes = ((byte[])script);
+
+            if (scriptBytes.Length > 10000)
             {
                 return false; // Size limit failed
             }
@@ -880,11 +844,13 @@ namespace Novacoin
             var falseBytes = new byte[0];
             var trueBytes = new byte[] { 0x01 };
 
-            var CodeQueue = script.GetByteQUeue();
+            var CodeQueue = script.GetInstructionQueue();
             var altStack = new List<byte[]>();
 
+#if !DEBUG
             try
             {
+#endif
                 instruction opcode;
                 byte[] pushArg;
 
@@ -1526,36 +1492,36 @@ namespace Novacoin
                                     {
                                         return false;
                                     }
-                                    Hash hash = null;
+                                    byte[] hash = null;
                                     var data = stacktop(ref stack, -1);
 
                                     switch (opcode)
                                     {
                                         case instruction.OP_HASH160:
-                                            hash = Hash160.Compute160(data);
+                                            hash = CryptoUtils.ComputeHash160(data);
                                             break;
                                         case instruction.OP_HASH256:
-                                            hash = Hash256.Compute256(data);
+                                            hash = CryptoUtils.ComputeHash256(data);
                                             break;
                                         case instruction.OP_SHA1:
-                                            hash = SHA1.Compute1(data);
+                                            hash = CryptoUtils.ComputeSha1(data);
                                             break;
                                         case instruction.OP_SHA256:
-                                            hash = SHA256.Compute256(data);
+                                            hash = CryptoUtils.ComputeSha256(data);
                                             break;
                                         case instruction.OP_RIPEMD160:
-                                            hash = RIPEMD160.Compute160(data);
+                                            hash = CryptoUtils.ComputeRipeMD160(data);
                                             break;
                                     }
                                     popstack(ref stack);
-                                    stack.Add(hash.hashBytes);
+                                    stack.Add(hash);
                                 }
                                 break;
 
                             case instruction.OP_CODESEPARATOR:
                                 {
                                     // Hash starts after the code separator
-                                    nCodeHashBegin = CodeQueue.CurrentIndex;
+                                    nCodeHashBegin = CodeQueue.Index;
                                 }
                                 break;
 
@@ -1572,7 +1538,7 @@ namespace Novacoin
                                     var pubkeyBytes = stacktop(ref stack, -1);
 
                                     // Subset of script starting at the most recent codeseparator
-                                    var scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin).ToArray());
+                                    var scriptCode = new CScript(scriptBytes.Skip(nCodeHashBegin).ToArray());
 
                                     // There's no way for a signature to sign itself
                                     scriptCode.RemovePattern(sigBytes);
@@ -1639,7 +1605,7 @@ namespace Novacoin
                                     }
 
                                     // Subset of script starting at the most recent codeseparator
-                                    var scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin).ToArray());
+                                    var scriptCode = new CScript(scriptBytes.Skip(nCodeHashBegin).ToArray());
 
                                     // There is no way for a signature to sign itself, so we need to drop the signatures
                                     for (int k = 0; k < nSigsCount; k++)
@@ -1720,12 +1686,14 @@ namespace Novacoin
                         return false;
                     }
                 }
+#if !DEBUG
             }
             catch (Exception)
             {
                 // If there are any exceptions then just return false.
                 return false;
             }
+#endif
 
             if (vfExec.Count() != 0)
             {