Enforce rule that the coinbase starts with serialized block height.
[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 using System.Diagnostics.Contracts;
24 using System.Numerics;
25
26 namespace Novacoin
27 {
28     /// <summary>
29     /// Representation of script code
30     /// </summary>
31         public class CScript
32         {
33         private List<byte> codeBytes;
34
35         /// <summary>
36         /// Initializes an empty instance of CScript
37         /// </summary>
38                 public CScript ()
39                 {
40             codeBytes = new List<byte>();
41                 }
42
43         /// <summary>
44         /// Initializes new instance of CScript and fills it with supplied bytes
45         /// </summary>
46         /// <param name="bytes">Enumerator interface for byte sequence</param>
47         public CScript(byte[] bytes)
48         {
49             codeBytes = new List<byte>(bytes);
50         }
51
52         /// <summary>
53         /// Return a new instance of ByteQueue object for current code bytes
54         /// </summary>
55         /// <returns></returns>
56         public InstructionQueue GetInstructionQueue()
57         {
58              return new InstructionQueue(ref codeBytes);
59         }
60
61         /// <summary>
62         /// Add serialized number to instructions list.
63         /// </summary>
64         /// <param name="n">Number to add.</param>
65         public void AddNumber(int n)
66         {
67             if (n == -1 || (n >= 1 && n <= 16))
68             {
69                 codeBytes.Add((byte)ScriptCode.EncodeOP_N(n, true));
70             }
71             else
72             {
73                 BigInteger bn = n;
74                 PushData(bn.ToByteArray());
75             }
76         }
77
78         /// <summary>
79         /// Adds specified operation to instruction list
80         /// </summary>
81         /// <param name="opcode">Instruction to add.</param>
82         public void AddInstruction(instruction opcode)
83         {
84             Contract.Requires<ArgumentException>(opcode >= instruction.OP_0 && opcode <= instruction.OP_INVALIDOPCODE, "Invalid instruction.");
85
86             codeBytes.Add((byte)opcode);
87         }
88
89         /// <summary>
90         /// Adds hash to instruction list.
91         ///    New items are added in this format:
92         ///    hash_length_byte hash_bytes
93         /// </summary>
94         /// <param name="hash">uint160 instance</param>
95         public void AddHash(uint160 hash)
96         {
97             codeBytes.Add((byte)hash.Size);
98             codeBytes.AddRange((byte[])hash);
99         }
100
101         /// <summary>
102         /// Adds hash to instruction list.
103         ///    New items are added in this format:
104         ///    hash_length_byte hash_bytes
105         /// </summary>
106         /// <param name="hash">uint256 instance</param>
107         public void AddHash(uint256 hash)
108         {
109             codeBytes.Add((byte)hash.Size);
110             codeBytes.AddRange((byte[])hash);
111         }
112
113         /// <summary>
114         /// Create new OP_PUSHDATAn operator and add it to instruction list
115         /// </summary>
116         /// <param name="dataBytes">Set of data bytes</param>
117         public void PushData(byte[] dataBytes)
118         {
119             var nCount = dataBytes.LongLength;
120
121             if (nCount < (int)instruction.OP_PUSHDATA1)
122             {
123                 // OP_0 and OP_FALSE
124                 codeBytes.Add((byte)nCount);
125             }
126             else if (nCount < 0xff)
127             {
128                 // OP_PUSHDATA1 0x01 [0x5a]
129                 codeBytes.Add((byte)instruction.OP_PUSHDATA1);
130                 codeBytes.Add((byte)nCount);
131             }
132             else if (nCount < 0xffff)
133             {
134                 // OP_PUSHDATA1 0x01 0x00 [0x5a]
135                 codeBytes.Add((byte)instruction.OP_PUSHDATA2);
136
137                 var szBytes = BitConverter.GetBytes((ushort)nCount);
138                 codeBytes.AddRange(szBytes);
139             }
140             else if (nCount < 0xffffffff)
141             {
142                 // OP_PUSHDATA1 0x01 0x00 0x00 0x00 [0x5a]
143                 codeBytes.Add((byte)instruction.OP_PUSHDATA4);
144
145                 var szBytes = BitConverter.GetBytes((uint)nCount);
146                 codeBytes.AddRange(szBytes);
147             }
148
149             // Add data bytes
150             codeBytes.AddRange(dataBytes);
151         }
152
153         /// <summary>
154         /// Just insert data array without including any prefixes. Please make sure that you know what you're doing, 
155         ///    it is recommended to use AddInstruction, AddHash or PushData instead.
156         /// </summary>
157         /// <param name="dataBytes">Data bytes</param>
158         public void AddRawData(byte[] dataBytes)
159         {
160             // Add data bytes
161             codeBytes.AddRange(dataBytes);
162         }
163
164         /// <summary>
165         /// Scan pushed data bytes for pattern and, in case of exact match, remove it.
166         /// </summary>
167         /// <param name="pattern">Pattern sequence</param>
168         /// <returns>Matches count</returns>
169         public int RemovePattern(byte[] pattern)
170         {
171             // There is no sense to continue if pattern is empty or longer than script itself
172             if (pattern.Length == 0 || pattern.Length > codeBytes.Count)
173             {
174                 return 0;
175             }
176
177             var count = 0;
178             var bq1 = new InstructionQueue(ref codeBytes);
179
180             byte[] pushData;
181             instruction opcode;
182
183             var newScript = new CScript();
184
185             while (ScriptCode.GetOp(ref bq1, out opcode, out pushData))
186             {
187                 if (pushData.Length == 0)
188                 {
189                     // No data, put instruction on its place
190                     newScript.AddInstruction(opcode);
191                 }
192                 else if (!pushData.SequenceEqual(pattern))
193                 {
194                     // No match, create push operator
195                     newScript.PushData(pushData);
196                 }
197                 else
198                 {
199                     count++; // match
200                 }
201             }
202
203             if (count > 0)
204             {
205                 // Replace current script if any matches were found
206                 codeBytes = newScript.codeBytes;
207             }
208
209             return count;
210         }
211
212         /// <summary>
213         /// Scan script for specific instruction and remove it if there are some matches.
214         /// </summary>
215         /// <param name="op">Instruction</param>
216         /// <returns>Matches count</returns>
217         public int RemoveInstruction(instruction op)
218         {
219             byte[] pushData;
220             instruction opcode;
221
222             var count = 0;
223             var newScript = new CScript();
224             var bq1 = new InstructionQueue(ref codeBytes);
225
226             while (ScriptCode.GetOp(ref bq1, out opcode, out pushData))
227             {
228                 if (pushData.Length != 0 && op != opcode)
229                 {
230                     // If instruction didn't match then push its data again
231                     newScript.PushData(pushData);
232                 }
233                 else if (Enum.IsDefined(typeof(instruction), op) && op != opcode)
234                 {
235                     // Instruction didn't match
236                     newScript.AddInstruction(opcode);
237                 }
238                 else
239                 {
240                     count++; // match
241                 }
242             }
243
244             if (count > 0)
245             {
246                 // Replace current script if any matches were found
247                 codeBytes = newScript.codeBytes;
248             }
249
250             return count;
251         }
252
253         /// <summary>
254         /// Is it true that script doesn't contain anything except push value operations?
255         /// </summary>
256         public bool IsPushOnly
257         {
258             get
259             {
260                 var wCodeBytes = new InstructionQueue(ref codeBytes);
261
262                 instruction opcode; // Current instruction
263                 byte[] pushArgs; // OP_PUSHDATAn argument
264
265                 // Scan instructions sequence
266                 while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
267                 {
268                     if (opcode > instruction.OP_16)
269                     {
270                         // We don't allow control instructions here
271                         return false;
272                     }
273                 }
274
275                 return true;
276             }
277         }
278
279         /// <summary>
280         /// Is it true that script doesn't contain non-canonical push operations?
281         /// </summary>
282         public bool HasOnlyCanonicalPushes
283         {
284             get
285             {
286                 var wCodeBytes = new InstructionQueue(ref codeBytes);
287
288                 byte[] pushArgs; // OP_PUSHDATAn argument
289                 instruction opcode; // Current instruction
290
291                 // Scan instructions sequence
292                 while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
293                 {
294                     var data = pushArgs;
295
296                     if (opcode < instruction.OP_PUSHDATA1 && opcode > instruction.OP_0 && (data.Length == 1 && data[0] <= 16))
297                     {
298                         // Could have used an OP_n code, rather than a 1-byte push.
299                         return false;
300                     }
301                     if (opcode == instruction.OP_PUSHDATA1 && data.Length < (int)instruction.OP_PUSHDATA1)
302                     {
303                         // Could have used a normal n-byte push, rather than OP_PUSHDATA1.
304                         return false;
305                     }
306                     if (opcode == instruction.OP_PUSHDATA2 && data.Length <= 0xFF)
307                     {
308                         // Could have used an OP_PUSHDATA1.
309                         return false;
310                     }
311                     if (opcode == instruction.OP_PUSHDATA4 && data.LongLength <= 0xFFFF)
312                     {
313                         // Could have used an OP_PUSHDATA2.
314                         return false;
315                     }
316                 }
317
318                 return true;
319             }
320         }
321
322         /// <summary>
323         /// Quick test for pay-to-script-hash CScripts
324         /// </summary>
325         public bool IsPayToScriptHash
326         {
327             get
328             {
329                 // Sender provides redeem script hash, receiver provides signature list and redeem script
330                 // OP_HASH160 20 [20 byte hash] OP_EQUAL
331                 return (codeBytes.Count() == 23 &&
332                         codeBytes[0] == (byte)instruction.OP_HASH160 &&
333                         codeBytes[1] == 0x14 && // 20 bytes hash length prefix
334                         codeBytes[22] == (byte)instruction.OP_EQUAL);
335             }
336         }
337
338         /// <summary>
339         /// Quick test for pay-to-pubkeyhash CScripts
340         /// </summary>
341         public bool IsPayToPubKeyHash
342         {
343             get
344             {
345                 // Sender provides hash of pubkey, receiver provides signature and pubkey
346                 // OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
347                 return (codeBytes.Count == 25 &&
348                         codeBytes[0] == (byte)instruction.OP_DUP &&
349                         codeBytes[1] == (byte)instruction.OP_HASH160 &&
350                         codeBytes[2] == 0x14 && // 20 bytes hash length prefix
351                         codeBytes[23] == (byte)instruction.OP_EQUALVERIFY &&
352                         codeBytes[24] == (byte)instruction.OP_CHECKSIG);
353             }
354         }
355
356         /// <summary>
357         /// Quick test for Null destination
358         /// </summary>
359         public bool IsNull
360         {
361             get { return codeBytes.Count == 0; }
362         }
363
364         /// <summary>
365         /// Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
366         /// as 20 sigops. With pay-to-script-hash, that changed:
367         /// CHECKMULTISIGs serialized in scriptSigs are
368         /// counted more accurately, assuming they are of the form
369         ///  ... OP_N CHECKMULTISIG ...
370         /// </summary>
371         /// <param name="fAccurate">Legacy mode flag</param>
372         /// <returns>Amount of sigops</returns>
373         public uint GetSigOpCount(bool fAccurate)
374         {
375             var wCodeBytes = new InstructionQueue(ref codeBytes);
376
377             instruction opcode; // Current instruction
378             byte[] pushArgs; // OP_PUSHDATAn argument
379
380             uint nCount = 0;
381             var lastOpcode = instruction.OP_INVALIDOPCODE;
382
383             // Scan instructions sequence
384             while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
385             {
386                 if (opcode == instruction.OP_CHECKSIG || opcode == instruction.OP_CHECKSIGVERIFY)
387                 {
388                     nCount++;
389                 }
390                 else if (opcode == instruction.OP_CHECKMULTISIG || opcode == instruction.OP_CHECKMULTISIGVERIFY)
391                 {
392                     if (fAccurate && lastOpcode >= instruction.OP_1 && lastOpcode <= instruction.OP_16)
393                     {
394                         nCount += (uint)ScriptCode.DecodeOP_N(lastOpcode);
395                     }
396                     else
397                     {
398                         nCount += 20;
399                     }
400                 }
401             }
402
403             return nCount;
404         }
405
406         /// <summary>
407         /// Accurately count sigOps, including sigOps in
408         /// pay-to-script-hash transactions
409         /// </summary>
410         /// <param name="scriptSig">pay-to-script-hash scriptPubKey</param>
411         /// <returns>SigOps count</returns>
412         public uint GetSigOpCount(CScript scriptSig)
413         {
414             if (!IsPayToScriptHash)
415             {
416                 return GetSigOpCount(true);
417             }
418
419             // This is a pay-to-script-hash scriptPubKey;
420             // get the last item that the scriptSig
421             // pushes onto the stack:
422             InstructionQueue wScriptSig = scriptSig.GetInstructionQueue();
423             uint nScriptSigSize = scriptSig.Size;
424
425             instruction opcode; // Current instruction
426             byte[] pushArgs = new byte[0]; // OP_PUSHDATAn argument
427
428
429             while (wScriptSig.Index < nScriptSigSize)
430             {
431                 if (!ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs))
432                 {
433                     return 0;
434                 }
435
436                 if (opcode > instruction.OP_16)
437                 {
438                     return 0;
439                 }
440             }
441
442             /// ... and return its opcount:
443             var subScript = new CScript(pushArgs);
444
445             return subScript.GetSigOpCount(true);
446
447         }
448
449         /// <summary>
450         /// Set pay-to-pubkey destination.
451         /// </summary>
452         /// <param name="pubKey">Instance of CPubKey.</param>
453         public void SetDestination(CPubKey pubKey)
454         {
455             codeBytes.Clear();
456             PushData(pubKey);
457             AddInstruction(instruction.OP_CHECKSIG);
458         }
459
460         /// <summary>
461         /// Set pay-to-pubkeyhash destination
462         /// </summary>
463         /// <param name="ID">Public key hash</param>
464         public void SetDestination(CKeyID ID)
465         {
466             codeBytes.Clear();
467             AddInstruction(instruction.OP_DUP);
468             AddInstruction(instruction.OP_HASH160);
469             AddHash(ID);
470             AddInstruction(instruction.OP_EQUALVERIFY);
471             AddInstruction(instruction.OP_CHECKSIG);
472         }
473
474         /// <summary>
475         /// Set pay-to-scripthash destination
476         /// </summary>
477         /// <param name="ID">Script hash</param>
478         public void SetDestination(CScriptID ID)
479         {
480             codeBytes.Clear();
481             AddInstruction(instruction.OP_HASH160);
482             AddHash(ID);
483             AddInstruction(instruction.OP_EQUAL);
484         }
485
486         /// <summary>
487         /// Reset script code buffer.
488         /// </summary>
489         public void SetNullDestination()
490         {
491             codeBytes.Clear();
492         }
493
494         /// <summary>
495         /// Set multisig destination.
496         /// </summary>
497         /// <param name="nRequired">Amount of required signatures.</param>
498         /// <param name="keys">Set of public keys.</param>
499         public void SetMultiSig(int nRequired, CPubKey[] keys)
500         {
501             codeBytes.Clear();
502             AddInstruction(ScriptCode.EncodeOP_N(nRequired));
503
504             foreach (var key in keys)
505             {
506                 PushData(key);
507             }
508
509             AddInstruction(ScriptCode.EncodeOP_N(keys.Length));
510             AddInstruction(instruction.OP_CHECKMULTISIG);
511         }
512
513         /// <summary>
514         /// Access to script code.
515         /// </summary>
516         public static implicit operator byte[] (CScript script)
517         {
518             return script.codeBytes.ToArray();
519         }
520
521         /// <summary>
522         /// Implicit cast of byte array to CScript.
523         /// </summary>
524         /// <param name="scriptBytes"></param>
525         public static implicit operator CScript(byte[] scriptBytes)
526         {
527             return new CScript(scriptBytes);
528         }
529
530         /// <summary>
531         /// Script size
532         /// </summary>
533         public uint Size
534         {
535             get { return (uint) codeBytes.Count; }
536         }
537
538         public CScriptID ScriptID
539         {
540             get { return new CScriptID(CryptoUtils.ComputeHash160(codeBytes.ToArray())); }
541         }
542
543         /// <summary>
544         /// Disassemble current script code
545         /// </summary>
546         /// <returns>Code listing</returns>
547                 public override string ToString()
548                 {
549                         var sb = new StringBuilder();
550             var wCodeBytes = new InstructionQueue(ref codeBytes);
551
552             instruction opcode; // Current instruction
553             byte[] pushArgs; // OP_PUSHDATAn argument
554             while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
555             {
556                 if (sb.Length != 0)
557                 {
558                     sb.Append(" ");
559                 }
560
561                 if (0 <= opcode && opcode <= instruction.OP_PUSHDATA4)
562                 {
563                     sb.Append(ScriptCode.ValueString(pushArgs));
564                 }
565                 else
566                 {
567                     sb.Append(ScriptCode.GetOpName(opcode));
568                 }
569             }
570
571             return sb.ToString();
572                 }
573         }
574 }