/// <summary>
        /// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
        /// </summary>
        /// <param name="scriptPubKey">CScript instance</param>
        /// <param name="typeRet">Output type</param>
        /// <param name="solutions">Set of solutions</param>
        /// <returns>Result</returns>
        public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList<byte[]> solutions)
        {
            byte[] scriptBytes = scriptPubKey;

            solutions = new List<byte[]>();

            // There are shortcuts for pay-to-script-hash and pay-to-pubkey-hash, which are more constrained than the other types.

            // It is always OP_HASH160 20 [20 byte hash] OP_EQUAL
            if (scriptPubKey.IsPayToScriptHash)
            {
                typeRet = txnouttype.TX_SCRIPTHASH;

                // Take 20 bytes with offset of 2 bytes
                var hashBytes = scriptBytes.Skip(2).Take(20);
                solutions.Add(hashBytes.ToArray());

                return true;
            }

            // It is always OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
            if (scriptPubKey.IsPayToPubKeyHash)
            {
                typeRet = txnouttype.TX_PUBKEYHASH;

                // Take 20 bytes with offset of 3 bytes
                var hashBytes = scriptBytes.Skip(3).Take(20);
                solutions.Add(hashBytes.ToArray());

                return true;
            }

            var templateTuples = new List<Tuple<txnouttype, byte[]>>();

            // Sender provides pubkey, receiver adds signature
            // [ECDSA public key] OP_CHECKSIG
            templateTuples.Add(
                new Tuple<txnouttype, byte[]>(
                    txnouttype.TX_PUBKEY,
                    new byte[] {
                        (byte)instruction.OP_PUBKEY,
                        (byte)instruction.OP_CHECKSIG
                    })
            );

            // Sender provides N pubkeys, receivers provides M signatures
            // N [pubkey1] [pubkey2] ... [pubkeyN] M OP_CHECKMULTISIG
            // Where N and M are small integer instructions (OP1 ... OP_16)
            templateTuples.Add(
                new Tuple<txnouttype, byte[]>(
                    txnouttype.TX_MULTISIG,
                    new byte[] {
                        (byte)instruction.OP_SMALLINTEGER,
                        (byte)instruction.OP_PUBKEYS,
                        (byte)instruction.OP_SMALLINTEGER,
                        (byte)instruction.OP_CHECKMULTISIG
                    })
            );

            // Data-carrying output
            // OP_RETURN [up to 80 bytes of data]
            templateTuples.Add(
                new Tuple<txnouttype, byte[]>(
                    txnouttype.TX_NULL_DATA,
                    new byte[] {
                        (byte)instruction.OP_RETURN,
                        (byte)instruction.OP_SMALLDATA
                    })
            );

            // Nonstandard tx output
            typeRet = txnouttype.TX_NONSTANDARD;

            foreach (var templateTuple in templateTuples)
            {
                var script1 = scriptPubKey;
                var script2 = new CScript(templateTuple.Item2);

                instruction opcode1, opcode2;

                // Compare
                var bq1 = script1.GetInstructionQueue();
                var bq2 = script2.GetInstructionQueue();

                byte[] args1, args2;

                int last1 = ((byte[])script1).Length - 1;
                int last2 = ((byte[])script2).Length - 1;

                while (true)
                {
                    if (bq1.Index == last1 && bq2.Index == last2)
                    {
                        // Found a match
                        typeRet = templateTuple.Item1;
                        if (typeRet == txnouttype.TX_MULTISIG)
                        {
                            // Additional checks for TX_MULTISIG:
                            var m = solutions.First().First();
                            var n = solutions.Last().First();

                            if (m < 1 || n < 1 || m > n || solutions.Count - 2 != n)
                            {
                                return false;
                            }
                        }
                        return true;
                    }

                    if (!GetOp(ref bq1, out opcode1, out args1))
                    {
                        break;
                    }
                    if (!GetOp(ref bq2, out opcode2, out args2))
                    {
                        break;
                    }

                    // Template matching instructions:
                    if (opcode2 == instruction.OP_PUBKEYS)
                    {
                        while (args1.Count() >= 33 && args1.Count() <= 120)
                        {
                            solutions.Add(args1);
                            if (!GetOp(ref bq1, out opcode1, out args1))
                            {
                                break;
                            }
                        }
                        if (!GetOp(ref bq2, out opcode2, out args2))
                        {
                            break;
                        }
                        // Normal situation is to fall through
                        // to other if/else statements
                    }
                    if (opcode2 == instruction.OP_PUBKEY)
                    {
                        int PubKeyLen = args1.Count();
                        if (PubKeyLen < 33 || PubKeyLen > 120)
                        {
                            break;
                        }
                        solutions.Add(args1);
                    }
                    else if (opcode2 == instruction.OP_PUBKEYHASH)
                    {
                        if (args1.Count() != 20) // hash160 size
                        {
                            break;
                        }
                        solutions.Add(args1);
                    }
                    else if (opcode2 == instruction.OP_SMALLINTEGER)
                    {
                        // Single-byte small integer pushed onto solutions
                        try
                        {
                            var n = (byte)DecodeOP_N(opcode1);
                            solutions.Add(new byte[] { n });
                        }
                        catch (Exception)
                        {
                            break;
                        }
                    }
                    else if (opcode2 == instruction.OP_SMALLDATA)
                    {
                        // small pushdata, <= 80 bytes
                        if (args1.Length > 80)
                        {
                            break;
                        }
                    }
                    else if (opcode1 != opcode2 || !args1.SequenceEqual(args2))
                    {
                        // Others must match exactly
                        break;
                    }
                }
            }

            solutions.Clear();
            typeRet = txnouttype.TX_NONSTANDARD;

            return false;
        }
        /// <summary>
        /// Is it a standart type of scriptPubKey?
        /// </summary>
        /// <param name="scriptPubKey">CScript instance</param>
        /// <param name="whichType">utut type</param>
        /// <returns>Checking result</returns>
        public static bool IsStandard(CScript scriptPubKey, out txnouttype whichType)
        {
            IList<byte[]> solutions;

            if (!Solver(scriptPubKey, out whichType, out solutions))
            {
                // No solutions found
                return false;
            }

            if (whichType == txnouttype.TX_MULTISIG)
            {
                // Additional verification of OP_CHECKMULTISIG arguments
                var m = solutions.First()[0];
                var n = solutions.Last()[0];

                // Support up to x-of-3 multisig txns as standard
                if (n < 1 || n > 3)
                {
                    return false;
                }
                if (m < 1 || m > n)
                {
                    return false;
                }
            }

            return whichType != txnouttype.TX_NONSTANDARD;
        }
 public static int ScriptSigArgsExpected(txnouttype t, IList<byte[]> solutions)
 {
     switch (t)
     {
         case txnouttype.TX_NONSTANDARD:
             return -1;
         case txnouttype.TX_NULL_DATA:
             return 1;
         case txnouttype.TX_PUBKEY:
             return 1;
         case txnouttype.TX_PUBKEYHASH:
             return 2;
         case txnouttype.TX_MULTISIG:
             if (solutions.Count < 1 || solutions.First().Length < 1)
                 return -1;
             return solutions.First()[0] + 1;
         case txnouttype.TX_SCRIPTHASH:
             return 1; // doesn't include args needed by the script
     }
     return -1;
 }
 public static string GetTxnOutputType(txnouttype t)
 {
     switch (t)
     {
         case txnouttype.TX_NONSTANDARD: return "nonstandard";
         case txnouttype.TX_PUBKEY: return "pubkey";
         case txnouttype.TX_PUBKEYHASH: return "pubkeyhash";
         case txnouttype.TX_SCRIPTHASH: return "scripthash";
         case txnouttype.TX_MULTISIG: return "multisig";
         case txnouttype.TX_NULL_DATA: return "nulldata";
     }
     return string.Empty;
 }