SetDestination fixes and tests
[NovacoinLibrary.git] / Novacoin / CScript.cs
index f5ce417..bb0e5a5 100644 (file)
@@ -1,8 +1,6 @@
 \feffusing System;
 using System.Linq;
 using System.Text;
-
-using System.Collections;
 using System.Collections.Generic;
 
 namespace Novacoin
@@ -98,8 +96,8 @@ namespace Novacoin
         /// <summary>
         /// Create new OP_PUSHDATAn operator and add it to opcode bytes list
         /// </summary>
-        /// <param name="dataBytes">List of data bytes</param>
-        public void PushData(IList<byte> dataBytes)
+        /// <param name="dataBytes">Set of data bytes</param>
+        public void PushData(IEnumerable<byte> dataBytes)
         {
             long nCount = dataBytes.LongCount();
 
@@ -177,77 +175,107 @@ namespace Novacoin
         /// Is it true that script doesn't contain anything except push value operations?
         /// </summary>
         /// <returns>Checking result</returns>
-        public bool IsPushonly()
+        public bool IsPushonly
         {
-            WrappedList<byte> wCodeBytes = new WrappedList<byte>(codeBytes);
-
-            opcodetype opcode; // Current opcode
-            IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
-            
-            // Scan opcodes sequence
-            while (ScriptOpcode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+            get
             {
-                if (opcode > opcodetype.OP_16)
+                WrappedList<byte> wCodeBytes = new WrappedList<byte>(codeBytes);
+
+                opcodetype opcode; // Current opcode
+                IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
+
+                // Scan opcodes sequence
+                while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
                 {
-                    // We don't allow control opcodes here
-                    return false;
+                    if (opcode > opcodetype.OP_16)
+                    {
+                        // We don't allow control opcodes here
+                        return false;
+                    }
                 }
-            }
 
-            return true;
+                return true;
+            }
         }
 
         /// <summary>
         /// Is it true that script doesn't contain non-canonical push operations?
         /// </summary>
         /// <returns>Checking result</returns>
-        public bool HashOnlyCanonicalPushes()
+        public bool HasOnlyCanonicalPushes
         {
-            WrappedList<byte> wCodeBytes = new WrappedList<byte>(codeBytes);
-
-            opcodetype opcode; // Current opcode
-            IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
-
-            // Scan opcodes sequence
-            while (ScriptOpcode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+            get
             {
-                byte[] data = pushArgs.ToArray();
+                WrappedList<byte> wCodeBytes = new WrappedList<byte>(codeBytes);
 
-                if (opcode < opcodetype.OP_PUSHDATA1 && opcode > opcodetype.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 == opcodetype.OP_PUSHDATA1 && data.Length < (int)opcodetype.OP_PUSHDATA1)
-                {
-                    // Could have used a normal n-byte push, rather than OP_PUSHDATA1.
-                    return false;
-                }
-                if (opcode == opcodetype.OP_PUSHDATA2 && data.Length <= 0xFF)
-                {
-                    // Could have used an OP_PUSHDATA1.
-                    return false;
-                }
-                if (opcode == opcodetype.OP_PUSHDATA4 && data.LongLength <= 0xFFFF)
+                opcodetype opcode; // Current opcode
+                IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
+
+                // Scan opcodes sequence
+                while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
                 {
-                    // Could have used an OP_PUSHDATA2.
-                    return false;
+                    byte[] data = pushArgs.ToArray();
+
+                    if (opcode < opcodetype.OP_PUSHDATA1 && opcode > opcodetype.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 == opcodetype.OP_PUSHDATA1 && data.Length < (int)opcodetype.OP_PUSHDATA1)
+                    {
+                        // Could have used a normal n-byte push, rather than OP_PUSHDATA1.
+                        return false;
+                    }
+                    if (opcode == opcodetype.OP_PUSHDATA2 && data.Length <= 0xFF)
+                    {
+                        // Could have used an OP_PUSHDATA1.
+                        return false;
+                    }
+                    if (opcode == opcodetype.OP_PUSHDATA4 && data.LongLength <= 0xFFFF)
+                    {
+                        // Could have used an OP_PUSHDATA2.
+                        return false;
+                    }
                 }
-            }
 
-            return true;
+                return true;
+            }
         }
 
         /// <summary>
         /// Quick test for pay-to-script-hash CScripts
         /// </summary>
         /// <returns>Checking result</returns>
-        public bool IsPayToScriptHash()
+        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)opcodetype.OP_HASH160 &&
+                        codeBytes[1] == 0x14 && // 20 bytes hash length prefix
+                        codeBytes[22] == (byte)opcodetype.OP_EQUAL);
+            }
+        }
+
+        /// <summary>
+        /// Quick test for pay-to-pubkeyhash CScripts
+        /// </summary>
+        /// <returns>Checking result</returns>
+        public bool IsPayToPubKeyHash
         {
-            return (codeBytes.Count() == 23 &&
-                    codeBytes[0] == (byte)opcodetype.OP_HASH160 &&
-                    codeBytes[1] == 0x14 &&
-                    codeBytes[22] == (byte)opcodetype.OP_EQUAL);
+            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)opcodetype.OP_DUP &&
+                        codeBytes[1] == (byte)opcodetype.OP_HASH160 &&
+                        codeBytes[2] == 0x14 && // 20 bytes hash length prefix
+                        codeBytes[23] == (byte)opcodetype.OP_EQUALVERIFY &&
+                        codeBytes[24] == (byte)opcodetype.OP_CHECKSIG);
+            }
         }
 
         /// <summary>
