9e65e380eb2c21e2040aacf32449d9f1c576c695
[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(IEnumerable<byte> dataBytes)
119         {
120             long nCount = dataBytes.LongCount();
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(IList<byte> pattern)
160         {
161             for (int i = 0; i < codeBytes.Count; i++)
162             {
163                 if (codeBytes.Skip(i).Take(pattern.Count).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(IList<byte> pattern)
176         {
177             List<byte> resultBytes = new List<byte>(codeBytes);
178             int count = 0;
179             int patternLen = pattern.Count;
180                         
181             foreach (int i in FindPattern(pattern))
182             {
183                 resultBytes.RemoveRange(i - count * patternLen, patternLen);
184                 count++;
185             }
186
187             codeBytes = resultBytes;
188             
189             return count;
190         }
191
192         /// <summary>
193         /// Is it true that script doesn't contain anything except push value operations?
194         /// </summary>
195         public bool IsPushOnly
196         {
197             get
198             {
199                 ByteQueue wCodeBytes = new ByteQueue(codeBytes);
200
201                 instruction opcode; // Current opcode
202                 IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
203
204                 // Scan opcodes sequence
205                 while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
206                 {
207                     if (opcode > instruction.OP_16)
208                     {
209                         // We don't allow control opcodes here
210                         return false;
211                     }
212                 }
213
214                 return true;
215             }
216         }
217
218         /// <summary>
219         /// Is it true that script doesn't contain non-canonical push operations?
220         /// </summary>
221         public bool HasOnlyCanonicalPushes
222         {
223             get
224             {
225                 ByteQueue wCodeBytes = new ByteQueue(codeBytes);
226
227                 instruction opcode; // Current opcode
228                 IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
229
230                 // Scan opcodes sequence
231                 while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
232                 {
233                     byte[] data = pushArgs.ToArray();
234
235                     if (opcode < instruction.OP_PUSHDATA1 && opcode > instruction.OP_0 && (data.Length == 1 && data[0] <= 16))
236                     {
237                         // Could have used an OP_n code, rather than a 1-byte push.
238                         return false;
239                     }
240                     if (opcode == instruction.OP_PUSHDATA1 && data.Length < (int)instruction.OP_PUSHDATA1)
241                     {
242                         // Could have used a normal n-byte push, rather than OP_PUSHDATA1.
243                         return false;
244                     }
245                     if (opcode == instruction.OP_PUSHDATA2 && data.Length <= 0xFF)
246                     {
247                         // Could have used an OP_PUSHDATA1.
248                         return false;
249                     }
250                     if (opcode == instruction.OP_PUSHDATA4 && data.LongLength <= 0xFFFF)
251                     {
252                         // Could have used an OP_PUSHDATA2.
253                         return false;
254                     }
255                 }
256
257                 return true;
258             }
259         }
260
261         /// <summary>
262         /// Quick test for pay-to-script-hash CScripts
263         /// </summary>
264         public bool IsPayToScriptHash
265         {
266             get
267             {
268                 // Sender provides redeem script hash, receiver provides signature list and redeem script
269                 // OP_HASH160 20 [20 byte hash] OP_EQUAL
270                 return (codeBytes.Count() == 23 &&
271                         codeBytes[0] == (byte)instruction.OP_HASH160 &&
272                         codeBytes[1] == 0x14 && // 20 bytes hash length prefix
273                         codeBytes[22] == (byte)instruction.OP_EQUAL);
274             }
275         }
276
277         /// <summary>
278         /// Quick test for pay-to-pubkeyhash CScripts
279         /// </summary>
280         public bool IsPayToPubKeyHash
281         {
282             get
283             {
284                 // Sender provides hash of pubkey, receiver provides signature and pubkey
285                 // OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
286                 return (codeBytes.Count == 25 &&
287                         codeBytes[0] == (byte)instruction.OP_DUP &&
288                         codeBytes[1] == (byte)instruction.OP_HASH160 &&
289                         codeBytes[2] == 0x14 && // 20 bytes hash length prefix
290                         codeBytes[23] == (byte)instruction.OP_EQUALVERIFY &&
291                         codeBytes[24] == (byte)instruction.OP_CHECKSIG);
292             }
293         }
294
295         /// <summary>
296         /// Quick test for Null destination
297         /// </summary>
298         public bool IsNull
299         {
300             get { return codeBytes.Count == 0; }
301         }
302
303         /// <summary>
304         /// Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
305         /// as 20 sigops. With pay-to-script-hash, that changed:
306         /// CHECKMULTISIGs serialized in scriptSigs are
307         /// counted more accurately, assuming they are of the form
308         ///  ... OP_N CHECKMULTISIG ...
309         /// </summary>
310         /// <param name="fAccurate">Legacy mode flag</param>
311         /// <returns>Amount of sigops</returns>
312         public int GetSigOpCount(bool fAccurate)
313         {
314             ByteQueue wCodeBytes = new ByteQueue(codeBytes);
315
316             instruction opcode; // Current opcode
317             IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
318
319             int nCount = 0;
320             instruction lastOpcode = instruction.OP_INVALIDOPCODE;
321
322             // Scan opcodes sequence
323             while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
324             {
325                 if (opcode == instruction.OP_CHECKSIG || opcode == instruction.OP_CHECKSIGVERIFY)
326                 {
327                     nCount++;
328                 }
329                 else if (opcode == instruction.OP_CHECKMULTISIG || opcode == instruction.OP_CHECKMULTISIGVERIFY)
330                 {
331                     if (fAccurate && lastOpcode >= instruction.OP_1 && lastOpcode <= instruction.OP_16)
332                     {
333                         nCount += ScriptCode.DecodeOP_N(lastOpcode);
334                     }
335                     else
336                     {
337                         nCount += 20;
338                     }
339                 }
340             }
341
342             return nCount;
343         }
344
345         /// <summary>
346         /// Accurately count sigOps, including sigOps in
347         /// pay-to-script-hash transactions
348         /// </summary>
349         /// <param name="scriptSig">pay-to-script-hash scriptPubKey</param>
350         /// <returns>SigOps count</returns>
351         public int GetSigOpCount(CScript scriptSig)
352         {
353             if (!IsPayToScriptHash)
354             {
355                 return GetSigOpCount(true);
356             }
357
358             // This is a pay-to-script-hash scriptPubKey;
359             // get the last item that the scriptSig
360             // pushes onto the stack:
361             ByteQueue wScriptSig = scriptSig.GetByteQUeue();
362
363             instruction opcode; // Current opcode
364             IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
365
366             while (ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs))
367             {
368                 if (opcode > instruction.OP_16)
369                 {
370                     return 0;
371                 }
372             }
373
374             /// ... and return its opcount:
375             CScript subScript = new CScript(pushArgs);
376
377             return subScript.GetSigOpCount(true);
378
379         }
380
381         /// <summary>
382         /// Set pay-to-pubkey destination.
383         /// </summary>
384         /// <param name="pubKey">Instance of CPubKey.</param>
385         public void SetDestination(CPubKey pubKey)
386         {
387             codeBytes.Clear();
388             PushData(pubKey.PublicBytes);
389             AddOp(instruction.OP_CHECKSIG);
390         }
391
392         /// <summary>
393         /// Set pay-to-pubkeyhash destination
394         /// </summary>
395         /// <param name="ID">Public key hash</param>
396         public void SetDestination(CKeyID ID)
397         {
398             codeBytes.Clear();
399             AddOp(instruction.OP_DUP);
400             AddOp(instruction.OP_HASH160);
401             AddHash(ID);
402             AddOp(instruction.OP_EQUALVERIFY);
403             AddOp(instruction.OP_CHECKSIG);
404         }
405
406         /// <summary>
407         /// Set pay-to-scripthash destination
408         /// </summary>
409         /// <param name="ID">Script hash</param>
410         public void SetDestination(CScriptID ID)
411         {
412             codeBytes.Clear();
413             AddOp(instruction.OP_HASH160);
414             AddHash(ID);
415             AddOp(instruction.OP_EQUAL);
416         }
417
418         /// <summary>
419         /// Reset script code buffer.
420         /// </summary>
421         public void SetNullDestination()
422         {
423             codeBytes.Clear();
424         }
425
426         /// <summary>
427         /// Set multisig destination.
428         /// </summary>
429         /// <param name="nRequired">Amount of required signatures.</param>
430         /// <param name="keys">Set of public keys.</param>
431         public void SetMultiSig(int nRequired, IEnumerable<CPubKey> keys)
432         {
433             codeBytes.Clear();
434             AddOp(ScriptCode.EncodeOP_N(nRequired));
435
436             foreach (CPubKey key in keys)
437             {
438                 PushData(key.PublicBytes.ToList());
439             }
440
441             AddOp(ScriptCode.EncodeOP_N(keys.Count()));
442             AddOp(instruction.OP_CHECKMULTISIG);
443         }
444
445         /// <summary>
446         /// Access to script code.
447         /// </summary>
448         public IEnumerable<byte> Bytes
449         {
450             get { return codeBytes; }
451         }
452
453         public CScriptID ScriptID
454         {
455             get { return new CScriptID(Hash160.Compute160(codeBytes)); }
456         }
457
458         /// <summary>
459         /// Disassemble current script code
460         /// </summary>
461         /// <returns>Code listing</returns>
462                 public override string ToString()
463                 {
464                         StringBuilder sb = new StringBuilder();
465             ByteQueue wCodeBytes = new ByteQueue(codeBytes);
466
467             instruction opcode; // Current opcode
468             IEnumerable<byte> pushArgs; // OP_PUSHDATAn argument
469             while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs))
470             {
471                 if (sb.Length != 0)
472                 {
473                     sb.Append(" ");
474                 }
475
476                 if (0 <= opcode && opcode <= instruction.OP_PUSHDATA4)
477                 {
478                     sb.Append(ScriptCode.ValueString(pushArgs));
479                 }
480                 else
481                 {
482                     sb.Append(ScriptCode.GetOpName(opcode));
483                 }
484             }
485
486             return sb.ToString();
487                 }
488         }
489 }
490