Document SignatureHash and EvalScript methods, a little refactoring of hashing instru...
[NovacoinLibrary.git] / Novacoin / ScriptCode.cs
index 6a44b2a..04ca7c4 100644 (file)
@@ -457,10 +457,14 @@ namespace Novacoin
                     return "OP_NOP10";
 
                 // template matching params
+                case instruction.OP_SMALLINTEGER:
+                    return "OP_SMALLINTEGER";
                 case instruction.OP_PUBKEYHASH:
                     return "OP_PUBKEYHASH";
                 case instruction.OP_PUBKEY:
                     return "OP_PUBKEY";
+                case instruction.OP_PUBKEYS:
+                    return "OP_PUBKEYS";
                 case instruction.OP_SMALLDATA:
                     return "OP_SMALLDATA";
 
@@ -796,12 +800,12 @@ namespace Novacoin
 
                 IEnumerable<byte> args1, args2;
 
-                byte last1 = script1.Bytes.Last();
-                byte last2 = script2.Bytes.Last();
+                int last1 = script1.Bytes.Count() -1;
+                int last2 = script2.Bytes.Count() - 1;
 
                 while (true)
                 {
-                    if (bq1.GetCurrent() == last1 && bq2.GetCurrent() == last2)
+                    if (bq1.CurrentIndex == last1 && bq2.CurrentIndex == last2)
                     {
                         // Found a match
                         typeRet = templateTuple.Item1;
@@ -831,8 +835,7 @@ namespace Novacoin
                     // Template matching opcodes:
                     if (opcode2 == instruction.OP_PUBKEYS)
                     {
-                        int PubKeyLen = args1.Count();
-                        while (PubKeyLen >= 33 && PubKeyLen <= 120)
+                        while (args1.Count() >= 33 && args1.Count() <= 120)
                         {
                             solutions.Add(args1);
                             if (!GetOp(ref bq1, out opcode1, out args1))
@@ -841,7 +844,9 @@ namespace Novacoin
                             }
                         }
                         if (!GetOp(ref bq2, out opcode2, out args2))
+                        {
                             break;
+                        }
                         // Normal situation is to fall through
                         // to other if/else statements
                     }
@@ -870,7 +875,7 @@ namespace Novacoin
                             byte n = (byte)DecodeOP_N(opcode1);
                             solutions.Add(new byte[] { n });
                         }
-                        catch (ArgumentException)
+                        catch (Exception)
                         {
                             break;
                         }
@@ -897,7 +902,15 @@ namespace Novacoin
             return false;
         }
 
-        public static Hash256 SignatureHash(CScript scriptCode, CTransaction txTo, int nIn, int nHashType)
+        /// <summary>
+        /// Generation of SignatureHash. This method is responsible for removal of transaction metadata. It's necessary signature can't sign itself. 
+        /// </summary>
+        /// <param name="script">Spending instructions</param>
+        /// <param name="txTo">Instance of transaction</param>
+        /// <param name="nIn">Input number</param>
+        /// <param name="nHashType">Hash type flag</param>
+        /// <returns></returns>
+        public static Hash256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType)
         {
             if (nIn >= txTo.vin.Length)
             {
@@ -906,18 +919,19 @@ namespace Novacoin
                 throw new ArgumentOutOfRangeException("nIn", sb.ToString());
             }
 
+            // Init a copy of transaction
             CTransaction txTmp = new CTransaction(txTo);
 
             // In case concatenating two scripts ends up with two codeseparators,
             // or an extra one at the end, this prevents all those possible incompatibilities.
-            scriptCode.RemovePattern(new byte[] { (byte)instruction.OP_CODESEPARATOR });
+            script.RemovePattern(new byte[] { (byte)instruction.OP_CODESEPARATOR });
 
             // Blank out other inputs' signatures
             for (int i = 0; i < txTmp.vin.Length; i++)
             {
                 txTmp.vin[i].scriptSig = new CScript();
             }
-            txTmp.vin[nIn].scriptSig = scriptCode;
+            txTmp.vin[nIn].scriptSig = script;
 
             // Blank out some of the outputs
             if ((nHashType & 0x1f) == (int)sigflag.SIGHASH_NONE)
@@ -976,6 +990,14 @@ namespace Novacoin
             return Hash256.Compute256(b);
         }
 
+        //
+        // Script is a stack machine (like Forth) that evaluates a predicate
+        // returning a bool indicating valid or not.  There are no loops.
+        //
+
+        /// <summary>
+        /// Script machine exception
+        /// </summary>
         public class StackMachineException : Exception
         {
             public StackMachineException()
@@ -993,12 +1015,6 @@ namespace Novacoin
             }
         }
 
-
-        //
-        // Script is a stack machine (like Forth) that evaluates a predicate
-        // returning a bool indicating valid or not.  There are no loops.
-        //
-
         /// <summary>
         /// Remove last element from stack
         /// </summary>
@@ -1081,6 +1097,16 @@ namespace Novacoin
             return new BigInteger(value.ToArray());
         }
 
