2 using System.Collections.Generic;
11 public enum opcodetype
53 OP_FROMALTSTACK = 0x6c,
85 OP_EQUALVERIFY = 0x88,
110 OP_NUMEQUALVERIFY = 0x9d,
111 OP_NUMNOTEQUAL = 0x9e,
113 OP_GREATERTHAN = 0xa0,
114 OP_LESSTHANOREQUAL = 0xa1,
115 OP_GREATERTHANOREQUAL = 0xa2,
127 OP_CODESEPARATOR = 0xab,
129 OP_CHECKSIGVERIFY = 0xad,
130 OP_CHECKMULTISIG = 0xae,
131 OP_CHECKMULTISIGVERIFY = 0xaf,
145 // template matching params
147 OP_SMALLINTEGER = 0xfa,
149 OP_PUBKEYHASH = 0xfd,
152 OP_INVALIDOPCODE = 0xff,
156 /// Transaction output types.
158 public enum txnouttype
162 // 'standard' transaction types:
170 public static class ScriptCode
174 /// Get the name of supplied opcode
176 /// <param name="opcode">Opcode</param>
177 /// <returns>Opcode name</returns>
178 public static string GetOpName(opcodetype opcode)
183 case opcodetype.OP_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:
193 case opcodetype.OP_RESERVED:
194 return "OP_RESERVED";
195 case opcodetype.OP_1:
197 case opcodetype.OP_2:
199 case opcodetype.OP_3:
201 case opcodetype.OP_4:
203 case opcodetype.OP_5:
205 case opcodetype.OP_6:
207 case opcodetype.OP_7:
209 case opcodetype.OP_8:
211 case opcodetype.OP_9:
213 case opcodetype.OP_10:
215 case opcodetype.OP_11:
217 case opcodetype.OP_12:
219 case opcodetype.OP_13:
221 case opcodetype.OP_14:
223 case opcodetype.OP_15:
225 case opcodetype.OP_16:
229 case opcodetype.OP_NOP:
231 case opcodetype.OP_VER:
233 case opcodetype.OP_IF:
235 case opcodetype.OP_NOTIF:
237 case opcodetype.OP_VERIF:
239 case opcodetype.OP_VERNOTIF:
240 return "OP_VERNOTIF";
241 case opcodetype.OP_ELSE:
243 case opcodetype.OP_ENDIF:
245 case opcodetype.OP_VERIFY:
247 case opcodetype.OP_RETURN:
251 case opcodetype.OP_TOALTSTACK:
252 return "OP_TOALTSTACK";
253 case opcodetype.OP_FROMALTSTACK:
254 return "OP_FROMALTSTACK";
255 case opcodetype.OP_2DROP:
257 case opcodetype.OP_2DUP:
259 case opcodetype.OP_3DUP:
261 case opcodetype.OP_2OVER:
263 case opcodetype.OP_2ROT:
265 case opcodetype.OP_2SWAP:
267 case opcodetype.OP_IFDUP:
269 case opcodetype.OP_DEPTH:
271 case opcodetype.OP_DROP:
273 case opcodetype.OP_DUP:
275 case opcodetype.OP_NIP:
277 case opcodetype.OP_OVER:
279 case opcodetype.OP_PICK:
281 case opcodetype.OP_ROLL:
283 case opcodetype.OP_ROT:
285 case opcodetype.OP_SWAP:
287 case opcodetype.OP_TUCK:
291 case opcodetype.OP_CAT:
293 case opcodetype.OP_SUBSTR:
295 case opcodetype.OP_LEFT:
297 case opcodetype.OP_RIGHT:
299 case opcodetype.OP_SIZE:
303 case opcodetype.OP_INVERT:
305 case opcodetype.OP_AND:
307 case opcodetype.OP_OR:
309 case opcodetype.OP_XOR:
311 case opcodetype.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";
321 case opcodetype.OP_1ADD:
323 case opcodetype.OP_1SUB:
325 case opcodetype.OP_2MUL:
327 case opcodetype.OP_2DIV:
329 case opcodetype.OP_NEGATE:
331 case opcodetype.OP_ABS:
333 case opcodetype.OP_NOT:
335 case opcodetype.OP_0NOTEQUAL:
336 return "OP_0NOTEQUAL";
337 case opcodetype.OP_ADD:
339 case opcodetype.OP_SUB:
341 case opcodetype.OP_MUL:
343 case opcodetype.OP_DIV:
345 case opcodetype.OP_MOD:
347 case opcodetype.OP_LSHIFT:
349 case opcodetype.OP_RSHIFT:
351 case opcodetype.OP_BOOLAND:
353 case opcodetype.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:
371 case opcodetype.OP_MAX:
373 case opcodetype.OP_WITHIN:
377 case opcodetype.OP_RIPEMD160:
378 return "OP_RIPEMD160";
379 case opcodetype.OP_SHA1:
381 case opcodetype.OP_SHA256:
383 case opcodetype.OP_HASH160:
385 case opcodetype.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";
399 case opcodetype.OP_NOP1:
401 case opcodetype.OP_NOP2:
403 case opcodetype.OP_NOP3:
405 case opcodetype.OP_NOP4:
407 case opcodetype.OP_NOP5:
409 case opcodetype.OP_NOP6:
411 case opcodetype.OP_NOP7:
413 case opcodetype.OP_NOP8:
415 case opcodetype.OP_NOP9:
417 case opcodetype.OP_NOP10:
420 // template matching params
421 case opcodetype.OP_PUBKEYHASH:
422 return "OP_PUBKEYHASH";
423 case opcodetype.OP_PUBKEY:
425 case opcodetype.OP_SMALLDATA:
426 return "OP_SMALLDATA";
428 case opcodetype.OP_INVALIDOPCODE:
429 return "OP_INVALIDOPCODE";
436 /// Get next opcode from passed list of bytes and extract push arguments if there are some.
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)
444 bytesRet = new List<byte>();
445 opcodeRet = opcodetype.OP_INVALIDOPCODE;
452 opcode = (opcodetype)codeBytes.GetItem();
454 catch (WrappedListException)
456 // No instruction found there
461 if (opcode <= opcodetype.OP_PUSHDATA4)
463 byte[] szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length
467 if (opcode < opcodetype.OP_PUSHDATA1)
469 // Zero value opcodes (OP_0, OP_FALSE)
470 szBytes[3] = (byte)opcode;
472 else if (opcode == opcodetype.OP_PUSHDATA1)
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();
478 else if (opcode == opcodetype.OP_PUSHDATA2)
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);
484 else if (opcode == opcodetype.OP_PUSHDATA4)
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);
491 catch (WrappedListException)
493 // Unable to read operand length
497 int nSize = (int)Interop.BEBytesToUInt32(szBytes);
501 // If nSize is greater than zero then there is some data available
504 // Read found number of bytes into list of OP_PUSHDATAn arguments.
505 bytesRet = codeBytes.GetEnumerableItems(nSize);
507 catch (WrappedListException)
509 // Unable to read data
521 /// Convert value bytes into readable representation.
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.
525 /// <param name="bytes">Collection of value bytes.</param>
526 /// <returns>Formatted value.</returns>
527 public static string ValueString(IEnumerable<byte> bytes)
529 StringBuilder sb = new StringBuilder();
531 if (bytes.Count() <= 4)
533 byte[] valueBytes = new byte[4] { 0, 0, 0, 0 };
534 bytes.ToArray().CopyTo(valueBytes, valueBytes.Length - bytes.Count());
536 sb.Append(Interop.BEBytesToUInt32(valueBytes));
540 return Interop.ToHex(bytes);
543 return sb.ToString();
547 /// Convert list of stack items into human readable representation.
549 /// <param name="stackList">List of stack items.</param>
550 /// <returns>Formatted value.</returns>
551 public static string StackString(IList<IList<byte>> stackList)
553 StringBuilder sb = new StringBuilder();
554 foreach (IList<byte> bytesList in stackList)
556 sb.Append(ValueString(bytesList));
559 return sb.ToString();
563 /// Decode small integer
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)
569 if (opcode == opcodetype.OP_0)
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);
579 /// Converts small integer into opcode
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)
585 // The n value must be in the range of 0 to 16.
587 throw new Exception("Invalid small integer value.");
589 return opcodetype.OP_0;
590 return (opcodetype)(opcodetype.OP_1 + n - 1);
593 public static int ScriptSigArgsExpected(txnouttype t, IList<IEnumerable<byte>> solutions)
597 case txnouttype.TX_NONSTANDARD:
599 case txnouttype.TX_NULL_DATA:
601 case txnouttype.TX_PUBKEY:
603 case txnouttype.TX_PUBKEYHASH:
605 case txnouttype.TX_MULTISIG:
606 if (solutions.Count() < 1 || solutions.First().Count() < 1)
608 return solutions.First().First() + 1;
609 case txnouttype.TX_SCRIPTHASH:
610 return 1; // doesn't include args needed by the script
616 public static bool IsStandard(CScript scriptPubKey, out txnouttype whichType)
618 IList<IEnumerable<byte>> solutions = new List<IEnumerable<byte>>();
620 if (!Solver(scriptPubKey, out whichType, out solutions))
622 // No solutions found
626 if (whichType == txnouttype.TX_MULTISIG)
628 byte m = solutions.First().First();
629 byte n = solutions.Last().First();
631 // Support up to x-of-3 multisig txns as standard
642 return whichType != txnouttype.TX_NONSTANDARD;
646 /// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
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)
654 solutions = new List<IEnumerable<byte>>();
656 // There are shortcuts for pay-to-script-hash and pay-to-pubkey-hash, which are more constrained than the other types:
658 // It is always OP_HASH160 20 [20 byte hash] OP_EQUAL
659 if (scriptPubKey.IsPayToScriptHash)
661 typeRet = txnouttype.TX_SCRIPTHASH;
663 // Take 20 bytes with offset of 2 bytes
664 IEnumerable<byte> hashBytes = scriptPubKey.Bytes.Skip(2).Take(20);
665 solutions.Add(hashBytes);
670 // It is always OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
671 if (scriptPubKey.IsPayToPubKeyHash)
673 typeRet = txnouttype.TX_PUBKEYHASH;
675 // Take 20 bytes with offset of 3 bytes
676 IEnumerable<byte> hashBytes = scriptPubKey.Bytes.Skip(3).Take(20);
677 solutions.Add(hashBytes);
682 List<Tuple<txnouttype, IEnumerable<byte>>> templateTuples = new List<Tuple<txnouttype, IEnumerable<byte>>>();
684 // Sender provides pubkey, receiver adds signature
685 // [ECDSA public key] OP_CHECKSIG
687 new Tuple<txnouttype, IEnumerable<byte>>(
688 txnouttype.TX_PUBKEY,
689 new byte[] { (byte)opcodetype.OP_PUBKEY, (byte)opcodetype.OP_CHECKSIG })
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)
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 })
701 // Data-carrying output
702 // OP_RETURN [up to 80 bytes of data]
704 new Tuple<txnouttype, IEnumerable<byte>>(
705 txnouttype.TX_NULL_DATA,
706 new byte[] { (byte)opcodetype.OP_RETURN, (byte)opcodetype.OP_SMALLDATA })
709 // Nonstandard tx output
710 typeRet = txnouttype.TX_NONSTANDARD;
712 foreach (Tuple<txnouttype, IEnumerable<byte>> templateTuple in templateTuples)
714 CScript script1 = scriptPubKey;
715 CScript script2 = new CScript(templateTuple.Item2);
717 opcodetype opcode1, opcode2;
720 WrappedList<byte> wl1 = script1.GetWrappedList();
721 WrappedList<byte> wl2 = script2.GetWrappedList();
723 IEnumerable<byte> args1, args2;
725 byte last1 = script1.Bytes.Last();
726 byte last2 = script2.Bytes.Last();
730 if (wl1.GetCurrentItem() == last1 && wl2.GetCurrentItem() == last2)
733 typeRet = templateTuple.Item1;
734 if (typeRet == txnouttype.TX_MULTISIG)
736 // Additional checks for TX_MULTISIG:
737 byte m = solutions.First().First();
738 byte n = solutions.Last().First();
740 if (m < 1 || n < 1 || m > n || solutions.Count - 2 != n)
748 if (!GetOp(ref wl1, out opcode1, out args1))
752 if (!GetOp(ref wl2, out opcode2, out args2))
757 // Template matching opcodes:
758 if (opcode2 == opcodetype.OP_PUBKEYS)
760 while (args1.Count() >= 33 && args1.Count() <= 120)
762 solutions.Add(args1);
763 if (!GetOp(ref wl1, out opcode1, out args1))
768 if (!GetOp(ref wl2, out opcode2, out args2))
770 // Normal situation is to fall through
771 // to other if/else statements
773 if (opcode2 == opcodetype.OP_PUBKEY)
775 if (args1.Count() < 33 || args1.Count() > 120)
779 solutions.Add(args1);
781 else if (opcode2 == opcodetype.OP_PUBKEYHASH)
783 if (args1.Count() != 20) // hash160 size
787 solutions.Add(args1);
789 else if (opcode2 == opcodetype.OP_SMALLINTEGER)
791 // Single-byte small integer pushed onto solutions
792 if (opcode1 == opcodetype.OP_0 || (opcode1 >= opcodetype.OP_1 && opcode1 <= opcodetype.OP_16))
794 byte n = (byte)DecodeOP_N(opcode1);
795 solutions.Add(new byte[] { n });
802 else if (opcode2 == opcodetype.OP_SMALLDATA)
804 // small pushdata, <= 80 bytes
805 if (args1.Count() > 80)
810 else if (opcode1 != opcode2 || !args1.SequenceEqual(args2))
812 // Others must match exactly
819 typeRet = txnouttype.TX_NONSTANDARD;