public static class ScriptCode
{
-
+
/// <summary>
/// Get the name of supplied opcode
/// </summary>
// Immediate operand
if (opcode <= opcodetype.OP_PUSHDATA4)
{
- byte[] szBytes = new byte[4] {0, 0, 0, 0}; // Zero length
+ byte[] szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length
try
{
{
// The next byte contains the number of bytes to be pushed onto the stack,
// i.e. you have something like OP_PUSHDATA1 0x01 [0x5a]
- szBytes[3] = (byte) codeBytes.GetItem();
+ szBytes[3] = (byte)codeBytes.GetItem();
}
else if (opcode == opcodetype.OP_PUSHDATA2)
{
if (bytes.Count() <= 4)
{
- byte[] valueBytes = new byte[4] {0, 0, 0, 0};
+ byte[] valueBytes = new byte[4] { 0, 0, 0, 0 };
bytes.ToArray().CopyTo(valueBytes, valueBytes.Length - bytes.Count());
sb.Append(Interop.BEBytesToUInt32(valueBytes));
public static string StackString(IList<IList<byte>> stackList)
{
StringBuilder sb = new StringBuilder();
- foreach(IList<byte> bytesList in stackList)
+ foreach (IList<byte> bytesList in stackList)
{
sb.Append(ValueString(bytesList));
}
return (opcodetype)(opcodetype.OP_1 + n - 1);
}
+ public static int ScriptSigArgsExpected(txnouttype t, IList<IEnumerable<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().Count() < 1)
+ return -1;
+ return solutions.First().First() + 1;
+ case txnouttype.TX_SCRIPTHASH:
+ return 1; // doesn't include args needed by the script
+ }
+ return -1;
+ }
+
+
+ public static bool IsStandard(CScript scriptPubKey, out txnouttype whichType)
+ {
+ IList<IEnumerable<byte>> solutions = new List<IEnumerable<byte>>();
+
+ if (!Solver(scriptPubKey, out whichType, out solutions))
+ {
+ // No solutions found
+ return false;
+ }
+
+ if (whichType == txnouttype.TX_MULTISIG)
+ {
+ byte m = solutions.First().First();
+ byte n = solutions.Last().First();
+
+ // 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;
+ }
+
+ /// <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<IEnumerable<byte>> solutions)
{
solutions = new List<IEnumerable<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())
+ if (scriptPubKey.IsPayToScriptHash)
{
typeRet = txnouttype.TX_SCRIPTHASH;
// Take 20 bytes with offset of 2 bytes
- IEnumerable<byte> hashBytes = scriptPubKey.Enumerable.Skip(2).Take(20);
+ IEnumerable<byte> hashBytes = scriptPubKey.Bytes.Skip(2).Take(20);
solutions.Add(hashBytes);
return true;
}
// It is always OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG
- if (scriptPubKey.IsPayToPubKeyHash())
+ if (scriptPubKey.IsPayToPubKeyHash)
{
typeRet = txnouttype.TX_PUBKEYHASH;
// Take 20 bytes with offset of 3 bytes
- IEnumerable<byte> hashBytes = scriptPubKey.Enumerable.Skip(3).Take(20);
+ IEnumerable<byte> hashBytes = scriptPubKey.Bytes.Skip(3).Take(20);
solutions.Add(hashBytes);
return true;
// [ECDSA public key] OP_CHECKSIG
templateTuples.Add(
new Tuple<txnouttype, IEnumerable<byte>>(
- txnouttype.TX_PUBKEY,
+ txnouttype.TX_PUBKEY,
new byte[] { (byte)opcodetype.OP_PUBKEY, (byte)opcodetype.OP_CHECKSIG })
);
// OP_RETURN [up to 80 bytes of data]
templateTuples.Add(
new Tuple<txnouttype, IEnumerable<byte>>(
- txnouttype.TX_NULL_DATA,
+ txnouttype.TX_NULL_DATA,
new byte[] { (byte)opcodetype.OP_RETURN, (byte)opcodetype.OP_SMALLDATA })
);
foreach (Tuple<txnouttype, IEnumerable<byte>> templateTuple in templateTuples)
{
- CScript script2 = new CScript(templateTuple.Item2);
CScript script1 = scriptPubKey;
+ CScript script2 = new CScript(templateTuple.Item2);
opcodetype opcode1, opcode2;
IEnumerable<byte> args1, args2;
- byte last1 = script1.Enumerable.Last();
- byte last2 = script2.Enumerable.Last();
+ byte last1 = script1.Bytes.Last();
+ byte last2 = script2.Bytes.Last();
while (true)
{
- if (wl1.GetItem() == last1 && wl2.GetItem() == last2)
+ if (wl1.GetCurrentItem() == last1 && wl2.GetCurrentItem() == last2)
{
// Found a match
typeRet = templateTuple.Item1;
solutions.Add(args1);
}
else if (opcode2 == opcodetype.OP_SMALLINTEGER)
- {
+ {
// Single-byte small integer pushed onto solutions
if (opcode1 == opcodetype.OP_0 || (opcode1 >= opcodetype.OP_1 && opcode1 <= opcodetype.OP_16))
{
break;
}
}
- else if (opcode1 != opcode2 || args1.SequenceEqual(args2))
+ else if (opcode1 != opcode2 || !args1.SequenceEqual(args2))
{
// Others must match exactly
break;