using System.Linq;
using System.Text;
+using System.Numerics;
+
+// using Org.BouncyCastle.Math;
+
namespace Novacoin
{
/// <summary>
TX_NULL_DATA,
};
+ /// <summary>
+ /// Signature hash types/flags
+ /// </summary>
+ 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;
+ }
/// <summary>
/// Get the name of supplied opcode
/// <summary>
/// Get next opcode from passed list of bytes and extract push arguments if there are some.
/// </summary>
- /// <param name="codeBytes">WrappedList reference.</param>
+ /// <param name="codeBytes">ByteQueue reference.</param>
/// <param name="opcodeRet">Found opcode.</param>
/// <param name="bytesRet">IEnumerable out param which is used to get the push arguments.</param>
/// <returns>Result of operation</returns>
- public static bool GetOp(ref WrappedList<byte> codeBytes, out opcodetype opcodeRet, out IEnumerable<byte> bytesRet)
+ public static bool GetOp(ref ByteQueue codeBytes, out opcodetype opcodeRet, out IEnumerable<byte> bytesRet)
{
bytesRet = new List<byte>();
opcodeRet = opcodetype.OP_INVALIDOPCODE;
try
{
// Read instruction
- opcode = (opcodetype)codeBytes.GetItem();
+ opcode = (opcodetype)codeBytes.Get();
}
- catch (WrappedListException)
+ catch (ByteQueueException)
{
// No instruction found there
return false;
{
// 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.Get();
}
else if (opcode == opcodetype.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.GetItems(2).CopyTo(szBytes, 2);
+ codeBytes.Get(2).CopyTo(szBytes, 2);
}
else if (opcode == opcodetype.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.GetItems(4);
+ szBytes = codeBytes.Get(4);
}
}
- catch (WrappedListException)
+ catch (ByteQueueException)
{
// Unable to read operand length
return false;
try
{
// Read found number of bytes into list of OP_PUSHDATAn arguments.
- bytesRet = codeBytes.GetEnumerableItems(nSize);
+ bytesRet = codeBytes.GetEnumerable(nSize);
}
- catch (WrappedListException)
+ catch (ByteQueueException)
{
// Unable to read data
return false;
return -1;
}
-
+ /// <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<IEnumerable<byte>> solutions = new List<IEnumerable<byte>>();
if (whichType == txnouttype.TX_MULTISIG)
{
+ // Additional verification of OP_CHECKMULTISIG arguments
byte m = solutions.First().First();
byte n = solutions.Last().First();
// 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;
opcodetype opcode1, opcode2;
// Compare
- WrappedList<byte> wl1 = script1.GetWrappedList();
- WrappedList<byte> wl2 = script2.GetWrappedList();
+ ByteQueue wl1 = script1.GetByteQUeue();
+ ByteQueue wl2 = script2.GetByteQUeue();
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.GetCurrentItem() == last1 && wl2.GetCurrentItem() == last2)
+ if (wl1.GetCurrent() == last1 && wl2.GetCurrent() == last2)
{
// Found a match
typeRet = templateTuple.Item1;
return false;
}
- };
+
+ public static Hash256 SignatureHash(CScript scriptCode, 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());
+ }
+
+ 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.
+ scriptCode.RemovePattern(new byte[] { (byte)opcodetype.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 = scriptCode;
+
+ // 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<byte> b = new List<byte>();
+ b.AddRange(txTmp.Bytes);
+ b.AddRange(BitConverter.GetBytes(nHashType));
+
+ return Hash256.Compute256(b);
+ }
+
+ public class StackMachineException : Exception
+ {
+ public StackMachineException()
+ {
+ }
+
+ public StackMachineException(string message)
+ : base(message)
+ {
+ }
+
+ public StackMachineException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+ }
+
+
+ //
+ // Script is a stack machine (like Forth) that evaluates a predicate
+ // returning a bool indicating valid or not. There are no loops.
+ //
+
+ /// <summary>
+ /// Remove last element from stack
+ /// </summary>
+ /// <param name="stack">Stack reference</param>
+ static void popstack(ref List<IEnumerable<byte>> stack)
+ {
+ int nCount = stack.Count;
+ if (nCount == 0)
+ throw new StackMachineException("popstack() : stack empty");
+ stack.RemoveAt(nCount - 1);
+ }
+
+ /// <summary>
+ /// Get element at specified stack depth
+ /// </summary>
+ /// <param name="stack">Stack reference</param>
+ /// <param name="nDepth">Depth</param>
+ /// <returns>Byte sequence</returns>
+ static IEnumerable<byte> stacktop(ref List<IEnumerable<byte>> 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];
+ }
+
+ /// <summary>
+ /// Cast argument to boolean value
+ /// </summary>
+ /// <param name="value">Some byte sequence</param>
+ /// <returns></returns>
+ private static bool CastToBool(IEnumerable<byte> 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;
+ }
+
+ /// <summary>
+ /// Cast argument to integer value
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ private static BigInteger CastToBigInteger(IEnumerable<byte> value)
+ {
+ if (value.Count() > 4)
+ {
+ throw new StackMachineException("CastToBigInteger() : overflow");
+ }
+
+ return new BigInteger(value.ToArray());
+ }
+
+ static bool EvalScript(ref List<IEnumerable<byte>> stack, CScript script, CTransaction txTo, int nIn, int flags, int nHashType)
+ {
+ ByteQueue pc = script.GetByteQUeue();
+
+ ByteQueue pbegincodehash = script.GetByteQUeue();
+
+ opcodetype opcode;
+
+ List<bool> vfExec = new List<bool>();
+ List<IEnumerable<byte>> altstack = new List<IEnumerable<byte>>();
+
+ byte[] vchFalse = new byte[0];
+ byte[] vchTrue = new byte[] { 0x01 };
+
+ if (script.Bytes.Count() > 10000)
+ {
+ return false;
+ }
+
+ int nOpCount = 0;
+
+ while (true)
+ {
+ bool fExec = false;
+ foreach (bool fValue in vfExec)
+ {
+ if (!fValue)
+ {
+ fExec = true;
+ break;
+ }
+ }
+
+ //
+ // Read instruction
+ //
+ IEnumerable<byte> pushArg;
+ if (!GetOp(ref pc, out opcode, out pushArg))
+ {
+ return false;
+ }
+
+ if (pushArg.Count() > 520) // Check against MAX_SCRIPT_ELEMENT_SIZE
+ {
+ return false;
+ }
+
+ if (opcode > opcodetype.OP_16 && ++nOpCount > 201)
+ {
+ return false;
+ }
+
+ if (fExec && 0 <= opcode && opcode <= opcodetype.OP_PUSHDATA4)
+ {
+ // Push argument to stack
+ stack.Add(pushArg);
+ }
+ else if (fExec || (opcodetype.OP_IF <= opcode && opcode <= opcodetype.OP_ENDIF))
+ switch (opcode)
+ {
+ //
+ // Disabled opcodes
+ //
+ case opcodetype.OP_CAT:
+ case opcodetype.OP_SUBSTR:
+ case opcodetype.OP_LEFT:
+ case opcodetype.OP_RIGHT:
+ case opcodetype.OP_INVERT:
+ case opcodetype.OP_AND:
+ case opcodetype.OP_OR:
+ case opcodetype.OP_XOR:
+ case opcodetype.OP_2MUL:
+ case opcodetype.OP_2DIV:
+ case opcodetype.OP_MUL:
+ case opcodetype.OP_DIV:
+ case opcodetype.OP_MOD:
+ case opcodetype.OP_LSHIFT:
+ case opcodetype.OP_RSHIFT:
+ return false;
+
+ //
+ // Push value
+ //
+ case opcodetype.OP_1NEGATE:
+ case opcodetype.OP_1:
+ case opcodetype.OP_2:
+ case opcodetype.OP_3:
+ case opcodetype.OP_4:
+ case opcodetype.OP_5:
+ case opcodetype.OP_6:
+ case opcodetype.OP_7:
+ case opcodetype.OP_8:
+ case opcodetype.OP_9:
+ case opcodetype.OP_10:
+ case opcodetype.OP_11:
+ case opcodetype.OP_12:
+ case opcodetype.OP_13:
+ case opcodetype.OP_14:
+ case opcodetype.OP_15:
+ case opcodetype.OP_16:
+ {
+ // ( -- value)
+ BigInteger bn = new BigInteger((int)opcode - (int)(opcodetype.OP_1 - 1));
+ stack.Add(bn.ToByteArray());
+ }
+ break;
+
+
+ //
+ // Control
+ //
+ case opcodetype.OP_NOP:
+ case opcodetype.OP_NOP1:
+ case opcodetype.OP_NOP2:
+ case opcodetype.OP_NOP3:
+ case opcodetype.OP_NOP4:
+ case opcodetype.OP_NOP5:
+ case opcodetype.OP_NOP6:
+ case opcodetype.OP_NOP7:
+ case opcodetype.OP_NOP8:
+ case opcodetype.OP_NOP9:
+ case opcodetype.OP_NOP10:
+ break;
+
+ case opcodetype.OP_IF:
+ case opcodetype.OP_NOTIF:
+ {
+ // <expression> if [statements] [else [statements]] endif
+ bool fValue = false;
+ if (fExec)
+ {
+ if (stack.Count() < 1)
+ {
+ return false;
+ }
+ IEnumerable<byte> vch = stacktop(ref stack, -1);
+ fValue = CastToBool(vch);
+ if (opcode == opcodetype.OP_NOTIF)
+ {
+ fValue = !fValue;
+ }
+ popstack(ref stack);
+ }
+ vfExec.Add(fValue);
+ }
+ break;
+
+ case opcodetype.OP_ELSE:
+ {
+ int nExecCount = vfExec.Count();
+ if (nExecCount == 0)
+ {
+ return false;
+ }
+ vfExec[nExecCount - 1] = !vfExec[nExecCount - 1];
+ }
+ break;
+
+ case opcodetype.OP_ENDIF:
+ {
+ int nExecCount = vfExec.Count();
+ if (nExecCount == 0)
+ {
+ return false;
+ }
+ vfExec.RemoveAt(nExecCount - 1);
+ }
+ break;
+
+ case opcodetype.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 opcodetype.OP_RETURN:
+ return false;
+ //
+ // Stack ops
+ //
+ case opcodetype.OP_TOALTSTACK:
+ {
+ if (stack.Count() < 1)
+ {
+ return false;
+ }
+ altstack.Add(stacktop(ref stack, -1));
+ popstack(ref stack);
+ }
+ break;
+
+ case opcodetype.OP_FROMALTSTACK:
+ {
+ if (altstack.Count() < 1)
+ {
+ return false;
+ }
+ stack.Add(stacktop(ref stack, -1));
+ popstack(ref altstack);
+ }
+ break;
+
+ case opcodetype.OP_2DROP:
+ {
+ // (x1 x2 -- )
+ if (stack.Count() < 2)
+ {
+ return false;
+ }
+ popstack(ref stack);
+ popstack(ref stack);
+ }
+ break;
+
+ case opcodetype.OP_2DUP:
+ {
+ // (x1 x2 -- x1 x2 x1 x2)
+ if (stack.Count() < 2)
+ {
+ return false;
+ }
+ IEnumerable<byte> vch1 = stacktop(ref stack, -2);
+ IEnumerable<byte> vch2 = stacktop(ref stack, -1);
+ stack.Add(vch1);
+ stack.Add(vch2);
+ }
+ break;
+
+ case opcodetype.OP_3DUP:
+ {
+ // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
+ if (stack.Count() < 3)
+ {
+ return false;
+ }
+ IEnumerable<byte> vch1 = stacktop(ref stack, -3);
+ IEnumerable<byte> vch2 = stacktop(ref stack, -2);
+ IEnumerable<byte> vch3 = stacktop(ref stack, -1);
+ stack.Add(vch1);
+ stack.Add(vch2);
+ stack.Add(vch3);
+ }
+ break;
+
+ case opcodetype.OP_2OVER:
+ {
+ // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
+ if (stack.Count() < 4)
+ {
+ return false;
+ }
+ IEnumerable<byte> vch1 = stacktop(ref stack, -4);
+ IEnumerable<byte> vch2 = stacktop(ref stack, -3);
+ stack.Add(vch1);
+ stack.Add(vch2);
+ }
+ break;
+
+ case opcodetype.OP_2ROT:
+ {
+ int nStackDepth = stack.Count();
+ // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
+ if (nStackDepth < 6)
+ {
+ return false;
+ }
+ IEnumerable<byte> vch1 = stacktop(ref stack, -6);
+ IEnumerable<byte> vch2 = stacktop(ref stack, -5);
+ stack.RemoveRange(nStackDepth - 6, 2);
+ stack.Add(vch1);
+ stack.Add(vch2);
+ }
+ break;
+
+ case opcodetype.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 opcodetype.OP_IFDUP:
+ {
+ // (x - 0 | x x)
+ if (stack.Count() < 1)
+ {
+ return false;
+ }
+
+ IEnumerable<byte> vch = stacktop(ref stack, -1);
+
+ if (CastToBool(vch))
+ {
+ stack.Add(vch);
+ }
+ }
+ break;
+
+ case opcodetype.OP_DEPTH:
+ {
+ // -- stacksize
+ BigInteger bn = new BigInteger((ushort)stack.Count());
+ stack.Add(bn.ToByteArray());
+ }
+ break;
+
+ case opcodetype.OP_DROP:
+ {
+ // (x -- )
+ if (stack.Count() < 1)
+ {
+ return false;
+ }
+
+ popstack(ref stack);
+ }
+ break;
+
+ case opcodetype.OP_DUP:
+ {
+ // (x -- x x)
+ if (stack.Count() < 1)
+ {
+ return false;
+ }
+
+ IEnumerable<byte> vch = stacktop(ref stack, -1);
+ stack.Add(vch);
+ }
+ break;
+
+ case opcodetype.OP_NIP:
+ {
+ // (x1 x2 -- x2)
+ int nStackDepth = stack.Count();
+ if (nStackDepth < 2)
+ {
+ return false;
+ }
+
+ stack.RemoveAt(nStackDepth - 2);
+ }
+ break;
+
+ case opcodetype.OP_OVER:
+ {
+ // (x1 x2 -- x1 x2 x1)
+ if (stack.Count() < 2)
+ {
+ return false;
+ }
+
+ IEnumerable<byte> vch = stacktop(ref stack, -2);
+ stack.Add(vch);
+ }
+ break;
+
+ case opcodetype.OP_PICK:
+ case opcodetype.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<byte> vch = stacktop(ref stack, -n - 1);
+ if (opcode == opcodetype.OP_ROLL)
+ {
+ stack.RemoveAt(nStackDepth - n - 1);
+ }
+
+ stack.Add(vch);
+ }
+ break;
+
+ case opcodetype.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 opcodetype.OP_SWAP:
+ {
+ // (x1 x2 -- x2 x1)
+ int nStackDepth = stack.Count();
+ if (nStackDepth < 2)
+ {
+ return false;
+ }
+ stack.Swap(nStackDepth - 2, nStackDepth - 1);
+ }
+ break;
+
+ case opcodetype.OP_TUCK:
+ {
+ // (x1 x2 -- x2 x1 x2)
+ int nStackDepth = stack.Count();
+ if (nStackDepth < 2)
+ {
+ return false;
+ }
+ IEnumerable<byte> vch = stacktop(ref stack, -1);
+ stack.Insert(nStackDepth - 2, vch);
+ }
+ break;
+
+
+ case opcodetype.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 opcodetype.OP_EQUAL:
+ case opcodetype.OP_EQUALVERIFY:
+ //case opcodetype.OP_NOTEQUAL: // use OP_NUMNOTEQUAL
+ {
+ // (x1 x2 - bool)
+ if (stack.Count() < 2)
+ {
+ return false;
+ }
+
+ IEnumerable<byte> vch1 = stacktop(ref stack, -2);
+ IEnumerable<byte> vch2 = stacktop(ref stack, -1);
+ bool fEqual = (vch1 == 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 == opcodetype.OP_NOTEQUAL)
+ // fEqual = !fEqual;
+ popstack(ref stack);
+ popstack(ref stack);
+ stack.Add(fEqual ? vchTrue : vchFalse);
+
+ if (opcode == opcodetype.OP_EQUALVERIFY)
+ {
+ if (fEqual)
+ {
+ popstack(ref stack);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ break;
+
+
+ //
+ // Numeric
+ //
+ case opcodetype.OP_1ADD:
+ case opcodetype.OP_1SUB:
+ case opcodetype.OP_NEGATE:
+ case opcodetype.OP_ABS:
+ case opcodetype.OP_NOT:
+ case opcodetype.OP_0NOTEQUAL:
+ {
+ // (in -- out)
+ if (stack.Count() < 1)
+ {
+ return false;
+ }
+
+ BigInteger bn = CastToBigInteger(stacktop(ref stack, -1));
+ switch (opcode)
+ {
+ case opcodetype.OP_1ADD:
+ bn = bn + 1;
+ break;
+ case opcodetype.OP_1SUB:
+ bn = bn - 1;
+ break;
+ case opcodetype.OP_NEGATE:
+ bn = -bn;
+ break;
+ case opcodetype.OP_ABS:
+ bn = BigInteger.Abs(bn);
+ break;
+ case opcodetype.OP_NOT:
+ bn = bn == 0 ? 1 : 0;
+ break;
+ case opcodetype.OP_0NOTEQUAL:
+ bn = bn != 0 ? 1 : 0;
+ break;
+
+ default:
+ throw new StackMachineException("invalid opcode");
+ break;
+ }
+
+ popstack(ref stack);
+ stack.Add(bn.ToByteArray());
+ }
+ break;
+
+ case opcodetype.OP_ADD:
+ case opcodetype.OP_SUB:
+ case opcodetype.OP_BOOLAND:
+ case opcodetype.OP_BOOLOR:
+ case opcodetype.OP_NUMEQUAL:
+ case opcodetype.OP_NUMEQUALVERIFY:
+ case opcodetype.OP_NUMNOTEQUAL:
+ case opcodetype.OP_LESSTHAN:
+ case opcodetype.OP_GREATERTHAN:
+ case opcodetype.OP_LESSTHANOREQUAL:
+ case opcodetype.OP_GREATERTHANOREQUAL:
+ case opcodetype.OP_MIN:
+ case opcodetype.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 opcodetype.OP_ADD:
+ bn = bn1 + bn2;
+ break;
+ case opcodetype.OP_SUB:
+ bn = bn1 - bn2;
+ break;
+ case opcodetype.OP_BOOLAND:
+ bn = (bn1 != 0 && bn2 != 0) ? 1 : 0;
+ break;
+ case opcodetype.OP_BOOLOR:
+ bn = (bn1 != 0 || bn2 != 0) ? 1 : 0;
+ break;
+ case opcodetype.OP_NUMEQUAL:
+ bn = (bn1 == bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_NUMEQUALVERIFY:
+ bn = (bn1 == bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_NUMNOTEQUAL:
+ bn = (bn1 != bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_LESSTHAN:
+ bn = (bn1 < bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_GREATERTHAN:
+ bn = (bn1 > bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_LESSTHANOREQUAL:
+ bn = (bn1 <= bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_GREATERTHANOREQUAL:
+ bn = (bn1 >= bn2) ? 1 : 0;
+ break;
+ case opcodetype.OP_MIN:
+ bn = (bn1 < bn2 ? bn1 : bn2);
+ break;
+ case opcodetype.OP_MAX:
+ bn = (bn1 > bn2 ? bn1 : bn2);
+ break;
+
+ default:
+ throw new StackMachineException("invalid opcode");
+ break;
+ }
+
+ popstack(ref stack);
+ popstack(ref stack);
+ stack.Add(bn.ToByteArray());
+
+ if (opcode == opcodetype.OP_NUMEQUALVERIFY)
+ {
+ if (CastToBool(stacktop(ref stack, -1)))
+ {
+ popstack(ref stack);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ break;
+
+ case opcodetype.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 ? vchTrue : vchFalse);
+ }
+ break;
+
+
+ //
+ // Crypto
+ //
+ case opcodetype.OP_RIPEMD160:
+ case opcodetype.OP_SHA1:
+ case opcodetype.OP_SHA256:
+ case opcodetype.OP_HASH160:
+ case opcodetype.OP_HASH256:
+ {
+ // (in -- hash)
+ if (stack.Count() < 1)
+ return false;
+ IEnumerable<byte> vch = stacktop(ref stack, -1);
+ IEnumerable<byte> vchHash = null;
+ if (opcode == opcodetype.OP_RIPEMD160)
+ {
+ RIPEMD160 hash = RIPEMD160.Compute160(vch);
+ vchHash = hash.hashBytes;
+ }
+ else if (opcode == opcodetype.OP_SHA1)
+ {
+ SHA1 hash = SHA1.Compute1(vch);
+ vchHash = hash.hashBytes;
+ }
+ else if (opcode == opcodetype.OP_SHA256)
+ {
+ SHA256 hash = SHA256.Compute256(vch);
+ vchHash = hash.hashBytes;
+ }
+ else if (opcode == opcodetype.OP_HASH160)
+ {
+ Hash160 hash = Hash160.Compute160(vch);
+ vchHash = hash.hashBytes;
+ }
+ else if (opcode == opcodetype.OP_HASH256)
+ {
+ Hash256 hash = Hash256.Compute256(vch);
+ vchHash = hash.hashBytes;
+ }
+ popstack(ref stack);
+ stack.Add(vchHash);
+ }
+ break;
+
+ case opcodetype.OP_CODESEPARATOR:
+ {
+ // Hash starts after the code separator
+ pbegincodehash = pc;
+ }
+ break;
+
+ case opcodetype.OP_CHECKSIG:
+ case opcodetype.OP_CHECKSIGVERIFY:
+ {
+ // (sig pubkey -- bool)
+ if (stack.Count() < 2)
+ {
+ return false;
+ }
+
+ IList<byte> sigBytes = stacktop(ref stack, -2).ToList();
+ IList<byte> pubkeyBytes = stacktop(ref stack, -1).ToList();
+
+ // Subset of script starting at the most recent codeseparator
+ CScript scriptCode = new CScript(script.Bytes.Skip(pbegincodehash.CurrentIndex));
+
+ // 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 ? vchTrue : vchFalse);
+ if (opcode == opcodetype.OP_CHECKSIGVERIFY)
+ {
+ if (fSuccess)
+ {
+ popstack(ref stack);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ break;
+
+ case opcodetype.OP_CHECKMULTISIG:
+ case opcodetype.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(pbegincodehash.CurrentIndex));
+
+ // 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<byte> vchSig = stacktop(ref stack, -isig - k);
+ scriptCode.RemovePattern(vchSig.ToList());
+ }
+
+ bool fSuccess = true;
+ while (fSuccess && nSigsCount > 0)
+ {
+ IList<byte> sigBytes = stacktop(ref stack, -isig).ToList();
+ IList<byte> 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 ? vchTrue : vchFalse);
+
+ if (opcode == opcodetype.OP_CHECKMULTISIGVERIFY)
+ {
+ if (fSuccess)
+ {
+ popstack(ref stack);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ // Size limits
+ if (stack.Count() + altstack.Count() > 1000)
+ {
+ return false;
+ }
+ }
+
+
+ if (vfExec.Count() == 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ static bool IsCanonicalPubKey(IList<byte> 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;
+ }
+
+ static bool IsCanonicalSignature(IList<byte> sigBytes, int flags)
+ {
+ // STUB
+
+ return true;
+ }
+
+
+ static bool CheckSig(IList<byte> sigBytes, IList<byte> pubKeyBytes, CScript scriptCode, CTransaction txTo, int nIn, int nHashType, int flags)
+ {
+ // STUB
+ return true;
+ }
+
+};
}