X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCScript.cs;h=95ca17a88e5dc7898e8d896170bf94cb10b74908;hb=0fe762d6eee8a8a23033f813217c1675a34f2d6a;hp=70cf412dd3b690b54b22708b89f3c2db12b894af;hpb=8b06904374c83d9e4216bcd202913c5e5ce10442;p=NovacoinLibrary.git diff --git a/Novacoin/CScript.cs b/Novacoin/CScript.cs index 70cf412..95ca17a 100644 --- a/Novacoin/CScript.cs +++ b/Novacoin/CScript.cs @@ -1,429 +1,547 @@ -using System; +/** + * Novacoin classes library + * Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com) + + * 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.Linq; using System.Text; +using System.Collections.Generic; +using System.Diagnostics.Contracts; namespace Novacoin { - /** Script opcodes */ - enum opcodetype - { - // 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, - }; - + /// + /// Representation of script code + /// public class CScript { - private byte[] scriptCode = {}; + private List codeBytes; + /// + /// Initializes an empty instance of CScript + /// public CScript () { + codeBytes = new List(); } - private string GetOpName(opcodetype opcode) - { - switch (opcode) { - // push value - case opcodetype.OP_0: - return "0"; - case opcodetype.OP_PUSHDATA1: - return "OP_PUSHDATA1"; - case opcodetype.OP_PUSHDATA2: - return "OP_PUSHDATA2"; - case opcodetype.OP_PUSHDATA4: - return "OP_PUSHDATA4"; - case opcodetype.OP_1NEGATE: - return "-1"; - case opcodetype.OP_RESERVED: - return "OP_RESERVED"; - case opcodetype.OP_1: - return "1"; - case opcodetype.OP_2: - return "2"; - case opcodetype.OP_3: - return "3"; - case opcodetype.OP_4: - return "4"; - case opcodetype.OP_5: - return "5"; - case opcodetype.OP_6: - return "6"; - case opcodetype.OP_7: - return "7"; - case opcodetype.OP_8: - return "8"; - case opcodetype.OP_9: - return "9"; - case opcodetype.OP_10: - return "10"; - case opcodetype.OP_11: - return "11"; - case opcodetype.OP_12: - return "12"; - case opcodetype.OP_13: - return "13"; - case opcodetype.OP_14: - return "14"; - case opcodetype.OP_15: - return "15"; - case opcodetype.OP_16: - return "16"; - - // control - case opcodetype.OP_NOP: - return "OP_NOP"; - case opcodetype.OP_VER: - return "OP_VER"; - case opcodetype.OP_IF: - return "OP_IF"; - case opcodetype.OP_NOTIF: - return "OP_NOTIF"; - case opcodetype.OP_VERIF: - return "OP_VERIF"; - case opcodetype.OP_VERNOTIF: - return "OP_VERNOTIF"; - case opcodetype.OP_ELSE: - return "OP_ELSE"; - case opcodetype.OP_ENDIF: - return "OP_ENDIF"; - case opcodetype.OP_VERIFY: - return "OP_VERIFY"; - case opcodetype.OP_RETURN: - return "OP_RETURN"; - - // stack ops - case opcodetype.OP_TOALTSTACK: - return "OP_TOALTSTACK"; - case opcodetype.OP_FROMALTSTACK: - return "OP_FROMALTSTACK"; - case opcodetype.OP_2DROP: - return "OP_2DROP"; - case opcodetype.OP_2DUP: - return "OP_2DUP"; - case opcodetype.OP_3DUP: - return "OP_3DUP"; - case opcodetype.OP_2OVER: - return "OP_2OVER"; - case opcodetype.OP_2ROT: - return "OP_2ROT"; - case opcodetype.OP_2SWAP: - return "OP_2SWAP"; - case opcodetype.OP_IFDUP: - return "OP_IFDUP"; - case opcodetype.OP_DEPTH: - return "OP_DEPTH"; - case opcodetype.OP_DROP: - return "OP_DROP"; - case opcodetype.OP_DUP: - return "OP_DUP"; - case opcodetype.OP_NIP: - return "OP_NIP"; - case opcodetype.OP_OVER: - return "OP_OVER"; - case opcodetype.OP_PICK: - return "OP_PICK"; - case opcodetype.OP_ROLL: - return "OP_ROLL"; - case opcodetype.OP_ROT: - return "OP_ROT"; - case opcodetype.OP_SWAP: - return "OP_SWAP"; - case opcodetype.OP_TUCK: - return "OP_TUCK"; - - // splice ops - case opcodetype.OP_CAT: - return "OP_CAT"; - case opcodetype.OP_SUBSTR: - return "OP_SUBSTR"; - case opcodetype.OP_LEFT: - return "OP_LEFT"; - case opcodetype.OP_RIGHT: - return "OP_RIGHT"; - case opcodetype.OP_SIZE: - return "OP_SIZE"; - - // bit logic - case opcodetype.OP_INVERT: - return "OP_INVERT"; - case opcodetype.OP_AND: - return "OP_AND"; - case opcodetype.OP_OR: - return "OP_OR"; - case opcodetype.OP_XOR: - return "OP_XOR"; - case opcodetype.OP_EQUAL: - return "OP_EQUAL"; - case opcodetype.OP_EQUALVERIFY: - return "OP_EQUALVERIFY"; - case opcodetype.OP_RESERVED1: - return "OP_RESERVED1"; - case opcodetype.OP_RESERVED2: - return "OP_RESERVED2"; - - // numeric - case opcodetype.OP_1ADD: - return "OP_1ADD"; - case opcodetype.OP_1SUB: - return "OP_1SUB"; - case opcodetype.OP_2MUL: - return "OP_2MUL"; - case opcodetype.OP_2DIV: - return "OP_2DIV"; - case opcodetype.OP_NEGATE: - return "OP_NEGATE"; - case opcodetype.OP_ABS: - return "OP_ABS"; - case opcodetype.OP_NOT: - return "OP_NOT"; - case opcodetype.OP_0NOTEQUAL: - return "OP_0NOTEQUAL"; - case opcodetype.OP_ADD: - return "OP_ADD"; - case opcodetype.OP_SUB: - return "OP_SUB"; - case opcodetype.OP_MUL: - return "OP_MUL"; - case opcodetype.OP_DIV: - return "OP_DIV"; - case opcodetype.OP_MOD: - return "OP_MOD"; - case opcodetype.OP_LSHIFT: - return "OP_LSHIFT"; - case opcodetype.OP_RSHIFT: - return "OP_RSHIFT"; - case opcodetype.OP_BOOLAND: - return "OP_BOOLAND"; - case opcodetype.OP_BOOLOR: - return "OP_BOOLOR"; - case opcodetype.OP_NUMEQUAL: - return "OP_NUMEQUAL"; - case opcodetype.OP_NUMEQUALVERIFY: - return "OP_NUMEQUALVERIFY"; - case opcodetype.OP_NUMNOTEQUAL: - return "OP_NUMNOTEQUAL"; - case opcodetype.OP_LESSTHAN: - return "OP_LESSTHAN"; - case opcodetype.OP_GREATERTHAN: - return "OP_GREATERTHAN"; - case opcodetype.OP_LESSTHANOREQUAL: - return "OP_LESSTHANOREQUAL"; - case opcodetype.OP_GREATERTHANOREQUAL: - return "OP_GREATERTHANOREQUAL"; - case opcodetype.OP_MIN: - return "OP_MIN"; - case opcodetype.OP_MAX: - return "OP_MAX"; - case opcodetype.OP_WITHIN: - return "OP_WITHIN"; - - // crypto - case opcodetype.OP_RIPEMD160: - return "OP_RIPEMD160"; - case opcodetype.OP_SHA1: - return "OP_SHA1"; - case opcodetype.OP_SHA256: - return "OP_SHA256"; - case opcodetype.OP_HASH160: - return "OP_HASH160"; - case opcodetype.OP_HASH256: - return "OP_HASH256"; - case opcodetype.OP_CODESEPARATOR: - return "OP_CODESEPARATOR"; - case opcodetype.OP_CHECKSIG: - return "OP_CHECKSIG"; - case opcodetype.OP_CHECKSIGVERIFY: - return "OP_CHECKSIGVERIFY"; - case opcodetype.OP_CHECKMULTISIG: - return "OP_CHECKMULTISIG"; - case opcodetype.OP_CHECKMULTISIGVERIFY: - return "OP_CHECKMULTISIGVERIFY"; - - // expanson - case opcodetype.OP_NOP1: - return "OP_NOP1"; - case opcodetype.OP_NOP2: - return "OP_NOP2"; - case opcodetype.OP_NOP3: - return "OP_NOP3"; - case opcodetype.OP_NOP4: - return "OP_NOP4"; - case opcodetype.OP_NOP5: - return "OP_NOP5"; - case opcodetype.OP_NOP6: - return "OP_NOP6"; - case opcodetype.OP_NOP7: - return "OP_NOP7"; - case opcodetype.OP_NOP8: - return "OP_NOP8"; - case opcodetype.OP_NOP9: - return "OP_NOP9"; - case opcodetype.OP_NOP10: - return "OP_NOP10"; - - // template matching params - case opcodetype.OP_PUBKEYHASH: - return "OP_PUBKEYHASH"; - case opcodetype.OP_PUBKEY: - return "OP_PUBKEY"; - case opcodetype.OP_SMALLDATA: - return "OP_SMALLDATA"; - - case opcodetype.OP_INVALIDOPCODE: - return "OP_INVALIDOPCODE"; - default: - return "OP_UNKNOWN"; - } - } + /// + /// Initializes new instance of CScript and fills it with supplied bytes + /// + /// Enumerator interface for byte sequence + public CScript(byte[] bytes) + { + codeBytes = new List(bytes); + } + + /// + /// Return a new instance of ByteQueue object for current code bytes + /// + /// + public InstructionQueue GetInstructionQueue() + { + return new InstructionQueue(ref codeBytes); + } + + /// + /// Adds specified operation to instruction list + /// + /// + public void AddInstruction(instruction opcode) + { + Contract.Requires(opcode >= instruction.OP_0 && opcode <= instruction.OP_INVALIDOPCODE, "Invalid instruction."); + + codeBytes.Add((byte)opcode); + } + + /// + /// Adds hash to instruction list. + /// New items are added in this format: + /// hash_length_byte hash_bytes + /// + /// uint160 instance + public void AddHash(uint160 hash) + { + codeBytes.Add((byte)hash.Size); + codeBytes.AddRange((byte[])hash); + } + + /// + /// Adds hash to instruction list. + /// New items are added in this format: + /// hash_length_byte hash_bytes + /// + /// uint256 instance + public void AddHash(uint256 hash) + { + codeBytes.Add((byte)hash.Size); + codeBytes.AddRange((byte[])hash); + } + + /// + /// Create new OP_PUSHDATAn operator and add it to instruction list + /// + /// Set of data bytes + public void PushData(byte[] dataBytes) + { + var nCount = dataBytes.LongLength; + + if (nCount < (int)instruction.OP_PUSHDATA1) + { + // OP_0 and OP_FALSE + codeBytes.Add((byte)nCount); + } + else if (nCount < 0xff) + { + // OP_PUSHDATA1 0x01 [0x5a] + codeBytes.Add((byte)instruction.OP_PUSHDATA1); + codeBytes.Add((byte)nCount); + } + else if (nCount < 0xffff) + { + // OP_PUSHDATA1 0x01 0x00 [0x5a] + codeBytes.Add((byte)instruction.OP_PUSHDATA2); + + var szBytes = BitConverter.GetBytes((ushort)nCount); + codeBytes.AddRange(szBytes); + } + else if (nCount < 0xffffffff) + { + // OP_PUSHDATA1 0x01 0x00 0x00 0x00 [0x5a] + codeBytes.Add((byte)instruction.OP_PUSHDATA4); + + var szBytes = BitConverter.GetBytes((uint)nCount); + codeBytes.AddRange(szBytes); + } + + // Add data bytes + codeBytes.AddRange(dataBytes); + } + + /// + /// Just insert data array without including any prefixes. Please make sure that you know what you're doing, + /// it is recommended to use AddInstruction, AddHash or PushData instead. + /// + /// Data bytes + public void AddRawData(byte[] dataBytes) + { + // Add data bytes + codeBytes.AddRange(dataBytes); + } + + /// + /// Scan pushed data bytes for pattern and, in case of exact match, remove it. + /// + /// Pattern sequence + /// Matches count + public int RemovePattern(byte[] pattern) + { + // There is no sense to continue if pattern is empty or longer than script itself + if (pattern.Length == 0 || pattern.Length > codeBytes.Count) + { + return 0; + } + + var count = 0; + var bq1 = new InstructionQueue(ref codeBytes); + + byte[] pushData; + instruction opcode; + + var newScript = new CScript(); + + while (ScriptCode.GetOp(ref bq1, out opcode, out pushData)) + { + if (pushData.Length == 0) + { + // No data, put instruction on its place + newScript.AddInstruction(opcode); + } + else if (!pushData.SequenceEqual(pattern)) + { + // No match, create push operator + newScript.PushData(pushData); + } + else + { + count++; // match + } + } + + if (count > 0) + { + // Replace current script if any matches were found + codeBytes = newScript.codeBytes; + } + + return count; + } + + /// + /// Scan script for specific instruction and remove it if there are some matches. + /// + /// Instruction + /// Matches count + public int RemoveInstruction(instruction op) + { + byte[] pushData; + instruction opcode; + + var count = 0; + var newScript = new CScript(); + var bq1 = new InstructionQueue(ref codeBytes); + + while (ScriptCode.GetOp(ref bq1, out opcode, out pushData)) + { + if (pushData.Length != 0 && op != opcode) + { + // If instruction didn't match then push its data again + newScript.PushData(pushData); + } + else if (Enum.IsDefined(typeof(instruction), op) && op != opcode) + { + // Instruction didn't match + newScript.AddInstruction(opcode); + } + else + { + count++; // match + } + } + + if (count > 0) + { + // Replace current script if any matches were found + codeBytes = newScript.codeBytes; + } + + return count; + } + + /// + /// Is it true that script doesn't contain anything except push value operations? + /// + public bool IsPushOnly + { + get + { + var wCodeBytes = new InstructionQueue(ref codeBytes); + + instruction opcode; // Current instruction + byte[] pushArgs; // OP_PUSHDATAn argument + + // Scan instructions sequence + while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) + { + if (opcode > instruction.OP_16) + { + // We don't allow control instructions here + return false; + } + } + + return true; + } + } + + /// + /// Is it true that script doesn't contain non-canonical push operations? + /// + public bool HasOnlyCanonicalPushes + { + get + { + var wCodeBytes = new InstructionQueue(ref codeBytes); + byte[] pushArgs; // OP_PUSHDATAn argument + instruction opcode; // Current instruction + + // Scan instructions sequence + while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) + { + var data = pushArgs; + + if (opcode < instruction.OP_PUSHDATA1 && opcode > instruction.OP_0 && (data.Length == 1 && data[0] <= 16)) + { + // Could have used an OP_n code, rather than a 1-byte push. + return false; + } + if (opcode == instruction.OP_PUSHDATA1 && data.Length < (int)instruction.OP_PUSHDATA1) + { + // Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return false; + } + if (opcode == instruction.OP_PUSHDATA2 && data.Length <= 0xFF) + { + // Could have used an OP_PUSHDATA1. + return false; + } + if (opcode == instruction.OP_PUSHDATA4 && data.LongLength <= 0xFFFF) + { + // Could have used an OP_PUSHDATA2. + return false; + } + } + + return true; + } + } + + /// + /// Quick test for pay-to-script-hash CScripts + /// + public bool IsPayToScriptHash + { + get + { + // Sender provides redeem script hash, receiver provides signature list and redeem script + // OP_HASH160 20 [20 byte hash] OP_EQUAL + return (codeBytes.Count() == 23 && + codeBytes[0] == (byte)instruction.OP_HASH160 && + codeBytes[1] == 0x14 && // 20 bytes hash length prefix + codeBytes[22] == (byte)instruction.OP_EQUAL); + } + } + + /// + /// Quick test for pay-to-pubkeyhash CScripts + /// + public bool IsPayToPubKeyHash + { + get + { + // Sender provides hash of pubkey, receiver provides signature and pubkey + // OP_DUP OP_HASH160 20 [20 byte hash] OP_EQUALVERIFY OP_CHECKSIG + return (codeBytes.Count == 25 && + codeBytes[0] == (byte)instruction.OP_DUP && + codeBytes[1] == (byte)instruction.OP_HASH160 && + codeBytes[2] == 0x14 && // 20 bytes hash length prefix + codeBytes[23] == (byte)instruction.OP_EQUALVERIFY && + codeBytes[24] == (byte)instruction.OP_CHECKSIG); + } + } + + /// + /// Quick test for Null destination + /// + public bool IsNull + { + get { return codeBytes.Count == 0; } + } + + /// + /// Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs + /// as 20 sigops. With pay-to-script-hash, that changed: + /// CHECKMULTISIGs serialized in scriptSigs are + /// counted more accurately, assuming they are of the form + /// ... OP_N CHECKMULTISIG ... + /// + /// Legacy mode flag + /// Amount of sigops + public uint GetSigOpCount(bool fAccurate) + { + var wCodeBytes = new InstructionQueue(ref codeBytes); + + instruction opcode; // Current instruction + byte[] pushArgs; // OP_PUSHDATAn argument + + uint nCount = 0; + var lastOpcode = instruction.OP_INVALIDOPCODE; + + // Scan instructions sequence + while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) + { + if (opcode == instruction.OP_CHECKSIG || opcode == instruction.OP_CHECKSIGVERIFY) + { + nCount++; + } + else if (opcode == instruction.OP_CHECKMULTISIG || opcode == instruction.OP_CHECKMULTISIGVERIFY) + { + if (fAccurate && lastOpcode >= instruction.OP_1 && lastOpcode <= instruction.OP_16) + { + nCount += (uint)ScriptCode.DecodeOP_N(lastOpcode); + } + else + { + nCount += 20; + } + } + } + + return nCount; + } + + /// + /// Accurately count sigOps, including sigOps in + /// pay-to-script-hash transactions + /// + /// pay-to-script-hash scriptPubKey + /// SigOps count + public uint GetSigOpCount(CScript scriptSig) + { + if (!IsPayToScriptHash) + { + return GetSigOpCount(true); + } + + // This is a pay-to-script-hash scriptPubKey; + // get the last item that the scriptSig + // pushes onto the stack: + InstructionQueue wScriptSig = scriptSig.GetInstructionQueue(); + int nScriptSigSize = scriptSig.Size; + + instruction opcode; // Current instruction + byte[] pushArgs = new byte[0]; // OP_PUSHDATAn argument + + + while (wScriptSig.Index < nScriptSigSize) + { + if (!ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs)) + { + return 0; + } + + if (opcode > instruction.OP_16) + { + return 0; + } + } + + /// ... and return its opcount: + var subScript = new CScript(pushArgs); + + return subScript.GetSigOpCount(true); + + } + + /// + /// Set pay-to-pubkey destination. + /// + /// Instance of CPubKey. + public void SetDestination(CPubKey pubKey) + { + codeBytes.Clear(); + PushData(pubKey); + AddInstruction(instruction.OP_CHECKSIG); + } + + /// + /// Set pay-to-pubkeyhash destination + /// + /// Public key hash + public void SetDestination(CKeyID ID) + { + codeBytes.Clear(); + AddInstruction(instruction.OP_DUP); + AddInstruction(instruction.OP_HASH160); + AddHash(ID); + AddInstruction(instruction.OP_EQUALVERIFY); + AddInstruction(instruction.OP_CHECKSIG); + } + + /// + /// Set pay-to-scripthash destination + /// + /// Script hash + public void SetDestination(CScriptID ID) + { + codeBytes.Clear(); + AddInstruction(instruction.OP_HASH160); + AddHash(ID); + AddInstruction(instruction.OP_EQUAL); + } + + /// + /// Reset script code buffer. + /// + public void SetNullDestination() + { + codeBytes.Clear(); + } + + /// + /// Set multisig destination. + /// + /// Amount of required signatures. + /// Set of public keys. + public void SetMultiSig(int nRequired, CPubKey[] keys) + { + codeBytes.Clear(); + AddInstruction(ScriptCode.EncodeOP_N(nRequired)); + + foreach (var key in keys) + { + PushData(key); + } + + AddInstruction(ScriptCode.EncodeOP_N(keys.Length)); + AddInstruction(instruction.OP_CHECKMULTISIG); + } + + /// + /// Access to script code. + /// + public static implicit operator byte[] (CScript script) + { + return script.codeBytes.ToArray(); + } + + /// + /// Script size + /// + public int Size + { + get { return codeBytes.Count; } + } + + public CScriptID ScriptID + { + get { return new CScriptID(Hash160.Compute160(codeBytes.ToArray())); } + } + + /// + /// Disassemble current script code + /// + /// Code listing public override string ToString() { - // TODO: disassembly - - StringBuilder sb = new StringBuilder(scriptCode.Length * 2); - foreach (byte b in scriptCode) - { - sb.AppendFormat ("{0:x2}", b); - } - return sb.ToString(); + var sb = new StringBuilder(); + var wCodeBytes = new InstructionQueue(ref codeBytes); + + instruction opcode; // Current instruction + byte[] pushArgs; // OP_PUSHDATAn argument + while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) + { + if (sb.Length != 0) + { + sb.Append(" "); + } + + if (0 <= opcode && opcode <= instruction.OP_PUSHDATA4) + { + sb.Append(ScriptCode.ValueString(pushArgs)); + } + else + { + sb.Append(ScriptCode.GetOpName(opcode)); + } + } + + return sb.ToString(); } } } -