+        /// <summary>
+        /// Execution of script
+        /// </summary>
+        /// <param name="stack"></param>
+        /// <param name="script">Script to execute</param>
+        /// <param name="txTo">Transaction instance</param>
+        /// <param name="nIn">Input number</param>
+        /// <param name="flags">Signature checking flags</param>
+        /// <param name="nHashType">Hash type flag</param>
+        /// <returns></returns>
         static bool EvalScript(ref List<IEnumerable<byte>> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType)
         {
             instruction opcode;
@@ -1166,7 +1192,7 @@ namespace Novacoin
                         case instruction.OP_16:
                             {
                                 // ( -- value)
-                                BigInteger bn = DecodeOP_N(opcode);
+                                BigInteger bn = DecodeOP_N(opcode, true);
                                 stack.Add(bn.ToByteArray());
                             }
                             break;
@@ -1187,8 +1213,8 @@ namespace Novacoin
                         case instruction.OP_NOP10:
                             {
                                 // Just do nothing
-                                break;
                             }
+                            break;
 
                         //
                         // Control
@@ -1727,7 +1753,6 @@ namespace Novacoin
                             }
                             break;
 
-
                         //
                         // Crypto
                         //
@@ -1739,36 +1764,32 @@ namespace Novacoin
                             {
                                 // (in -- hash)
                                 if (stack.Count() < 1)
-                                    return false;
-                                IEnumerable<byte> vch = stacktop(ref stack, -1);
-                                IEnumerable<byte> vchHash = null;
-                                if (opcode == instruction.OP_RIPEMD160)
-                                {
-                                    RIPEMD160 hash = RIPEMD160.Compute160(vch);
-                                    vchHash = hash.hashBytes;
-                                }
-                                else if (opcode == instruction.OP_SHA1)
-                                {
-                                    SHA1 hash = SHA1.Compute1(vch);
-                                    vchHash = hash.hashBytes;
-                                }
-                                else if (opcode == instruction.OP_SHA256)
-                                {
-                                    SHA256 hash = SHA256.Compute256(vch);
-                                    vchHash = hash.hashBytes;
-                                }
-                                else if (opcode == instruction.OP_HASH160)
                                 {
-                                    Hash160 hash = Hash160.Compute160(vch);
-                                    vchHash = hash.hashBytes;
+                                    return false;
                                 }
-                                else if (opcode == instruction.OP_HASH256)
+                                Hash hash = null;
+                                IEnumerable<byte> data = stacktop(ref stack, -1);
+
+                                switch (opcode)
                                 {
-                                    Hash256 hash = Hash256.Compute256(vch);
-                                    vchHash = hash.hashBytes;
+                                    case instruction.OP_HASH160:
+                                        hash = Hash160.Compute160(data);
+                                        break;
+                                    case instruction.OP_HASH256:
+                                        hash = Hash256.Compute256(data);
+                                        break;
+                                    case instruction.OP_SHA1:
+                                        hash = SHA1.Compute1(data);
+                                        break;
+                                    case instruction.OP_SHA256:
+                                        hash = SHA256.Compute256(data);
+                                        break;
+                                    case instruction.OP_RIPEMD160:
+                                        hash = RIPEMD160.Compute160(data);
+                                        break;
                                 }
                                 popstack(ref stack);
-                                stack.Add(vchHash);
+                                stack.Add(hash.hashBytes);
                             }
                             break;
 
@@ -1980,7 +2001,18 @@ namespace Novacoin
             return true;
         }
 
-        static bool CheckSig(IList<byte> vchSig, IList<byte> vchPubKey, CScript scriptCode, CTransaction txTo, int nIn, int nHashType, int flags)
+        /// <summary>
+        /// Check signature.
+        /// </summary>
+        /// <param name="sigBytes">Signature</param>
+        /// <param name="pubkeyBytes">Public key</param>
+        /// <param name="script">Spending script</param>
+        /// <param name="txTo">CTransaction instance</param>
+        /// <param name="nIn">Input number</param>
+        /// <param name="nHashType">Hashing type flag</param>
+        /// <param name="flags">Signature checking flags</param>
+        /// <returns></returns>
+        static bool CheckSig(IList<byte> sigBytes, IList<byte> pubkeyBytes, CScript script, CTransaction txTo, int nIn, int nHashType, int flags)
         {
             CPubKey pubkey;
 
@@ -1988,7 +2020,7 @@ namespace Novacoin
             {
                 // Trying to initialize the public key instance
 
-                pubkey = new CPubKey(vchPubKey);
+                pubkey = new CPubKey(pubkeyBytes);
             }
             catch (Exception)
             {
@@ -2002,7 +2034,7 @@ namespace Novacoin
                 return false;
             }
 
-            if (vchSig.Count == 0)
+            if (sigBytes.Count == 0)
             {
                 return false;
             }
@@ -2010,19 +2042,19 @@ namespace Novacoin
             // Hash type is one byte tacked on to the end of the signature
             if (nHashType == 0)
             {
-                nHashType = vchSig.Last();
+                nHashType = sigBytes.Last();
             }
-            else if (nHashType != vchSig.Last())
+            else if (nHashType != sigBytes.Last())
             {
                 return false;
             }
 
             // Remove hash type
-            vchSig.RemoveAt(vchSig.Count - 1);
+            sigBytes.RemoveAt(sigBytes.Count - 1);
 
-            Hash256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType);
+            Hash256 sighash = SignatureHash(script, txTo, nIn, nHashType);
 
-            if (!pubkey.VerifySignature(sighash, vchSig))
+            if (!pubkey.VerifySignature(sighash, sigBytes))
             {
                 return false;
             }