Use BouncyCastle hashing functions
[NovacoinLibrary.git] / Novacoin / CScript.cs
1 \feff/**
2  *  Novacoin classes library
3  *  Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
4
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 using System;
20 using System.Linq;
21 using System.Text;
22 using System.Collections.Generic;
23
24 namespace Novacoin
25 {
26     public class CScriptException : Exception
27     {
28         public CScriptException()
29         {
30         }
31
32         public CScriptException(string message)
33             : base(message)
34         {
35         }
36
37         public CScriptException(string message, Exception inner)
38             : base(message, inner)
39         {
40         }
41     }
42
43     /// <summary>
44     /// Representation of script code
45     /// </summary>
46         public class CScript
47         {
48         private List<byte> codeBytes;
49
50         /// <summary>
51         /// Initializes an empty instance of CScript
52         /// </summary>
53                 public CScript ()
54                 {
55             codeBytes = new List<byte>();
56                 }
57
58         /// <summary>
59         /// Initializes new instance of CScript and fills it with supplied bytes
60         /// </summary>
61         /// <param name="bytes">Enumerator interface for byte sequence</param>
62         public CScript(IEnumerable<byte> bytes)
63         {
64             codeBytes = new List<byte>(bytes);
65         }
66
67         /// <summary>
68         /// Return a new instance of ByteQueue object for current code bytes
69         /// </summary>
70         /// <returns></returns>
71         public ByteQueue GetByteQUeue()
72         {
73              return new ByteQueue(codeBytes);
74         }
75
76         /// <summary>
77         /// Adds specified operation to opcode bytes list
78         /// </summary>
79         /// <param name="opcode"></param>
80         public void AddOp(instruction opcode)
81         {
82             if (opcode < instruction.OP_0 || opcode > instruction.OP_INVALIDOPCODE)
83             {
84                 throw new CScriptException("CScript::AddOp() : invalid opcode");
85             }
86
87             codeBytes.Add((byte)opcode);
88         }
89
90         /// <summary>
91         /// Adds hash to opcode bytes list.
92         ///    New items are added in this format:
93         ///    hash_length_byte hash_bytes
94         /// </summary>
95         /// <param name="hash">Hash160 instance</param>
96         public void AddHash(Hash160 hash)
97         {
98             codeBytes.Add((byte)hash.hashSize);
99             codeBytes.AddRange(hash.hashBytes);
100         }
101
102         /// <summary>
103         /// Adds hash to opcode bytes list.
104         ///    New items are added in this format:
105         ///    hash_length_byte hash_bytes
106         /// </summary>
107         /// <param name="hash">Hash256 instance</param>
108         public void AddHash(Hash256 hash)
109         {
110             codeBytes.Add((byte)hash.hashSize);
111             codeBytes.AddRange(hash.hashBytes);
112         }
113
114         /// <summary>
115         /// Create new OP_PUSHDATAn operator and add it to opcode bytes list
116         /// </summary>
117         /// <param name="dataBytes">Set of data bytes</param>
118         public void PushData(byte[] dataBytes)
119         {
120             var nCount = dataBytes.LongLength;
121
122             if (nCount < (int)instruction.OP_PUSHDATA1)
123             {
124                 // OP_0 and OP_FALSE
125                 codeBytes.Add((byte)nCount);
126             }
127             else if (nCount < 0xff)
128             {
129                 // OP_PUSHDATA1 0x01 [0x5a]
130                 codeBytes.Add((byte)instruction.OP_PUSHDATA1);
131                 codeBytes.Add((byte)nCount);
132             }
133             else if (nCount < 0xffff)
134             {
135                 // OP_PUSHDATA1 0x00 0x01 [0x5a]
136                 codeBytes.Add((byte)instruction.OP_PUSHDATA2);
137
138                 byte[] szBytes = Interop.BEBytes((ushort)nCount);
139                 codeBytes.AddRange(szBytes);
140             }
141             else if (nCount < 0xffffffff)
142             {
143                 // OP_PUSHDATA1 0x00 0x00 0x00 0x01 [0x5a]
144                 codeBytes.Add((byte)instruction.OP_PUSHDATA4);
145
146                 byte[] szBytes = Interop.BEBytes((uint)nCount);
147                 codeBytes.AddRange(szBytes);
148             }
149
150             // Add data bytes
151             codeBytes.AddRange(dataBytes);
152         }
153
154         /// <summary>
155         /// Scan code bytes for pattern
156         /// </summary>
157         /// <param name="pattern">Pattern sequence</param>
158         /// <returns>Matches enumerator</returns>
159         private IEnumerable<int> FindPattern(byte[] pattern)
160         {
161             for (int i = 0; i < codeBytes.Count; i++)
162             {
163                 if (codeBytes.Skip(i).Take(pattern.Length).SequenceEqual(pattern))
164                 {
165                     yield return i;
166                 }
167             }
168         }
169
170         /// <summary>
171         /// Scan code bytes for pattern and remove it
172         /// </summary>
173         /// <param name="pattern">Pattern sequence</param>
174         /// <returns>Matches number</returns>
175         public int RemovePattern(byte[] pattern)
176         {
177             var resultBytes = new List<byte>(codeBytes);
178             int count = 0;
179                         
180             foreach (int i in FindPattern(pattern))
181             {
182                 resultBytes.RemoveRange(i - count * pattern.Length, pattern.Length);
183                 count++;
184             }
185
186             codeBytes = resultBytes;
187             
188             return count;
189         }
190
191         /// <summary>
192         /// Is it true that script doesn't contain anything except push value operations?
193         /// </summary>
194         public bool IsPushOnly
195         {
196             get
197             {
198                 var wCodeBytes = new ByteQueue(codeBytes);
199
200                 instruction opcode; // Current opcode
201                 byte[] pushArgs; // OP_PUSHDATAn argument
202
203                 // Scan opcodes sequence
204                 while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
205                 {
206                     if (opcode > instruction.OP_16)
207                     {
208                         // We don't allow control opcodes here
209                         return false;
210                     }
211                 }
212
213                 return true;
214             }
215         }
216
217         /// <summary>
218         /// Is it true that script doesn't contain non-canonical push operations?
219         /// </summary>
220         public bool HasOnlyCanonicalPushes
221         {
222             get
223             {
224                 var wCodeBytes = new ByteQueue(codeBytes);
225
226                 byte[] pushArgs; // OP_PUSHDATAn argument
227                 instruction opcode; // Current opcode
228
229                 // Scan opcodes sequence
230                 while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
231                 {
232                     var data = pushArgs;
233
234                     if (opcode < instruction.OP_PUSHDATA1 && opcode > instruction.OP_0 && (data.Length == 1 && data[0] <= 16))
235                     {
236                         // Could have used an OP_n code, rather than a 1-byte push.
237                         return false;
238                     }
239                     if (opcode == instruction.OP_PUSHDATA1 && data.Length < (int)instruction.OP_PUSHDATA1)
240                     {
241                         // Could have used a normal n-byte push, rather than OP_PUSHDATA1.
242                         return false;
243                     }
244                     if (opcode == instruction.OP_PUSHDATA2 && data.Length <= 0xFF)
245                     {
246                         // Could have used an OP_PUSHDATA1.
247                         return false;
248                     }
249                     if (opcode == instruction.OP_PUSHDATA4 && data.LongLength <= 0xFFFF)
250                     {
251                         // Could have used an OP_PUSHDATA2.
252                         return false;
253                     }
254                 }
255
256                 return true;
257             }
258         }
259
260         /// <summary>
261         /// Quick test for pay-to-script-hash CScripts
262         /// </summary>
263         public bool IsPayToScriptHash
264         {
265             get
266             {
267                 // Sender provides redeem script hash, receiver provides signature list and redeem script
268                 // OP_HASH160 20 [20 byte hash] OP_EQUAL
269                 return (codeBytes.Count() == 23 &&
270                         codeBytes[0] == (byte)instruction.OP_HASH160 &&
271                         codeBytes[1] == 0x14 && // 20 bytes hash length prefix
272                         codeBytes[22] == (byte)instruction.OP_EQUAL);
273             }
274         }
275
276         /// <summary>
277         /// Quick test for pay-to-pubkeyhash CScripts
278         /// </summary>
279         public bool IsPayToPubKeyHash
280         {
281             get
282             {
283                 // Sender provides hash of pubkey, receiver provides signature and pubkey
284                 // OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
285                 return (codeBytes.Count == 25 &&
286                         codeBytes[0] == (byte)instruction.OP_DUP &&
287                         codeBytes[1] == (byte)instruction.OP_HASH160 &&
288                         codeBytes[2] == 0x14 && // 20 bytes hash length prefix
289                         codeBytes[23] == (byte)instruction.OP_EQUALVERIFY &&
290                         codeBytes[24] == (byte)instruction.OP_CHECKSIG);
291             }
292         }
293
294         /// <summary>
295         /// Quick test for Null destination
296         /// </summary>
297         public bool IsNull
298         {
299             get { return codeBytes.Count == 0; }
300         }
301
302         /// <summary>
303         /// Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
304         /// as 20 sigops. With pay-to-script-hash, that changed:
305         /// CHECKMULTISIGs serialized in scriptSigs are
306         /// counted more accurately, assuming they are of the form
307         ///  ... OP_N CHECKMULTISIG ...
308         /// </summary>
309         /// <param name="fAccurate">Legacy mode flag</param>
310         /// <returns>Amount of sigops</returns>
311         public int GetSigOpCount(bool fAccurate)
312         {
313             var wCodeBytes = new ByteQueue(codeBytes);
314
315             instruction opcode; // Current opcode
316             byte[] pushArgs; // OP_PUSHDATAn argument
317
318             int nCount = 0;
319             var lastOpcode = instruction.OP_INVALIDOPCODE;
320
321             // Scan opcodes sequence
322             while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
323             {
324                 if (opcode == instruction.OP_CHECKSIG || opcode == instruction.OP_CHECKSIGVERIFY)
325                 {
326                     nCount++;
327                 }
328                 else if (opcode == instruction.OP_CHECKMULTISIG || opcode == instruction.OP_CHECKMULTISIGVERIFY)
329                 {
330                     if (fAccurate && lastOpcode >= instruction.OP_1 && lastOpcode <= instruction.OP_16)
331                     {
332                         nCount += ScriptCode.DecodeOP_N(lastOpcode);
333                     }
334                     else
335                     {
336                         nCount += 20;
337                     }
338                 }
339             }
340
341             return nCount;
342         }
343
344         /// <summary>
345         /// Accurately count sigOps, including sigOps in
346         /// pay-to-script-hash transactions
347         /// </summary>
348         /// <param name="scriptSig">pay-to-script-hash scriptPubKey</param>
349         /// <returns>SigOps count</returns>
350         public int GetSigOpCount(CScript scriptSig)
351         {
352             if (!IsPayToScriptHash)
353             {
354                 return GetSigOpCount(true);
355             }
356
357             // This is a pay-to-script-hash scriptPubKey;
358             // get the last item that the scriptSig
359             // pushes onto the stack:
360             ByteQueue wScriptSig = scriptSig.GetByteQUeue();
361
362             instruction opcode; // Current opcode
363             byte[] pushArgs; // OP_PUSHDATAn argument
364
365             while (ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs))
366             {
367                 if (opcode > instruction.OP_16)
368                 {
369                     return 0;
370                 }
371             }
372
373             /// ... and return its opcount:
374             var subScript = new CScript(pushArgs);
375
376             return subScript.GetSigOpCount(true);
377
378         }
379
380         /// <summary>
381         /// Set pay-to-pubkey destination.
382         /// </summary>
383         /// <param name="pubKey">Instance of CPubKey.</param>
384         public void SetDestination(CPubKey pubKey)
385         {
386             codeBytes.Clear();
387             PushData(pubKey.PublicBytes);
388             AddOp(instruction.OP_CHECKSIG);
389         }
390
391         /// <summary>
392         /// Set pay-to-pubkeyhash destination
393         /// </summary>
394         /// <param name="ID">Public key hash</param>
395         public void SetDestination(CKeyID ID)
396         {
397             codeBytes.Clear();
398             AddOp(instruction.OP_DUP);
399             AddOp(instruction.OP_HASH160);
400             AddHash(ID);
401             AddOp(instruction.OP_EQUALVERIFY);
402             AddOp(instruction.OP_CHECKSIG);
403         }
404
405         /// <summary>
406         /// Set pay-to-scripthash destination
407         /// </summary>
408         /// <param name="ID">Script hash</param>
409         public void SetDestination(CScriptID ID)
410         {
411             codeBytes.Clear();
412             AddOp(instruction.OP_HASH160);
413             AddHash(ID);
414             AddOp(instruction.OP_EQUAL);
415         }
416
417         /// <summary>
418         /// Reset script code buffer.
419         /// </summary>
420         public void SetNullDestination()
421         {
422             codeBytes.Clear();
423         }
424
425         /// <summary>
426         /// Set multisig destination.
427         /// </summary>
428         /// <param name="nRequired">Amount of required signatures.</param>
429         /// <param name="keys">Set of public keys.</param>
430         public void SetMultiSig(int nRequired, CPubKey[] keys)
431         {
432             codeBytes.Clear();
433             AddOp(ScriptCode.EncodeOP_N(nRequired));
434
435             foreach (var key in keys)
436             {
437                 PushData(key.PublicBytes);
438             }
439
440             AddOp(ScriptCode.EncodeOP_N(keys.Length));
441             AddOp(instruction.OP_CHECKMULTISIG);
442         }
443
444         /// <summary>
445         /// Access to script code.
446         /// </summary>
447         public byte[] Bytes
448         {
449             get { return codeBytes.ToArray(); }
450         }
451
452         public CScriptID ScriptID
453         {
454             get { return new CScriptID(Hash160.Compute160(codeBytes.ToArray())); }
455         }
456
457         /// <summary>
458         /// Disassemble current script code
459         /// </summary>
460         /// <returns>Code listing</returns>
461                 public override string ToString()
462                 {
463                         var sb = new StringBuilder();
464             var wCodeBytes = new ByteQueue(codeBytes);
465
466             instruction opcode; // Current opcode
467             byte[] pushArgs; // OP_PUSHDATAn argument
468             while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
469             {
470                 if (sb.Length != 0)
471                 {
472                     sb.Append(" ");
473                 }
474
475                 if (0 <= opcode && opcode <= instruction.OP_PUSHDATA4)
476                 {
477                     sb.Append(ScriptCode.ValueString(pushArgs));
478                 }
479                 else
480                 {
481                     sb.Append(ScriptCode.GetOpName(opcode));
482                 }
483             }
484
485             return sb.ToString();
486                 }
487         }
488 }