using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Numerics;
namespace Novacoin
{
TX_NULL_DATA,
};
+ /// <summary>
+ /// Signature hash types/flags
+ /// </summary>
+ public enum sigflag
+ {
+ SIGHASH_ALL = 1,
+ SIGHASH_NONE = 2,
+ SIGHASH_SINGLE = 3,
+ SIGHASH_ANYONECANPAY = 0x80,
+ };
+
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)
{
{
// 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)
try
{
// Read found number of bytes into list of OP_PUSHDATAn arguments.
- bytesRet = codeBytes.GetEnumerableItems(nSize);
+ bytesRet = codeBytes.GetEnumerable(nSize);
}
catch (WrappedListException)
{
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();
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;
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.GetWrappedList();
+ ByteQueue wl2 = script2.GetWrappedList();
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})", nDepth);
+
+ throw new StackMachineException(sb.ToString());
+ }
+
+ if (nStackElement < 0)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendFormat("stacktop() : nDepth={0} exceeds real stack depth", nDepth);
+
+ 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("CastToBigNum() : overflow");
+ }
+
+ return new BigInteger(value.ToArray());
+ }
};
}