@@ -270,7 +298,7 @@ namespace Novacoin
             opcodetype lastOpcode = opcodetype.OP_INVALIDOPCODE;
 
             // Scan opcodes sequence
-            while (ScriptOpcode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+            while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
             {
                 if (opcode == opcodetype.OP_CHECKSIG || opcode == opcodetype.OP_CHECKSIGVERIFY)
                 {
@@ -280,7 +308,7 @@ namespace Novacoin
                 {
                     if (fAccurate && lastOpcode >= opcodetype.OP_1 && lastOpcode <= opcodetype.OP_16)
                     {
-                        nCount += ScriptOpcode.DecodeOP_N(lastOpcode);
+                        nCount += ScriptCode.DecodeOP_N(lastOpcode);
                     }
                     else
                     {
@@ -300,7 +328,7 @@ namespace Novacoin
         /// <returns>SigOps count</returns>
         public int GetSigOpCount(CScript scriptSig)
         {
-            if (!IsPayToScriptHash())
+            if (!IsPayToScriptHash)
             {
                 return GetSigOpCount(true);
             }
@@ -313,7 +341,7 @@ namespace Novacoin
             opcodetype opcode; // Current opcode
             IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
 
-            while (ScriptOpcode.GetOp(ref wScriptSig, out opcode, out pushArgs))
+            while (ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs))
             {
                 if (opcode > opcodetype.OP_16)
                 {
@@ -328,15 +356,35 @@ namespace Novacoin
 
         }
 
+        /// <summary>
+        /// Set pay-to-pubkey destination.
+        /// </summary>
+        /// <param name="pubKey">Instance of CPubKey.</param>
+        public void SetDestination(CPubKey pubKey)
+        {
+            codeBytes.Clear();
+            PushData(pubKey.PublicBytes);
+            AddOp(opcodetype.OP_CHECKSIG);
+        }
+
+        /// <summary>
+        /// Set pay-to-pubkeyhash destination
+        /// </summary>
+        /// <param name="ID">Public key hash</param>
         public void SetDestination(CKeyID ID)
         {
             codeBytes.Clear();
             AddOp(opcodetype.OP_DUP);
             AddOp(opcodetype.OP_HASH160);
             AddHash(ID);
-            AddOp(opcodetype.OP_EQUAL);
+            AddOp(opcodetype.OP_EQUALVERIFY);
+            AddOp(opcodetype.OP_CHECKSIG);
         }
 
+        /// <summary>
+        /// Set pay-to-scripthash destination
+        /// </summary>
+        /// <param name="ID">Script hash</param>
         public void SetDestination(CScriptID ID)
         {
             codeBytes.Clear();
@@ -345,20 +393,41 @@ namespace Novacoin
             AddOp(opcodetype.OP_EQUAL);
         }
 
-        public void SetMultiSig(int nRequired, IEnumerable<CKey> keys)
+        /// <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, IEnumerable<CPubKey> keys)
         {
             codeBytes.Clear();
-            AddOp(ScriptOpcode.EncodeOP_N(nRequired));
+            AddOp(ScriptCode.EncodeOP_N(nRequired));
 
-            foreach (CKey key in keys)
+            foreach (CPubKey key in keys)
             {
-                PushData(key.GetPubKey().Raw);
+                PushData(key.PublicBytes.ToList());
             }
-            AddOp(ScriptOpcode.EncodeOP_N(keys.Count()));
+            AddOp(ScriptCode.EncodeOP_N(keys.Count()));
             AddOp(opcodetype.OP_CHECKMULTISIG);
         }
 
         /// <summary>
+        /// Access to script code.
+        /// </summary>
+        public IEnumerable<byte> Bytes
+        {
+            get { return codeBytes; }
+        }
+
+        /// <summary>
         /// Disassemble current script code
         /// </summary>
         /// <returns>Code listing</returns>
@@ -369,7 +438,7 @@ namespace Novacoin
 
             opcodetype opcode; // Current opcode
             IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
-            while (ScriptOpcode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
+            while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
             {
                 if (sb.Length != 0)
                 {
@@ -378,11 +447,11 @@ namespace Novacoin
 
                 if (0 <= opcode && opcode <= opcodetype.OP_PUSHDATA4)
                 {
-                    sb.Append(ScriptOpcode.ValueString(pushArgs));
+                    sb.Append(ScriptCode.ValueString(pushArgs));
                 }
                 else
                 {
-                    sb.Append(ScriptOpcode.GetOpName(opcode));
+                    sb.Append(ScriptCode.GetOpName(opcode));
                 }
             }