ScriptSigArgsExpected, IsStandard
[NovacoinLibrary.git] / Novacoin / ScriptCode.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Novacoin
7 {
8     /// <summary>
9     /// Script opcodes
10     /// </summary>
11     public enum opcodetype
12     {
13         // push value
14         OP_0 = 0x00,
15         OP_FALSE = OP_0,
16         OP_PUSHDATA1 = 0x4c,
17         OP_PUSHDATA2 = 0x4d,
18         OP_PUSHDATA4 = 0x4e,
19         OP_1NEGATE = 0x4f,
20         OP_RESERVED = 0x50,
21         OP_1 = 0x51,
22         OP_TRUE = OP_1,
23         OP_2 = 0x52,
24         OP_3 = 0x53,
25         OP_4 = 0x54,
26         OP_5 = 0x55,
27         OP_6 = 0x56,
28         OP_7 = 0x57,
29         OP_8 = 0x58,
30         OP_9 = 0x59,
31         OP_10 = 0x5a,
32         OP_11 = 0x5b,
33         OP_12 = 0x5c,
34         OP_13 = 0x5d,
35         OP_14 = 0x5e,
36         OP_15 = 0x5f,
37         OP_16 = 0x60,
38
39         // control
40         OP_NOP = 0x61,
41         OP_VER = 0x62,
42         OP_IF = 0x63,
43         OP_NOTIF = 0x64,
44         OP_VERIF = 0x65,
45         OP_VERNOTIF = 0x66,
46         OP_ELSE = 0x67,
47         OP_ENDIF = 0x68,
48         OP_VERIFY = 0x69,
49         OP_RETURN = 0x6a,
50
51         // stack ops
52         OP_TOALTSTACK = 0x6b,
53         OP_FROMALTSTACK = 0x6c,
54         OP_2DROP = 0x6d,
55         OP_2DUP = 0x6e,
56         OP_3DUP = 0x6f,
57         OP_2OVER = 0x70,
58         OP_2ROT = 0x71,
59         OP_2SWAP = 0x72,
60         OP_IFDUP = 0x73,
61         OP_DEPTH = 0x74,
62         OP_DROP = 0x75,
63         OP_DUP = 0x76,
64         OP_NIP = 0x77,
65         OP_OVER = 0x78,
66         OP_PICK = 0x79,
67         OP_ROLL = 0x7a,
68         OP_ROT = 0x7b,
69         OP_SWAP = 0x7c,
70         OP_TUCK = 0x7d,
71
72         // splice ops
73         OP_CAT = 0x7e,
74         OP_SUBSTR = 0x7f,
75         OP_LEFT = 0x80,
76         OP_RIGHT = 0x81,
77         OP_SIZE = 0x82,
78
79         // bit logic
80         OP_INVERT = 0x83,
81         OP_AND = 0x84,
82         OP_OR = 0x85,
83         OP_XOR = 0x86,
84         OP_EQUAL = 0x87,
85         OP_EQUALVERIFY = 0x88,
86         OP_RESERVED1 = 0x89,
87         OP_RESERVED2 = 0x8a,
88
89         // numeric
90         OP_1ADD = 0x8b,
91         OP_1SUB = 0x8c,
92         OP_2MUL = 0x8d,
93         OP_2DIV = 0x8e,
94         OP_NEGATE = 0x8f,
95         OP_ABS = 0x90,
96         OP_NOT = 0x91,
97         OP_0NOTEQUAL = 0x92,
98
99         OP_ADD = 0x93,
100         OP_SUB = 0x94,
101         OP_MUL = 0x95,
102         OP_DIV = 0x96,
103         OP_MOD = 0x97,
104         OP_LSHIFT = 0x98,
105         OP_RSHIFT = 0x99,
106
107         OP_BOOLAND = 0x9a,
108         OP_BOOLOR = 0x9b,
109         OP_NUMEQUAL = 0x9c,
110         OP_NUMEQUALVERIFY = 0x9d,
111         OP_NUMNOTEQUAL = 0x9e,
112         OP_LESSTHAN = 0x9f,
113         OP_GREATERTHAN = 0xa0,
114         OP_LESSTHANOREQUAL = 0xa1,
115         OP_GREATERTHANOREQUAL = 0xa2,
116         OP_MIN = 0xa3,
117         OP_MAX = 0xa4,
118
119         OP_WITHIN = 0xa5,
120
121         // crypto
122         OP_RIPEMD160 = 0xa6,
123         OP_SHA1 = 0xa7,
124         OP_SHA256 = 0xa8,
125         OP_HASH160 = 0xa9,
126         OP_HASH256 = 0xaa,
127         OP_CODESEPARATOR = 0xab,
128         OP_CHECKSIG = 0xac,
129         OP_CHECKSIGVERIFY = 0xad,
130         OP_CHECKMULTISIG = 0xae,
131         OP_CHECKMULTISIGVERIFY = 0xaf,
132
133         // expansion
134         OP_NOP1 = 0xb0,
135         OP_NOP2 = 0xb1,
136         OP_NOP3 = 0xb2,
137         OP_NOP4 = 0xb3,
138         OP_NOP5 = 0xb4,
139         OP_NOP6 = 0xb5,
140         OP_NOP7 = 0xb6,
141         OP_NOP8 = 0xb7,
142         OP_NOP9 = 0xb8,
143         OP_NOP10 = 0xb9,
144
145         // template matching params
146         OP_SMALLDATA = 0xf9,
147         OP_SMALLINTEGER = 0xfa,
148         OP_PUBKEYS = 0xfb,
149         OP_PUBKEYHASH = 0xfd,
150         OP_PUBKEY = 0xfe,
151
152         OP_INVALIDOPCODE = 0xff,
153     };
154
155     /// <summary>
156     /// Transaction output types.
157     /// </summary>
158     public enum txnouttype
159     {
160         TX_NONSTANDARD,
161
162         // 'standard' transaction types:
163         TX_PUBKEY,
164         TX_PUBKEYHASH,
165         TX_SCRIPTHASH,
166         TX_MULTISIG,
167         TX_NULL_DATA,
168     };
169
170     public static class ScriptCode
171     {
172
173         /// <summary>
174         /// Get the name of supplied opcode
175         /// </summary>
176         /// <param name="opcode">Opcode</param>
177         /// <returns>Opcode name</returns>
178         public static string GetOpName(opcodetype opcode)
179         {
180             switch (opcode)
181             {
182                 // push value
183                 case opcodetype.OP_0:
184                     return "0";
185                 case opcodetype.OP_PUSHDATA1:
186                     return "OP_PUSHDATA1";
187                 case opcodetype.OP_PUSHDATA2:
188                     return "OP_PUSHDATA2";
189                 case opcodetype.OP_PUSHDATA4:
190                     return "OP_PUSHDATA4";
191                 case opcodetype.OP_1NEGATE:
192                     return "-1";
193                 case opcodetype.OP_RESERVED:
194                     return "OP_RESERVED";
195                 case opcodetype.OP_1:
196                     return "1";
197                 case opcodetype.OP_2:
198                     return "2";
199                 case opcodetype.OP_3:
200                     return "3";
201                 case opcodetype.OP_4:
202                     return "4";
203                 case opcodetype.OP_5:
204                     return "5";
205                 case opcodetype.OP_6:
206                     return "6";
207                 case opcodetype.OP_7:
208                     return "7";
209                 case opcodetype.OP_8:
210                     return "8";
211                 case opcodetype.OP_9:
212                     return "9";
213                 case opcodetype.OP_10:
214                     return "10";
215                 case opcodetype.OP_11:
216                     return "11";
217                 case opcodetype.OP_12:
218                     return "12";
219                 case opcodetype.OP_13:
220                     return "13";
221                 case opcodetype.OP_14:
222                     return "14";
223                 case opcodetype.OP_15:
224                     return "15";
225                 case opcodetype.OP_16:
226                     return "16";
227
228                 // control
229                 case opcodetype.OP_NOP:
230                     return "OP_NOP";
231                 case opcodetype.OP_VER:
232                     return "OP_VER";
233                 case opcodetype.OP_IF:
234                     return "OP_IF";
235                 case opcodetype.OP_NOTIF:
236                     return "OP_NOTIF";
237                 case opcodetype.OP_VERIF:
238                     return "OP_VERIF";
239                 case opcodetype.OP_VERNOTIF:
240                     return "OP_VERNOTIF";
241                 case opcodetype.OP_ELSE:
242                     return "OP_ELSE";
243                 case opcodetype.OP_ENDIF:
244                     return "OP_ENDIF";
245                 case opcodetype.OP_VERIFY:
246                     return "OP_VERIFY";
247                 case opcodetype.OP_RETURN:
248                     return "OP_RETURN";
249
250                 // stack ops
251                 case opcodetype.OP_TOALTSTACK:
252                     return "OP_TOALTSTACK";
253                 case opcodetype.OP_FROMALTSTACK:
254                     return "OP_FROMALTSTACK";
255                 case opcodetype.OP_2DROP:
256                     return "OP_2DROP";
257                 case opcodetype.OP_2DUP:
258                     return "OP_2DUP";
259                 case opcodetype.OP_3DUP:
260                     return "OP_3DUP";
261                 case opcodetype.OP_2OVER:
262                     return "OP_2OVER";
263                 case opcodetype.OP_2ROT:
264                     return "OP_2ROT";
265                 case opcodetype.OP_2SWAP:
266                     return "OP_2SWAP";
267                 case opcodetype.OP_IFDUP:
268                     return "OP_IFDUP";
269                 case opcodetype.OP_DEPTH:
270                     return "OP_DEPTH";
271                 case opcodetype.OP_DROP:
272                     return "OP_DROP";
273                 case opcodetype.OP_DUP:
274                     return "OP_DUP";
275                 case opcodetype.OP_NIP:
276                     return "OP_NIP";
277                 case opcodetype.OP_OVER:
278                     return "OP_OVER";
279                 case opcodetype.OP_PICK:
280                     return "OP_PICK";
281                 case opcodetype.OP_ROLL:
282                     return "OP_ROLL";
283                 case opcodetype.OP_ROT:
284                     return "OP_ROT";
285                 case opcodetype.OP_SWAP:
286                     return "OP_SWAP";
287                 case opcodetype.OP_TUCK:
288                     return "OP_TUCK";
289
290                 // splice ops
291                 case opcodetype.OP_CAT:
292                     return "OP_CAT";
293                 case opcodetype.OP_SUBSTR:
294                     return "OP_SUBSTR";
295                 case opcodetype.OP_LEFT:
296                     return "OP_LEFT";
297                 case opcodetype.OP_RIGHT:
298                     return "OP_RIGHT";
299                 case opcodetype.OP_SIZE:
300                     return "OP_SIZE";
301
302                 // bit logic
303                 case opcodetype.OP_INVERT:
304                     return "OP_INVERT";
305                 case opcodetype.OP_AND:
306                     return "OP_AND";
307                 case opcodetype.OP_OR:
308                     return "OP_OR";
309                 case opcodetype.OP_XOR:
310                     return "OP_XOR";
311                 case opcodetype.OP_EQUAL:
312                     return "OP_EQUAL";
313                 case opcodetype.OP_EQUALVERIFY:
314                     return "OP_EQUALVERIFY";
315                 case opcodetype.OP_RESERVED1:
316                     return "OP_RESERVED1";
317                 case opcodetype.OP_RESERVED2:
318                     return "OP_RESERVED2";
319
320                 // numeric
321                 case opcodetype.OP_1ADD:
322                     return "OP_1ADD";
323                 case opcodetype.OP_1SUB:
324                     return "OP_1SUB";
325                 case opcodetype.OP_2MUL:
326                     return "OP_2MUL";
327                 case opcodetype.OP_2DIV:
328                     return "OP_2DIV";
329                 case opcodetype.OP_NEGATE:
330                     return "OP_NEGATE";
331                 case opcodetype.OP_ABS:
332                     return "OP_ABS";
333                 case opcodetype.OP_NOT:
334                     return "OP_NOT";
335                 case opcodetype.OP_0NOTEQUAL:
336                     return "OP_0NOTEQUAL";
337                 case opcodetype.OP_ADD:
338                     return "OP_ADD";
339                 case opcodetype.OP_SUB:
340                     return "OP_SUB";
341                 case opcodetype.OP_MUL:
342                     return "OP_MUL";
343                 case opcodetype.OP_DIV:
344                     return "OP_DIV";
345                 case opcodetype.OP_MOD:
346                     return "OP_MOD";
347                 case opcodetype.OP_LSHIFT:
348                     return "OP_LSHIFT";
349                 case opcodetype.OP_RSHIFT:
350                     return "OP_RSHIFT";
351                 case opcodetype.OP_BOOLAND:
352                     return "OP_BOOLAND";
353                 case opcodetype.OP_BOOLOR:
354                     return "OP_BOOLOR";
355                 case opcodetype.OP_NUMEQUAL:
356                     return "OP_NUMEQUAL";
357                 case opcodetype.OP_NUMEQUALVERIFY:
358                     return "OP_NUMEQUALVERIFY";
359                 case opcodetype.OP_NUMNOTEQUAL:
360                     return "OP_NUMNOTEQUAL";
361                 case opcodetype.OP_LESSTHAN:
362                     return "OP_LESSTHAN";
363                 case opcodetype.OP_GREATERTHAN:
364                     return "OP_GREATERTHAN";
365                 case opcodetype.OP_LESSTHANOREQUAL:
366                     return "OP_LESSTHANOREQUAL";
367                 case opcodetype.OP_GREATERTHANOREQUAL:
368                     return "OP_GREATERTHANOREQUAL";
369                 case opcodetype.OP_MIN:
370                     return "OP_MIN";
371                 case opcodetype.OP_MAX:
372                     return "OP_MAX";
373                 case opcodetype.OP_WITHIN:
374                     return "OP_WITHIN";
375
376                 // crypto
377                 case opcodetype.OP_RIPEMD160:
378                     return "OP_RIPEMD160";
379                 case opcodetype.OP_SHA1:
380                     return "OP_SHA1";
381                 case opcodetype.OP_SHA256:
382                     return "OP_SHA256";
383                 case opcodetype.OP_HASH160:
384                     return "OP_HASH160";
385                 case opcodetype.OP_HASH256:
386                     return "OP_HASH256";
387                 case opcodetype.OP_CODESEPARATOR:
388                     return "OP_CODESEPARATOR";
389                 case opcodetype.OP_CHECKSIG:
390                     return "OP_CHECKSIG";
391                 case opcodetype.OP_CHECKSIGVERIFY:
392                     return "OP_CHECKSIGVERIFY";
393                 case opcodetype.OP_CHECKMULTISIG:
394                     return "OP_CHECKMULTISIG";
395                 case opcodetype.OP_CHECKMULTISIGVERIFY:
396                     return "OP_CHECKMULTISIGVERIFY";
397
398                 // expansion
399                 case opcodetype.OP_NOP1:
400                     return "OP_NOP1";
401                 case opcodetype.OP_NOP2:
402                     return "OP_NOP2";
403                 case opcodetype.OP_NOP3:
404                     return "OP_NOP3";
405                 case opcodetype.OP_NOP4:
406                     return "OP_NOP4";
407                 case opcodetype.OP_NOP5:
408                     return "OP_NOP5";
409                 case opcodetype.OP_NOP6:
410                     return "OP_NOP6";
411                 case opcodetype.OP_NOP7:
412                     return "OP_NOP7";
413                 case opcodetype.OP_NOP8:
414                     return "OP_NOP8";
415                 case opcodetype.OP_NOP9:
416                     return "OP_NOP9";
417                 case opcodetype.OP_NOP10:
418                     return "OP_NOP10";
419
420                 // template matching params
421                 case opcodetype.OP_PUBKEYHASH:
422                     return "OP_PUBKEYHASH";
423                 case opcodetype.OP_PUBKEY:
424                     return "OP_PUBKEY";
425                 case opcodetype.OP_SMALLDATA:
426                     return "OP_SMALLDATA";
427
428                 case opcodetype.OP_INVALIDOPCODE:
429                     return "OP_INVALIDOPCODE";
430                 default:
431                     return "OP_UNKNOWN";
432             }
433         }
434
435         /// <summary>
436         /// Get next opcode from passed list of bytes and extract push arguments if there are some.
437         /// </summary>
438         /// <param name="codeBytes">WrappedList reference.</param>
439         /// <param name="opcodeRet">Found opcode.</param>
440         /// <param name="bytesRet">IEnumerable out param which is used to get the push arguments.</param>
441         /// <returns>Result of operation</returns>
442         public static bool GetOp(ref WrappedList<byte> codeBytes, out opcodetype opcodeRet, out IEnumerable<byte> bytesRet)
443         {
444             bytesRet = new List<byte>();
445             opcodeRet = opcodetype.OP_INVALIDOPCODE;
446
447             opcodetype opcode;
448
449             try
450             {
451                 // Read instruction
452                 opcode = (opcodetype)codeBytes.GetItem();
453             }
454             catch (WrappedListException)
455             {
456                 // No instruction found there
457                 return false;
458             }
459
460             // Immediate operand
461             if (opcode <= opcodetype.OP_PUSHDATA4)
462             {
463                 byte[] szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length
464
465                 try
466                 {
467                     if (opcode < opcodetype.OP_PUSHDATA1)
468                     {
469                         // Zero value opcodes (OP_0, OP_FALSE)
470                         szBytes[3] = (byte)opcode;
471                     }
472                     else if (opcode == opcodetype.OP_PUSHDATA1)
473                     {
474                         // The next byte contains the number of bytes to be pushed onto the stack, 
475                         //    i.e. you have something like OP_PUSHDATA1 0x01 [0x5a]
476                         szBytes[3] = (byte)codeBytes.GetItem();
477                     }
478                     else if (opcode == opcodetype.OP_PUSHDATA2)
479                     {
480                         // The next two bytes contain the number of bytes to be pushed onto the stack,
481                         //    i.e. now your operation will seem like this: OP_PUSHDATA2 0x00 0x01 [0x5a]
482                         codeBytes.GetItems(2).CopyTo(szBytes, 2);
483                     }
484                     else if (opcode == opcodetype.OP_PUSHDATA4)
485                     {
486                         // The next four bytes contain the number of bytes to be pushed onto the stack,
487                         //   OP_PUSHDATA4 0x00 0x00 0x00 0x01 [0x5a]
488                         szBytes = codeBytes.GetItems(4);
489                     }
490                 }
491                 catch (WrappedListException)
492                 {
493                     // Unable to read operand length
494                     return false;
495                 }
496
497                 int nSize = (int)Interop.BEBytesToUInt32(szBytes);
498
499                 if (nSize > 0)
500                 {
501                     // If nSize is greater than zero then there is some data available
502                     try
503                     {
504                         // Read found number of bytes into list of OP_PUSHDATAn arguments.
505                         bytesRet = codeBytes.GetEnumerableItems(nSize);
506                     }
507                     catch (WrappedListException)
508                     {
509                         // Unable to read data
510                         return false;
511                     }
512                 }
513             }
514
515             opcodeRet = opcode;
516
517             return true;
518         }
519
520         /// <summary>
521         /// Convert value bytes into readable representation.
522         /// 
523         /// If list lengh is equal or lesser than 4 bytes then bytes are interpreted as integer value. Otherwise you will get hex representation of supplied data.
524         /// </summary>
525         /// <param name="bytes">Collection of value bytes.</param>
526         /// <returns>Formatted value.</returns>
527         public static string ValueString(IEnumerable<byte> bytes)
528         {
529             StringBuilder sb = new StringBuilder();
530
531             if (bytes.Count() <= 4)
532             {
533                 byte[] valueBytes = new byte[4] { 0, 0, 0, 0 };
534                 bytes.ToArray().CopyTo(valueBytes, valueBytes.Length - bytes.Count());
535
536                 sb.Append(Interop.BEBytesToUInt32(valueBytes));
537             }
538             else
539             {
540                 return Interop.ToHex(bytes);
541             }
542
543             return sb.ToString();
544         }
545
546         /// <summary>
547         /// Convert list of stack items into human readable representation.
548         /// </summary>
549         /// <param name="stackList">List of stack items.</param>
550         /// <returns>Formatted value.</returns>
551         public static string StackString(IList<IList<byte>> stackList)
552         {
553             StringBuilder sb = new StringBuilder();
554             foreach (IList<byte> bytesList in stackList)
555             {
556                 sb.Append(ValueString(bytesList));
557             }
558
559             return sb.ToString();
560         }
561
562         /// <summary>
563         /// Decode small integer
564         /// </summary>
565         /// <param name="opcode">Small integer opcode (OP_0 - OP_16)</param>
566         /// <returns>Small integer</returns>
567         public static int DecodeOP_N(opcodetype opcode)
568         {
569             if (opcode == opcodetype.OP_0)
570                 return 0;
571
572             // Only OP_n opcodes are supported, throw exception otherwise.
573             if (opcode < opcodetype.OP_1 || opcode > opcodetype.OP_16)
574                 throw new Exception("Invalid small integer opcode.");
575             return (int)opcode - (int)(opcodetype.OP_1 - 1);
576         }
577
578         /// <summary>
579         /// Converts small integer into opcode
580         /// </summary>
581         /// <param name="n">Small integer from the range of 0 up to 16.</param>
582         /// <returns>Corresponding opcode.</returns>
583         public static opcodetype EncodeOP_N(int n)
584         {
585             // The n value must be in the range of 0 to 16.
586             if (n < 0 || n > 16)
587                 throw new Exception("Invalid small integer value.");
588             if (n == 0)
589                 return opcodetype.OP_0;
590             return (opcodetype)(opcodetype.OP_1 + n - 1);
591         }
592
593         public static int ScriptSigArgsExpected(txnouttype t, IList<IEnumerable<byte>> solutions)
594         {
595             switch (t)
596             {
597                 case txnouttype.TX_NONSTANDARD:
598                     return -1;
599                 case txnouttype.TX_NULL_DATA:
600                     return 1;
601                 case txnouttype.TX_PUBKEY:
602                     return 1;
603                 case txnouttype.TX_PUBKEYHASH:
604                     return 2;
605                 case txnouttype.TX_MULTISIG:
606                     if (solutions.Count() < 1 || solutions.First().Count() < 1)
607                         return -1;
608                     return solutions.First().First() + 1;
609                 case txnouttype.TX_SCRIPTHASH:
610                     return 1; // doesn't include args needed by the script
611             }
612             return -1;
613         }
614
615
616         public static bool IsStandard(CScript scriptPubKey, out txnouttype whichType)
617         {
618             IList<IEnumerable<byte>> solutions = new List<IEnumerable<byte>>();
619
620             if (!Solver(scriptPubKey, out whichType, out solutions))
621             {
622                 // No solutions found
623                 return false;
624             }
625
626             if (whichType == txnouttype.TX_MULTISIG)
627             {
628                 byte m = solutions.First().First();
629                 byte n = solutions.Last().First();
630
631                 // Support up to x-of-3 multisig txns as standard
632                 if (n < 1 || n > 3)
633                 {
634                     return false;
635                 }
636                 if (m < 1 || m > n)
637                 {
638                     return false;
639                 }
640             }
641
642             return whichType != txnouttype.TX_NONSTANDARD;
643         }
644
645         /// <summary>
646         /// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
647         /// </summary>
648         /// <param name="scriptPubKey">CScript instance</param>
649         /// <param name="typeRet">Output type</param>
650         /// <param name="solutions">Set of solutions</param>
651         /// <returns>Result</returns>
652         public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList<IEnumerable<byte>> solutions)
653         {
654             solutions = new List<IEnumerable<byte>>();
655
656             // There are shortcuts for pay-to-script-hash and pay-to-pubkey-hash, which are more constrained than the other types:
657
658             // It is always OP_HASH160 20 [20 byte hash] OP_EQUAL
659             if (scriptPubKey.IsPayToScriptHash())
660             {
661                 typeRet = txnouttype.TX_SCRIPTHASH;
662
663                 // Take 20 bytes with offset of 2 bytes
664                 IEnumerable<byte> hashBytes = scriptPubKey.Enumerable.Skip(2).Take(20);
665                 solutions.Add(hashBytes);
666
667                 return true;
668             }
669
670             // It is always OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
671             if (scriptPubKey.IsPayToPubKeyHash())
672             {
673                 typeRet = txnouttype.TX_PUBKEYHASH;
674
675                 // Take 20 bytes with offset of 3 bytes
676                 IEnumerable<byte> hashBytes = scriptPubKey.Enumerable.Skip(3).Take(20);
677                 solutions.Add(hashBytes);
678
679                 return true;
680             }
681
682             List<Tuple<txnouttype, IEnumerable<byte>>> templateTuples = new List<Tuple<txnouttype, IEnumerable<byte>>>();
683
684             // Sender provides pubkey, receiver adds signature
685             // [ECDSA public key] OP_CHECKSIG
686             templateTuples.Add(
687                 new Tuple<txnouttype, IEnumerable<byte>>(
688                     txnouttype.TX_PUBKEY,
689                     new byte[] { (byte)opcodetype.OP_PUBKEY, (byte)opcodetype.OP_CHECKSIG })
690             );
691
692             // Sender provides N pubkeys, receivers provides M signatures
693             // N [pubkey1] [pubkey2] ... [pubkeyN] M OP_CHECKMULTISIG
694             // Where N and M are small integer opcodes (OP1 ... OP_16)
695             templateTuples.Add(
696                 new Tuple<txnouttype, IEnumerable<byte>>(
697                     txnouttype.TX_MULTISIG,
698                     new byte[] { (byte)opcodetype.OP_SMALLINTEGER, (byte)opcodetype.OP_PUBKEYS, (byte)opcodetype.OP_SMALLINTEGER, (byte)opcodetype.OP_CHECKMULTISIG })
699             );
700
701             // Data-carrying output
702             // OP_RETURN [up to 80 bytes of data]
703             templateTuples.Add(
704                 new Tuple<txnouttype, IEnumerable<byte>>(
705                     txnouttype.TX_NULL_DATA,
706                     new byte[] { (byte)opcodetype.OP_RETURN, (byte)opcodetype.OP_SMALLDATA })
707             );
708
709             // Nonstandard tx output
710             typeRet = txnouttype.TX_NONSTANDARD;
711
712             foreach (Tuple<txnouttype, IEnumerable<byte>> templateTuple in templateTuples)
713             {
714                 CScript script2 = new CScript(templateTuple.Item2);
715                 CScript script1 = scriptPubKey;
716
717                 opcodetype opcode1, opcode2;
718
719                 // Compare
720                 WrappedList<byte> wl1 = script1.GetWrappedList();
721                 WrappedList<byte> wl2 = script2.GetWrappedList();
722
723                 IEnumerable<byte> args1, args2;
724
725                 byte last1 = script1.Enumerable.Last();
726                 byte last2 = script2.Enumerable.Last();
727
728                 while (true)
729                 {
730                     if (wl1.GetItem() == last1 && wl2.GetItem() == last2)
731                     {
732                         // Found a match
733                         typeRet = templateTuple.Item1;
734                         if (typeRet == txnouttype.TX_MULTISIG)
735                         {
736                             // Additional checks for TX_MULTISIG:
737                             byte m = solutions.First().First();
738                             byte n = solutions.Last().First();
739
740                             if (m < 1 || n < 1 || m > n || solutions.Count - 2 != n)
741                             {
742                                 return false;
743                             }
744                         }
745                         return true;
746                     }
747
748                     if (!GetOp(ref wl1, out opcode1, out args1))
749                     {
750                         break;
751                     }
752                     if (!GetOp(ref wl2, out opcode2, out args2))
753                     {
754                         break;
755                     }
756
757                     // Template matching opcodes:
758                     if (opcode2 == opcodetype.OP_PUBKEYS)
759                     {
760                         while (args1.Count() >= 33 && args1.Count() <= 120)
761                         {
762                             solutions.Add(args1);
763                             if (!GetOp(ref wl1, out opcode1, out args1))
764                             {
765                                 break;
766                             }
767                         }
768                         if (!GetOp(ref wl2, out opcode2, out args2))
769                             break;
770                         // Normal situation is to fall through
771                         // to other if/else statements
772                     }
773                     if (opcode2 == opcodetype.OP_PUBKEY)
774                     {
775                         if (args1.Count() < 33 || args1.Count() > 120)
776                         {
777                             break;
778                         }
779                         solutions.Add(args1);
780                     }
781                     else if (opcode2 == opcodetype.OP_PUBKEYHASH)
782                     {
783                         if (args1.Count() != 20) // hash160 size
784                         {
785                             break;
786                         }
787                         solutions.Add(args1);
788                     }
789                     else if (opcode2 == opcodetype.OP_SMALLINTEGER)
790                     {
791                         // Single-byte small integer pushed onto solutions
792                         if (opcode1 == opcodetype.OP_0 || (opcode1 >= opcodetype.OP_1 && opcode1 <= opcodetype.OP_16))
793                         {
794                             byte n = (byte)DecodeOP_N(opcode1);
795                             solutions.Add(new byte[] { n });
796                         }
797                         else
798                         {
799                             break;
800                         }
801                     }
802                     else if (opcode2 == opcodetype.OP_SMALLDATA)
803                     {
804                         // small pushdata, <= 80 bytes
805                         if (args1.Count() > 80)
806                         {
807                             break;
808                         }
809                     }
810                     else if (opcode1 != opcode2 || args1.SequenceEqual(args2))
811                     {
812                         // Others must match exactly
813                         break;
814                     }
815                 }
816             }
817
818             solutions.Clear();
819             typeRet = txnouttype.TX_NONSTANDARD;
820
821             return false;
822         }
823     };
824 }