/**
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
namespace Novacoin
{
///
/// Script opcodes
///
public enum instruction
{
// push value
OP_0 = 0x00,
OP_FALSE = OP_0,
OP_PUSHDATA1 = 0x4c,
OP_PUSHDATA2 = 0x4d,
OP_PUSHDATA4 = 0x4e,
OP_1NEGATE = 0x4f,
OP_RESERVED = 0x50,
OP_1 = 0x51,
OP_TRUE = OP_1,
OP_2 = 0x52,
OP_3 = 0x53,
OP_4 = 0x54,
OP_5 = 0x55,
OP_6 = 0x56,
OP_7 = 0x57,
OP_8 = 0x58,
OP_9 = 0x59,
OP_10 = 0x5a,
OP_11 = 0x5b,
OP_12 = 0x5c,
OP_13 = 0x5d,
OP_14 = 0x5e,
OP_15 = 0x5f,
OP_16 = 0x60,
// control
OP_NOP = 0x61,
OP_VER = 0x62,
OP_IF = 0x63,
OP_NOTIF = 0x64,
OP_VERIF = 0x65,
OP_VERNOTIF = 0x66,
OP_ELSE = 0x67,
OP_ENDIF = 0x68,
OP_VERIFY = 0x69,
OP_RETURN = 0x6a,
// stack ops
OP_TOALTSTACK = 0x6b,
OP_FROMALTSTACK = 0x6c,
OP_2DROP = 0x6d,
OP_2DUP = 0x6e,
OP_3DUP = 0x6f,
OP_2OVER = 0x70,
OP_2ROT = 0x71,
OP_2SWAP = 0x72,
OP_IFDUP = 0x73,
OP_DEPTH = 0x74,
OP_DROP = 0x75,
OP_DUP = 0x76,
OP_NIP = 0x77,
OP_OVER = 0x78,
OP_PICK = 0x79,
OP_ROLL = 0x7a,
OP_ROT = 0x7b,
OP_SWAP = 0x7c,
OP_TUCK = 0x7d,
// splice ops
OP_CAT = 0x7e,
OP_SUBSTR = 0x7f,
OP_LEFT = 0x80,
OP_RIGHT = 0x81,
OP_SIZE = 0x82,
// bit logic
OP_INVERT = 0x83,
OP_AND = 0x84,
OP_OR = 0x85,
OP_XOR = 0x86,
OP_EQUAL = 0x87,
OP_EQUALVERIFY = 0x88,
OP_RESERVED1 = 0x89,
OP_RESERVED2 = 0x8a,
// numeric
OP_1ADD = 0x8b,
OP_1SUB = 0x8c,
OP_2MUL = 0x8d,
OP_2DIV = 0x8e,
OP_NEGATE = 0x8f,
OP_ABS = 0x90,
OP_NOT = 0x91,
OP_0NOTEQUAL = 0x92,
OP_ADD = 0x93,
OP_SUB = 0x94,
OP_MUL = 0x95,
OP_DIV = 0x96,
OP_MOD = 0x97,
OP_LSHIFT = 0x98,
OP_RSHIFT = 0x99,
OP_BOOLAND = 0x9a,
OP_BOOLOR = 0x9b,
OP_NUMEQUAL = 0x9c,
OP_NUMEQUALVERIFY = 0x9d,
OP_NUMNOTEQUAL = 0x9e,
OP_LESSTHAN = 0x9f,
OP_GREATERTHAN = 0xa0,
OP_LESSTHANOREQUAL = 0xa1,
OP_GREATERTHANOREQUAL = 0xa2,
OP_MIN = 0xa3,
OP_MAX = 0xa4,
OP_WITHIN = 0xa5,
// crypto
OP_RIPEMD160 = 0xa6,
OP_SHA1 = 0xa7,
OP_SHA256 = 0xa8,
OP_HASH160 = 0xa9,
OP_HASH256 = 0xaa,
OP_CODESEPARATOR = 0xab,
OP_CHECKSIG = 0xac,
OP_CHECKSIGVERIFY = 0xad,
OP_CHECKMULTISIG = 0xae,
OP_CHECKMULTISIGVERIFY = 0xaf,
// expansion
OP_NOP1 = 0xb0,
OP_NOP2 = 0xb1,
OP_NOP3 = 0xb2,
OP_NOP4 = 0xb3,
OP_NOP5 = 0xb4,
OP_NOP6 = 0xb5,
OP_NOP7 = 0xb6,
OP_NOP8 = 0xb7,
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
// template matching params
OP_SMALLDATA = 0xf9,
OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb,
OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe,
OP_INVALIDOPCODE = 0xff,
};
///
/// Transaction output types.
///
public enum txnouttype
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
TX_NULL_DATA,
};
///
/// Signature hash types/flags
///
public enum sigflag
{
SIGHASH_ALL = 1,
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,
};
/** Script verification flags */
public enum scriptflag
{
SCRIPT_VERIFY_NONE = 0,
SCRIPT_VERIFY_P2SH = (1 << 0), // evaluate P2SH (BIP16) subscripts
SCRIPT_VERIFY_STRICTENC = (1 << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
SCRIPT_VERIFY_LOW_S = (1 << 2), // enforce low S values in signatures (depends on STRICTENC)
SCRIPT_VERIFY_NOCACHE = (1 << 3), // do not store results in signature cache (but do query it)
SCRIPT_VERIFY_NULLDUMMY = (1 << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
};
public static class ScriptCode
{
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;
}
///
/// Get the name of supplied opcode
///
/// Opcode
/// Opcode name
public static string GetOpName(instruction opcode)
{
if (opcode == instruction.OP_0) // OP_0 and OP_FALSE are synonyms
return "OP_0";
if (opcode == instruction.OP_1) // OP_1 and OP_TRUE are synonyms
return "OP_1";
return Enum.GetName(typeof(instruction), opcode);
}
///
/// Get next opcode from passed list of bytes and extract push arguments if there are some.
///
/// ByteQueue reference.
/// Found opcode.
/// IEnumerable out param which is used to get the push arguments.
/// Result of operation
public static bool GetOp(ref ByteQueue codeBytes, out instruction opcodeRet, out IEnumerable bytesRet)
{
bytesRet = new List();
opcodeRet = instruction.OP_INVALIDOPCODE;
instruction opcode;
try
{
// Read instruction
opcode = (instruction)codeBytes.Get();
}
catch (ByteQueueException)
{
// No instruction found there
return false;
}
// Immediate operand
if (opcode <= instruction.OP_PUSHDATA4)
{
byte[] szBytes = new byte[4] { 0, 0, 0, 0 }; // Zero length
try
{
if (opcode < instruction.OP_PUSHDATA1)
{
// Zero value opcodes (OP_0, OP_FALSE)
szBytes[3] = (byte)opcode;
}
else if (opcode == instruction.OP_PUSHDATA1)
{
// 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] = codeBytes.Get();
}
else if (opcode == instruction.OP_PUSHDATA2)
{
// The next two bytes contain the number of bytes to be pushed onto the stack,
// i.e. now your operation will seem like this: OP_PUSHDATA2 0x00 0x01 [0x5a]
codeBytes.Get(2).CopyTo(szBytes, 2);
}
else if (opcode == instruction.OP_PUSHDATA4)
{
// The next four bytes contain the number of bytes to be pushed onto the stack,
// OP_PUSHDATA4 0x00 0x00 0x00 0x01 [0x5a]
szBytes = codeBytes.Get(4);
}
}
catch (ByteQueueException)
{
// Unable to read operand length
return false;
}
int nSize = (int)Interop.BEBytesToUInt32(szBytes);
if (nSize > 0)
{
// If nSize is greater than zero then there is some data available
try
{
// Read found number of bytes into list of OP_PUSHDATAn arguments.
bytesRet = codeBytes.GetEnumerable(nSize);
}
catch (ByteQueueException)
{
// Unable to read data
return false;
}
}
}
opcodeRet = opcode;
return true;
}
///
/// Convert value bytes into readable representation.
///
/// 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.
///
/// Collection of value bytes.
/// Formatted value.
public static string ValueString(IEnumerable bytes)
{
StringBuilder sb = new StringBuilder();
if (bytes.Count() <= 4)
{
byte[] valueBytes = new byte[4] { 0, 0, 0, 0 };
bytes.ToArray().CopyTo(valueBytes, valueBytes.Length - bytes.Count());
sb.Append(Interop.BEBytesToUInt32(valueBytes));
}
else
{
return Interop.ToHex(bytes);
}
return sb.ToString();
}
///
/// Convert list of stack items into human readable representation.
///
/// List of stack items.
/// Formatted value.
public static string StackString(IList> stackList)
{
StringBuilder sb = new StringBuilder();
foreach (IList bytesList in stackList)
{
sb.Append(ValueString(bytesList));
}
return sb.ToString();
}
///
/// Decode instruction to integer value
///
/// Small integer opcode (OP_1_NEGATE and OP_0 - OP_16)
/// Small integer
public static int DecodeOP_N(instruction opcode, bool AllowNegate = false)
{
if (AllowNegate && opcode == instruction.OP_1NEGATE)
{
return -1;
}
if (opcode == instruction.OP_0)
{
return 0;
}
// Only OP_n opcodes are supported, throw exception otherwise.
if (opcode < instruction.OP_1 || opcode > instruction.OP_16)
{
throw new ArgumentException("Invalid integer instruction.");
}
return (int)opcode - (int)(instruction.OP_1 - 1);
}
///
/// Converts integer into instruction
///
/// Small integer from the range of -1 up to 16.
/// Corresponding opcode.
public static instruction EncodeOP_N(int n, bool allowNegate = false)
{
if (allowNegate && n == -1)
{
return instruction.OP_1NEGATE;
}
if (n == 0)
{
return instruction.OP_0;
}
// The n value must be in the range of 0 to 16.
if (n < 0 || n > 16)
throw new ArgumentException("Invalid integer value.");
return (instruction.OP_1 + n - 1);
}
public static int ScriptSigArgsExpected(txnouttype t, IList> 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;
}
///
/// Is it a standart type of scriptPubKey?
///
/// CScript instance
/// utut type
/// Checking result
public static bool IsStandard(CScript scriptPubKey, out txnouttype whichType)
{
IList> solutions = new List>();
if (!Solver(scriptPubKey, out whichType, out solutions))
{
// No solutions found
return false;
}
if (whichType == txnouttype.TX_MULTISIG)
{
// Additional verification of OP_CHECKMULTISIG arguments
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;
}
///
/// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
///
/// CScript instance
/// Output type
/// Set of solutions
/// Result
public static bool Solver(CScript scriptPubKey, out txnouttype typeRet, out IList> solutions)
{
solutions = new List>();
// 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
IEnumerable 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)
{
typeRet = txnouttype.TX_PUBKEYHASH;
// Take 20 bytes with offset of 3 bytes
IEnumerable hashBytes = scriptPubKey.Bytes.Skip(3).Take(20);
solutions.Add(hashBytes);
return true;
}
List>> templateTuples = new List>>();
// Sender provides pubkey, receiver adds signature
// [ECDSA public key] OP_CHECKSIG
templateTuples.Add(
new Tuple>(
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 opcodes (OP1 ... OP_16)
templateTuples.Add(
new Tuple>(
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.TX_NULL_DATA,
new byte[] {
(byte)instruction.OP_RETURN,
(byte)instruction.OP_SMALLDATA
})
);
// Nonstandard tx output
typeRet = txnouttype.TX_NONSTANDARD;
foreach (Tuple> templateTuple in templateTuples)
{
CScript script1 = scriptPubKey;
CScript script2 = new CScript(templateTuple.Item2);
instruction opcode1, opcode2;
// Compare
ByteQueue bq1 = script1.GetByteQUeue();
ByteQueue bq2 = script2.GetByteQUeue();
IEnumerable args1, args2;
int last1 = script1.Bytes.Count() -1;
int last2 = script2.Bytes.Count() - 1;
while (true)
{
if (bq1.CurrentIndex == last1 && bq2.CurrentIndex == last2)
{
// Found a match
typeRet = templateTuple.Item1;
if (typeRet == txnouttype.TX_MULTISIG)
{
// Additional checks for TX_MULTISIG:
byte m = solutions.First().First();
byte 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 opcodes:
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
{
byte 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.Count() > 80)
{
break;
}
}
else if (opcode1 != opcode2 || !args1.SequenceEqual(args2))
{
// Others must match exactly
break;
}
}
}
solutions.Clear();
typeRet = txnouttype.TX_NONSTANDARD;
return false;
}
///
/// Generation of SignatureHash. This method is responsible for removal of transaction metadata. It's necessary signature can't sign itself.
///
/// Spending instructions
/// Instance of transaction
/// Input number
/// Hash type flag
///
public static Hash256 SignatureHash(CScript script, CTransaction txTo, int nIn, int nHashType)
{
if (nIn >= txTo.vin.Length)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("ERROR: SignatureHash() : nIn={0} out of range\n", nIn);
throw new ArgumentOutOfRangeException("nIn", sb.ToString());
}
// Init a copy of transaction
CTransaction txTmp = new CTransaction(txTo);
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
script.RemovePattern(new byte[] { (byte)instruction.OP_CODESEPARATOR });
// Blank out other inputs' signatures
for (int i = 0; i < txTmp.vin.Length; i++)
{
txTmp.vin[i].scriptSig = new CScript();
}
txTmp.vin[nIn].scriptSig = script;
// Blank out some of the outputs
if ((nHashType & 0x1f) == (int)sigflag.SIGHASH_NONE)
{
// Wildcard payee
txTmp.vout = new CTxOut[0];
// Let the others update at will
for (int i = 0; i < txTmp.vin.Length; i++)
{
if (i != nIn)
{
txTmp.vin[i].nSequence = 0;
}
}
}
else if ((nHashType & 0x1f) == (int)sigflag.SIGHASH_SINGLE)
{
// Only lock-in the txout payee at same index as txin
int nOut = nIn;
if (nOut >= txTmp.vout.Length)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("ERROR: SignatureHash() : nOut={0} out of range\n", nOut);
throw new ArgumentOutOfRangeException("nOut", sb.ToString());
}
Array.Resize(ref txTmp.vout, nOut + 1);
for (int i = 0; i < nOut; i++)
{
txTmp.vout[i] = new CTxOut();
}
// Let the others update at will
for (int i = 0; i < txTmp.vin.Length; i++)
{
if (i != nIn)
{
txTmp.vin[i].nSequence = 0;
}
}
}
// Blank out other inputs completely, not recommended for open transactions
if ((nHashType & (int)sigflag.SIGHASH_ANYONECANPAY) != 0)
{
txTmp.vin[0] = txTmp.vin[nIn];
Array.Resize(ref txTmp.vin, 1);
}
// Serialize and hash
List b = new List();
b.AddRange(txTmp.Bytes);
b.AddRange(BitConverter.GetBytes(nHashType));
return Hash256.Compute256(b);
}
//
// Script is a stack machine (like Forth) that evaluates a predicate
// returning a bool indicating valid or not. There are no loops.
//
///
/// Script machine exception
///
public class StackMachineException : Exception
{
public StackMachineException()
{
}
public StackMachineException(string message)
: base(message)
{
}
public StackMachineException(string message, Exception inner)
: base(message, inner)
{
}
}
///
/// Remove last element from stack
///
/// Stack reference
private static void popstack(ref List> stack)
{
int nCount = stack.Count;
if (nCount == 0)
throw new StackMachineException("popstack() : stack empty");
stack.RemoveAt(nCount - 1);
}
///
/// Get element at specified stack depth
///
/// Stack reference
/// Depth
/// Byte sequence
private static IEnumerable stacktop(ref List> stack, int nDepth)
{
int nStackElement = stack.Count + nDepth;
if (nDepth >= 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("stacktop() : positive depth ({0}) has no sense.", nDepth);
throw new StackMachineException(sb.ToString());
}
if (nStackElement < 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("stacktop() : nDepth={0} exceeds real stack depth ({1})", nDepth, stack.Count);
throw new StackMachineException(sb.ToString());
}
return stack[nStackElement];
}
///
/// Cast argument to boolean value
///
/// Some byte sequence
///
private static bool CastToBool(IEnumerable arg)
{
byte[] value = arg.ToArray();
for (var i = 0; i < value.Length; i++)
{
if (value[i] != 0)
{
// Can be negative zero
if (i == value.Length - 1 && value[i] == 0x80)
{
return false;
}
return true;
}
}
return false;
}
///
/// Cast argument to integer value
///
///
///
private static BigInteger CastToBigInteger(IEnumerable value)
{
if (value.Count() > 4)
{
throw new StackMachineException("CastToBigInteger() : overflow");
}
return new BigInteger(value.ToArray());
}
///
/// Execution of script
///
///
/// Script to execute
/// Transaction instance
/// Input number
/// Signature checking flags
/// Hash type flag
///
public static bool EvalScript(ref List> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType)
{
if (script.Bytes.Count() > 10000)
{
return false; // Size limit failed
}
List vfExec = new List();
int nOpCount = 0;
int nCodeHashBegin = 0;
byte[] falseBytes = new byte[0];
byte[] trueBytes = new byte[] { 0x01 };
ByteQueue CodeQueue = script.GetByteQUeue();
List> altStack = new List>();
try
{
instruction opcode;
IEnumerable pushArg;
while (GetOp(ref CodeQueue, out opcode, out pushArg)) // Read instructions
{
bool fExec = vfExec.IndexOf(false) == -1;
if (pushArg.Count() > 520)
{
return false; // Script element size limit failed
}
if (opcode > instruction.OP_16 && ++nOpCount > 201)
{
return false;
}
if (fExec && 0 <= opcode && opcode <= instruction.OP_PUSHDATA4)
{
stack.Add(pushArg); // Push argument to stack
}
else if (fExec || (instruction.OP_IF <= opcode && opcode <= instruction.OP_ENDIF))
switch (opcode)
{
//
// Disabled opcodes
//
case instruction.OP_CAT:
case instruction.OP_SUBSTR:
case instruction.OP_LEFT:
case instruction.OP_RIGHT:
case instruction.OP_INVERT:
case instruction.OP_AND:
case instruction.OP_OR:
case instruction.OP_XOR:
case instruction.OP_2MUL:
case instruction.OP_2DIV:
case instruction.OP_MUL:
case instruction.OP_DIV:
case instruction.OP_MOD:
case instruction.OP_LSHIFT:
case instruction.OP_RSHIFT:
return false;
//
// Push integer instructions
//
case instruction.OP_1NEGATE:
case instruction.OP_1:
case instruction.OP_2:
case instruction.OP_3:
case instruction.OP_4:
case instruction.OP_5:
case instruction.OP_6:
case instruction.OP_7:
case instruction.OP_8:
case instruction.OP_9:
case instruction.OP_10:
case instruction.OP_11:
case instruction.OP_12:
case instruction.OP_13:
case instruction.OP_14:
case instruction.OP_15:
case instruction.OP_16:
{
// ( -- value)
BigInteger bn = DecodeOP_N(opcode, true);
stack.Add(bn.ToByteArray());
}
break;
//
// Extension
//
case instruction.OP_NOP:
case instruction.OP_NOP1:
case instruction.OP_NOP2:
case instruction.OP_NOP3:
case instruction.OP_NOP4:
case instruction.OP_NOP5:
case instruction.OP_NOP6:
case instruction.OP_NOP7:
case instruction.OP_NOP8:
case instruction.OP_NOP9:
case instruction.OP_NOP10:
{
// Just do nothing
}
break;
//
// Control
//
case instruction.OP_IF:
case instruction.OP_NOTIF:
{
// if [statements] [else [statements]] endif
bool fValue = false;
if (fExec)
{
if (stack.Count() < 1)
{
return false;
}
IEnumerable vch = stacktop(ref stack, -1);
fValue = CastToBool(vch);
if (opcode == instruction.OP_NOTIF)
{
fValue = !fValue;
}
popstack(ref stack);
}
vfExec.Add(fValue);
}
break;
case instruction.OP_ELSE:
{
int nExecCount = vfExec.Count();
if (nExecCount == 0)
{
return false;
}
vfExec[nExecCount - 1] = !vfExec[nExecCount - 1];
}
break;
case instruction.OP_ENDIF:
{
int nExecCount = vfExec.Count();
if (nExecCount == 0)
{
return false;
}
vfExec.RemoveAt(nExecCount - 1);
}
break;
case instruction.OP_VERIFY:
{
// (true -- ) or
// (false -- false) and return
if (stack.Count() < 1)
{
return false;
}
bool fValue = CastToBool(stacktop(ref stack, -1));
if (fValue)
{
popstack(ref stack);
}
else
{
return false;
}
}
break;
case instruction.OP_RETURN:
{
return false;
}
//
// Stack ops
//
case instruction.OP_TOALTSTACK:
{
if (stack.Count() < 1)
{
return false;
}
altStack.Add(stacktop(ref stack, -1));
popstack(ref stack);
}
break;
case instruction.OP_FROMALTSTACK:
{
if (altStack.Count() < 1)
{
return false;
}
stack.Add(stacktop(ref stack, -1));
popstack(ref altStack);
}
break;
case instruction.OP_2DROP:
{
// (x1 x2 -- )
if (stack.Count() < 2)
{
return false;
}
popstack(ref stack);
popstack(ref stack);
}
break;
case instruction.OP_2DUP:
{
// (x1 x2 -- x1 x2 x1 x2)
if (stack.Count() < 2)
{
return false;
}
IEnumerable vch1 = stacktop(ref stack, -2);
IEnumerable vch2 = stacktop(ref stack, -1);
stack.Add(vch1);
stack.Add(vch2);
}
break;
case instruction.OP_3DUP:
{
// (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
if (stack.Count() < 3)
{
return false;
}
IEnumerable vch1 = stacktop(ref stack, -3);
IEnumerable vch2 = stacktop(ref stack, -2);
IEnumerable vch3 = stacktop(ref stack, -1);
stack.Add(vch1);
stack.Add(vch2);
stack.Add(vch3);
}
break;
case instruction.OP_2OVER:
{
// (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
if (stack.Count() < 4)
{
return false;
}
IEnumerable vch1 = stacktop(ref stack, -4);
IEnumerable vch2 = stacktop(ref stack, -3);
stack.Add(vch1);
stack.Add(vch2);
}
break;
case instruction.OP_2ROT:
{
int nStackDepth = stack.Count();
// (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
if (nStackDepth < 6)
{
return false;
}
IEnumerable vch1 = stacktop(ref stack, -6);
IEnumerable vch2 = stacktop(ref stack, -5);
stack.RemoveRange(nStackDepth - 6, 2);
stack.Add(vch1);
stack.Add(vch2);
}
break;
case instruction.OP_2SWAP:
{
// (x1 x2 x3 x4 -- x3 x4 x1 x2)
int nStackDepth = stack.Count();
if (nStackDepth < 4)
{
return false;
}
stack.Swap(nStackDepth - 4, nStackDepth - 2);
stack.Swap(nStackDepth - 3, nStackDepth - 1);
}
break;
case instruction.OP_IFDUP:
{
// (x - 0 | x x)
if (stack.Count() < 1)
{
return false;
}
IEnumerable vch = stacktop(ref stack, -1);
if (CastToBool(vch))
{
stack.Add(vch);
}
}
break;
case instruction.OP_DEPTH:
{
// -- stacksize
BigInteger bn = new BigInteger((ushort)stack.Count());
stack.Add(bn.ToByteArray());
}
break;
case instruction.OP_DROP:
{
// (x -- )
if (stack.Count() < 1)
{
return false;
}
popstack(ref stack);
}
break;
case instruction.OP_DUP:
{
// (x -- x x)
if (stack.Count() < 1)
{
return false;
}
IEnumerable vch = stacktop(ref stack, -1);
stack.Add(vch);
}
break;
case instruction.OP_NIP:
{
// (x1 x2 -- x2)
int nStackDepth = stack.Count();
if (nStackDepth < 2)
{
return false;
}
stack.RemoveAt(nStackDepth - 2);
}
break;
case instruction.OP_OVER:
{
// (x1 x2 -- x1 x2 x1)
if (stack.Count() < 2)
{
return false;
}
IEnumerable vch = stacktop(ref stack, -2);
stack.Add(vch);
}
break;
case instruction.OP_PICK:
case instruction.OP_ROLL:
{
// (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
int nStackDepth = stack.Count();
if (nStackDepth < 2)
{
return false;
}
int n = (int)CastToBigInteger(stacktop(ref stack, -1));
popstack(ref stack);
if (n < 0 || n >= stack.Count())
{
return false;
}
IEnumerable vch = stacktop(ref stack, -n - 1);
if (opcode == instruction.OP_ROLL)
{
stack.RemoveAt(nStackDepth - n - 1);
}
stack.Add(vch);
}
break;
case instruction.OP_ROT:
{
// (x1 x2 x3 -- x2 x3 x1)
// x2 x1 x3 after first swap
// x2 x3 x1 after second swap
int nStackDepth = stack.Count();
if (nStackDepth < 3)
{
return false;
}
stack.Swap(nStackDepth - 3, nStackDepth - 2);
stack.Swap(nStackDepth - 2, nStackDepth - 1);
}
break;
case instruction.OP_SWAP:
{
// (x1 x2 -- x2 x1)
int nStackDepth = stack.Count();
if (nStackDepth < 2)
{
return false;
}
stack.Swap(nStackDepth - 2, nStackDepth - 1);
}
break;
case instruction.OP_TUCK:
{
// (x1 x2 -- x2 x1 x2)
int nStackDepth = stack.Count();
if (nStackDepth < 2)
{
return false;
}
IEnumerable vch = stacktop(ref stack, -1);
stack.Insert(nStackDepth - 2, vch);
}
break;
case instruction.OP_SIZE:
{
// (in -- in size)
if (stack.Count() < 1)
{
return false;
}
BigInteger bnSize = new BigInteger((ushort)stacktop(ref stack, -1).Count());
stack.Add(bnSize.ToByteArray());
}
break;
//
// Bitwise logic
//
case instruction.OP_EQUAL:
case instruction.OP_EQUALVERIFY:
//case instruction.OP_NOTEQUAL: // use OP_NUMNOTEQUAL
{
// (x1 x2 - bool)
if (stack.Count() < 2)
{
return false;
}
IEnumerable vch1 = stacktop(ref stack, -2);
IEnumerable vch2 = stacktop(ref stack, -1);
bool fEqual = (vch1.SequenceEqual(vch2));
// OP_NOTEQUAL is disabled because it would be too easy to say
// something like n != 1 and have some wiseguy pass in 1 with extra
// zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
//if (opcode == instruction.OP_NOTEQUAL)
// fEqual = !fEqual;
popstack(ref stack);
popstack(ref stack);
stack.Add(fEqual ? trueBytes : falseBytes);
if (opcode == instruction.OP_EQUALVERIFY)
{
if (fEqual)
{
popstack(ref stack);
}
else
{
return false;
}
}
}
break;
//
// Numeric
//
case instruction.OP_1ADD:
case instruction.OP_1SUB:
case instruction.OP_NEGATE:
case instruction.OP_ABS:
case instruction.OP_NOT:
case instruction.OP_0NOTEQUAL:
{
// (in -- out)
if (stack.Count() < 1)
{
return false;
}
BigInteger bn = CastToBigInteger(stacktop(ref stack, -1));
switch (opcode)
{
case instruction.OP_1ADD:
bn = bn + 1;
break;
case instruction.OP_1SUB:
bn = bn - 1;
break;
case instruction.OP_NEGATE:
bn = -bn;
break;
case instruction.OP_ABS:
bn = BigInteger.Abs(bn);
break;
case instruction.OP_NOT:
bn = bn == 0 ? 1 : 0;
break;
case instruction.OP_0NOTEQUAL:
bn = bn != 0 ? 1 : 0;
break;
}
popstack(ref stack);
stack.Add(bn.ToByteArray());
}
break;
case instruction.OP_ADD:
case instruction.OP_SUB:
case instruction.OP_BOOLAND:
case instruction.OP_BOOLOR:
case instruction.OP_NUMEQUAL:
case instruction.OP_NUMEQUALVERIFY:
case instruction.OP_NUMNOTEQUAL:
case instruction.OP_LESSTHAN:
case instruction.OP_GREATERTHAN:
case instruction.OP_LESSTHANOREQUAL:
case instruction.OP_GREATERTHANOREQUAL:
case instruction.OP_MIN:
case instruction.OP_MAX:
{
// (x1 x2 -- out)
if (stack.Count() < 2)
{
return false;
}
BigInteger bn1 = CastToBigInteger(stacktop(ref stack, -2));
BigInteger bn2 = CastToBigInteger(stacktop(ref stack, -1));
BigInteger bn = 0;
switch (opcode)
{
case instruction.OP_ADD:
bn = bn1 + bn2;
break;
case instruction.OP_SUB:
bn = bn1 - bn2;
break;
case instruction.OP_BOOLAND:
bn = (bn1 != 0 && bn2 != 0) ? 1 : 0;
break;
case instruction.OP_BOOLOR:
bn = (bn1 != 0 || bn2 != 0) ? 1 : 0;
break;
case instruction.OP_NUMEQUAL:
bn = (bn1 == bn2) ? 1 : 0;
break;
case instruction.OP_NUMEQUALVERIFY:
bn = (bn1 == bn2) ? 1 : 0;
break;
case instruction.OP_NUMNOTEQUAL:
bn = (bn1 != bn2) ? 1 : 0;
break;
case instruction.OP_LESSTHAN:
bn = (bn1 < bn2) ? 1 : 0;
break;
case instruction.OP_GREATERTHAN:
bn = (bn1 > bn2) ? 1 : 0;
break;
case instruction.OP_LESSTHANOREQUAL:
bn = (bn1 <= bn2) ? 1 : 0;
break;
case instruction.OP_GREATERTHANOREQUAL:
bn = (bn1 >= bn2) ? 1 : 0;
break;
case instruction.OP_MIN:
bn = (bn1 < bn2 ? bn1 : bn2);
break;
case instruction.OP_MAX:
bn = (bn1 > bn2 ? bn1 : bn2);
break;
}
popstack(ref stack);
popstack(ref stack);
stack.Add(bn.ToByteArray());
if (opcode == instruction.OP_NUMEQUALVERIFY)
{
if (CastToBool(stacktop(ref stack, -1)))
{
popstack(ref stack);
}
else
{
return false;
}
}
}
break;
case instruction.OP_WITHIN:
{
// (x min max -- out)
if (stack.Count() < 3)
{
return false;
}
BigInteger bn1 = CastToBigInteger(stacktop(ref stack, -3));
BigInteger bn2 = CastToBigInteger(stacktop(ref stack, -2));
BigInteger bn3 = CastToBigInteger(stacktop(ref stack, -1));
bool fValue = (bn2 <= bn1 && bn1 < bn3);
popstack(ref stack);
popstack(ref stack);
popstack(ref stack);
stack.Add(fValue ? trueBytes : falseBytes);
}
break;
//
// Crypto
//
case instruction.OP_RIPEMD160:
case instruction.OP_SHA1:
case instruction.OP_SHA256:
case instruction.OP_HASH160:
case instruction.OP_HASH256:
{
// (in -- hash)
if (stack.Count() < 1)
{
return false;
}
Hash hash = null;
IEnumerable data = stacktop(ref stack, -1);
switch (opcode)
{
case instruction.OP_HASH160:
hash = Hash160.Compute160(data);
break;
case instruction.OP_HASH256:
hash = Hash256.Compute256(data);
break;
case instruction.OP_SHA1:
hash = SHA1.Compute1(data);
break;
case instruction.OP_SHA256:
hash = SHA256.Compute256(data);
break;
case instruction.OP_RIPEMD160:
hash = RIPEMD160.Compute160(data);
break;
}
popstack(ref stack);
stack.Add(hash.hashBytes);
}
break;
case instruction.OP_CODESEPARATOR:
{
// Hash starts after the code separator
nCodeHashBegin = CodeQueue.CurrentIndex;
}
break;
case instruction.OP_CHECKSIG:
case instruction.OP_CHECKSIGVERIFY:
{
// (sig pubkey -- bool)
if (stack.Count() < 2)
{
return false;
}
IList sigBytes = stacktop(ref stack, -2).ToList();
IList pubkeyBytes = stacktop(ref stack, -1).ToList();
// Subset of script starting at the most recent codeseparator
CScript scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin));
// There's no way for a signature to sign itself
scriptCode.RemovePattern(sigBytes);
bool fSuccess = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubkeyBytes.ToList(), flags) && CheckSig(sigBytes, pubkeyBytes, scriptCode, txTo, nIn, nHashType, flags);
popstack(ref stack);
popstack(ref stack);
stack.Add(fSuccess ? trueBytes : falseBytes);
if (opcode == instruction.OP_CHECKSIGVERIFY)
{
if (fSuccess)
{
popstack(ref stack);
}
else
{
return false;
}
}
}
break;
case instruction.OP_CHECKMULTISIG:
case instruction.OP_CHECKMULTISIGVERIFY:
{
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
int i = 1;
if (stack.Count() < i)
{
return false;
}
int nKeysCount = (int)CastToBigInteger(stacktop(ref stack, -i));
if (nKeysCount < 0 || nKeysCount > 20)
{
return false;
}
nOpCount += nKeysCount;
if (nOpCount > 201)
{
return false;
}
int ikey = ++i;
i += nKeysCount;
if (stack.Count() < i)
{
return false;
}
int nSigsCount = (int)CastToBigInteger(stacktop(ref stack, -i));
if (nSigsCount < 0 || nSigsCount > nKeysCount)
{
return false;
}
int isig = ++i;
i += nSigsCount;
if (stack.Count() < i)
{
return false;
}
// Subset of script starting at the most recent codeseparator
CScript scriptCode = new CScript(script.Bytes.Skip(nCodeHashBegin));
// There is no way for a signature to sign itself, so we need to drop the signatures
for (int k = 0; k < nSigsCount; k++)
{
IEnumerable vchSig = stacktop(ref stack, -isig - k);
scriptCode.RemovePattern(vchSig.ToList());
}
bool fSuccess = true;
while (fSuccess && nSigsCount > 0)
{
IList sigBytes = stacktop(ref stack, -isig).ToList();
IList pubKeyBytes = stacktop(ref stack, -ikey).ToList();
// Check signature
bool fOk = IsCanonicalSignature(sigBytes, flags) && IsCanonicalPubKey(pubKeyBytes.ToList(), flags) && CheckSig(sigBytes, pubKeyBytes, scriptCode, txTo, nIn, nHashType, flags);
if (fOk)
{
isig++;
nSigsCount--;
}
ikey++;
nKeysCount--;
// If there are more signatures left than keys left,
// then too many signatures have failed
if (nSigsCount > nKeysCount)
{
fSuccess = false;
}
}
while (i-- > 1)
{
popstack(ref stack);
}
// A bug causes CHECKMULTISIG to consume one extra argument
// whose contents were not checked in any way.
//
// Unfortunately this is a potential source of mutability,
// so optionally verify it is exactly equal to zero prior
// to removing it from the stack.
if (stack.Count() < 1)
{
return false;
}
if ((flags & (int)scriptflag.SCRIPT_VERIFY_NULLDUMMY) != 0 && stacktop(ref stack, -1).Count() != 0)
{
return false; // CHECKMULTISIG dummy argument not null
}
popstack(ref stack);
stack.Add(fSuccess ? trueBytes : falseBytes);
if (opcode == instruction.OP_CHECKMULTISIGVERIFY)
{
if (fSuccess)
{
popstack(ref stack);
}
else
{
return false;
}
}
}
break;
default:
return false;
}
// Size limits
if (stack.Count() + altStack.Count() > 1000)
{
return false;
}
}
}
catch (Exception)
{
// If there are any exceptions then just return false.
return false;
}
if (vfExec.Count() != 0)
{
// Something went wrong with conditional instructions.
return false;
}
return true;
}
public static bool IsCanonicalPubKey(IList pubKeyBytes, int flags)
{
if ((flags & (int)scriptflag.SCRIPT_VERIFY_STRICTENC) == 0)
return true;
if (pubKeyBytes.Count < 33)
return false; // Non-canonical public key: too short
if (pubKeyBytes[0] == 0x04)
{
if (pubKeyBytes.Count != 65)
return false; // Non-canonical public key: invalid length for uncompressed key
}
else if (pubKeyBytes[0] == 0x02 || pubKeyBytes[0] == 0x03)
{
if (pubKeyBytes.Count != 33)
return false; // Non-canonical public key: invalid length for compressed key
}
else
{
return false; // Non-canonical public key: compressed nor uncompressed
}
return true;
}
public static bool IsCanonicalSignature(IList sigBytes, int flags)
{
// STUB
return true;
}
///
/// Check signature.
///
/// Signature
/// Public key
/// Spending script
/// CTransaction instance
/// Input number
/// Hashing type flag
/// Signature checking flags
/// Checking result
public static bool CheckSig(IList sigBytes, IList pubkeyBytes, CScript script, CTransaction txTo, int nIn, int nHashType, int flags)
{
CPubKey pubkey;
try
{
// Trying to initialize the public key instance
pubkey = new CPubKey(pubkeyBytes);
}
catch (Exception)
{
// Exception occurred while initializing the public key
return false;
}
if (!pubkey.IsValid)
{
return false;
}
if (sigBytes.Count == 0)
{
return false;
}
// Hash type is one byte tacked on to the end of the signature
if (nHashType == 0)
{
nHashType = sigBytes.Last();
}
else if (nHashType != sigBytes.Last())
{
return false;
}
// Remove hash type
sigBytes.RemoveAt(sigBytes.Count - 1);
Hash256 sighash = SignatureHash(script, txTo, nIn, nHashType);
if (!pubkey.VerifySignature(sighash, sigBytes))
{
return false;
}
return true;
}
///
/// Evaluates the both scriptSig and scriptPubKey.
///
///
///
/// Transaction
/// Input number
/// Script validation flags
/// Hash type flag
///
public static bool VerifyScript(CScript scriptSig, CScript scriptPubKey, CTransaction txTo, int nIn, int flags, int nHashType)
{
List> stack = new List>();
List> stackCopy = null;
if (!EvalScript(ref stack, scriptSig, txTo, nIn, flags, nHashType))
{
return false;
}
if ((flags & (int)scriptflag.SCRIPT_VERIFY_P2SH) != 0)
{
stackCopy = new List> (stack);
}
if (!EvalScript(ref stack, scriptPubKey, txTo, nIn, flags, nHashType))
{
return false;
}
if (stack.Count == 0 || CastToBool(stack.Last()) == false)
{
return false;
}
// Additional validation for spend-to-script-hash transactions:
if ((flags & (int)scriptflag.SCRIPT_VERIFY_P2SH) != 0 && scriptPubKey.IsPayToScriptHash)
{
if (!scriptSig.IsPushOnly) // scriptSig must be literals-only
{
return false;
}
// stackCopy cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with
// an empty stack and the EvalScript above would return false.
if (stackCopy.Count == 0)
{
throw new StackMachineException("Fatal script validation error.");
}
CScript pubKey2 = new CScript(stackCopy.Last());
popstack(ref stackCopy);
if (!EvalScript(ref stackCopy, pubKey2, txTo, nIn, flags, nHashType))
return false;
if (stackCopy.Count == 0)
return false;
return CastToBool(stackCopy.Last());
}
return true;
}
};
}