X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCScript.cs;h=229b60b8284554c5b370eefb2f3bc15fbf650eb1;hb=be9d844557911f95165d2c9875c4f5b2822cfc92;hp=9e65e380eb2c21e2040aacf32449d9f1c576c695;hpb=4426ee1dc8ae6733d46b5413d3bce28333792d22;p=NovacoinLibrary.git diff --git a/Novacoin/CScript.cs b/Novacoin/CScript.cs index 9e65e38..229b60b 100644 --- a/Novacoin/CScript.cs +++ b/Novacoin/CScript.cs @@ -20,26 +20,10 @@ using System; using System.Linq; using System.Text; using System.Collections.Generic; +using System.Diagnostics.Contracts; namespace Novacoin { - public class CScriptException : Exception - { - public CScriptException() - { - } - - public CScriptException(string message) - : base(message) - { - } - - public CScriptException(string message, Exception inner) - : base(message, inner) - { - } - } - /// /// Representation of script code /// @@ -59,7 +43,7 @@ namespace Novacoin /// Initializes new instance of CScript and fills it with supplied bytes /// /// Enumerator interface for byte sequence - public CScript(IEnumerable bytes) + public CScript(byte[] bytes) { codeBytes = new List(bytes); } @@ -68,27 +52,24 @@ namespace Novacoin /// Return a new instance of ByteQueue object for current code bytes /// /// - public ByteQueue GetByteQUeue() + public ByteQueue GetByteQueue() { - return new ByteQueue(codeBytes); + return new ByteQueue(ref codeBytes); } /// - /// Adds specified operation to opcode bytes list + /// Adds specified operation to instruction list /// /// - public void AddOp(instruction opcode) + public void AddInstruction(instruction opcode) { - if (opcode < instruction.OP_0 || opcode > instruction.OP_INVALIDOPCODE) - { - throw new CScriptException("CScript::AddOp() : invalid opcode"); - } + Contract.Requires(opcode >= instruction.OP_0 && opcode <= instruction.OP_INVALIDOPCODE, "Invalid instruction."); codeBytes.Add((byte)opcode); } /// - /// Adds hash to opcode bytes list. + /// Adds hash to instruction list. /// New items are added in this format: /// hash_length_byte hash_bytes /// @@ -96,11 +77,11 @@ namespace Novacoin public void AddHash(Hash160 hash) { codeBytes.Add((byte)hash.hashSize); - codeBytes.AddRange(hash.hashBytes); + codeBytes.AddRange((byte[])hash); } /// - /// Adds hash to opcode bytes list. + /// Adds hash to instruction list. /// New items are added in this format: /// hash_length_byte hash_bytes /// @@ -108,16 +89,16 @@ namespace Novacoin public void AddHash(Hash256 hash) { codeBytes.Add((byte)hash.hashSize); - codeBytes.AddRange(hash.hashBytes); + codeBytes.AddRange((byte[])hash); } /// - /// Create new OP_PUSHDATAn operator and add it to opcode bytes list + /// Create new OP_PUSHDATAn operator and add it to instruction list /// /// Set of data bytes - public void PushData(IEnumerable dataBytes) + public void PushData(byte[] dataBytes) { - long nCount = dataBytes.LongCount(); + var nCount = dataBytes.LongLength; if (nCount < (int)instruction.OP_PUSHDATA1) { @@ -132,18 +113,18 @@ namespace Novacoin } else if (nCount < 0xffff) { - // OP_PUSHDATA1 0x00 0x01 [0x5a] + // OP_PUSHDATA1 0x01 0x00 [0x5a] codeBytes.Add((byte)instruction.OP_PUSHDATA2); - byte[] szBytes = Interop.BEBytes((ushort)nCount); + var szBytes = BitConverter.GetBytes((ushort)nCount); codeBytes.AddRange(szBytes); } else if (nCount < 0xffffffff) { - // OP_PUSHDATA1 0x00 0x00 0x00 0x01 [0x5a] + // OP_PUSHDATA1 0x01 0x00 0x00 0x00 [0x5a] codeBytes.Add((byte)instruction.OP_PUSHDATA4); - byte[] szBytes = Interop.BEBytes((uint)nCount); + var szBytes = BitConverter.GetBytes((uint)nCount); codeBytes.AddRange(szBytes); } @@ -152,40 +133,102 @@ namespace Novacoin } /// - /// Scan code bytes for pattern + /// 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 enumerator - private IEnumerable FindPattern(IList pattern) + /// Matches count + public int RemovePattern(byte[] pattern) { - for (int i = 0; i < codeBytes.Count; i++) + // 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 ByteQueue(ref codeBytes); + + byte[] pushData; + instruction opcode; + + var newScript = new CScript(); + + while (ScriptCode.GetOp(ref bq1, out opcode, out pushData)) { - if (codeBytes.Skip(i).Take(pattern.Count).SequenceEqual(pattern)) + 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 { - yield return i; + count++; // match } } + + if (count > 0) + { + // Replace current script if any matches were found + codeBytes = newScript.codeBytes; + } + + return count; } /// - /// Scan code bytes for pattern and remove it + /// Scan script for specific instruction and remove it if there are some matches. /// - /// Pattern sequence - /// Matches number - public int RemovePattern(IList pattern) + /// Instruction + /// Matches count + public int RemoveInstruction(instruction op) { - List resultBytes = new List(codeBytes); - int count = 0; - int patternLen = pattern.Count; - - foreach (int i in FindPattern(pattern)) + byte[] pushData; + instruction opcode; + + var count = 0; + var newScript = new CScript(); + var bq1 = new ByteQueue(ref codeBytes); + + while (ScriptCode.GetOp(ref bq1, out opcode, out pushData)) { - resultBytes.RemoveRange(i - count * patternLen, patternLen); - count++; + 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; } - codeBytes = resultBytes; - return count; } @@ -196,17 +239,17 @@ namespace Novacoin { get { - ByteQueue wCodeBytes = new ByteQueue(codeBytes); + var wCodeBytes = new ByteQueue(ref codeBytes); - instruction opcode; // Current opcode - IEnumerable pushArgs; // OP_PUSHDATAn argument + instruction opcode; // Current instruction + byte[] pushArgs; // OP_PUSHDATAn argument - // Scan opcodes sequence + // Scan instructions sequence while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) { if (opcode > instruction.OP_16) { - // We don't allow control opcodes here + // We don't allow control instructions here return false; } } @@ -222,15 +265,15 @@ namespace Novacoin { get { - ByteQueue wCodeBytes = new ByteQueue(codeBytes); + var wCodeBytes = new ByteQueue(ref codeBytes); - instruction opcode; // Current opcode - IEnumerable pushArgs; // OP_PUSHDATAn argument + byte[] pushArgs; // OP_PUSHDATAn argument + instruction opcode; // Current instruction - // Scan opcodes sequence + // Scan instructions sequence while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) { - byte[] data = pushArgs.ToArray(); + var data = pushArgs; if (opcode < instruction.OP_PUSHDATA1 && opcode > instruction.OP_0 && (data.Length == 1 && data[0] <= 16)) { @@ -309,17 +352,17 @@ namespace Novacoin /// /// Legacy mode flag /// Amount of sigops - public int GetSigOpCount(bool fAccurate) + public uint GetSigOpCount(bool fAccurate) { - ByteQueue wCodeBytes = new ByteQueue(codeBytes); + var wCodeBytes = new ByteQueue(ref codeBytes); - instruction opcode; // Current opcode - IEnumerable pushArgs; // OP_PUSHDATAn argument + instruction opcode; // Current instruction + byte[] pushArgs; // OP_PUSHDATAn argument - int nCount = 0; - instruction lastOpcode = instruction.OP_INVALIDOPCODE; + uint nCount = 0; + var lastOpcode = instruction.OP_INVALIDOPCODE; - // Scan opcodes sequence + // Scan instructions sequence while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) { if (opcode == instruction.OP_CHECKSIG || opcode == instruction.OP_CHECKSIGVERIFY) @@ -330,7 +373,7 @@ namespace Novacoin { if (fAccurate && lastOpcode >= instruction.OP_1 && lastOpcode <= instruction.OP_16) { - nCount += ScriptCode.DecodeOP_N(lastOpcode); + nCount += (uint)ScriptCode.DecodeOP_N(lastOpcode); } else { @@ -348,7 +391,7 @@ namespace Novacoin /// /// pay-to-script-hash scriptPubKey /// SigOps count - public int GetSigOpCount(CScript scriptSig) + public uint GetSigOpCount(CScript scriptSig) { if (!IsPayToScriptHash) { @@ -358,13 +401,20 @@ namespace Novacoin // This is a pay-to-script-hash scriptPubKey; // get the last item that the scriptSig // pushes onto the stack: - ByteQueue wScriptSig = scriptSig.GetByteQUeue(); + ByteQueue wScriptSig = scriptSig.GetByteQueue(); + int nScriptSigSize = scriptSig.Size; + + instruction opcode; // Current instruction + byte[] pushArgs = new byte[0]; // OP_PUSHDATAn argument - instruction opcode; // Current opcode - IEnumerable pushArgs; // OP_PUSHDATAn argument - while (ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs)) + while (wScriptSig.Index < nScriptSigSize) { + if (!ScriptCode.GetOp(ref wScriptSig, out opcode, out pushArgs)) + { + return 0; + } + if (opcode > instruction.OP_16) { return 0; @@ -372,7 +422,7 @@ namespace Novacoin } /// ... and return its opcount: - CScript subScript = new CScript(pushArgs); + var subScript = new CScript(pushArgs); return subScript.GetSigOpCount(true); @@ -385,8 +435,8 @@ namespace Novacoin public void SetDestination(CPubKey pubKey) { codeBytes.Clear(); - PushData(pubKey.PublicBytes); - AddOp(instruction.OP_CHECKSIG); + PushData(pubKey); + AddInstruction(instruction.OP_CHECKSIG); } /// @@ -396,11 +446,11 @@ namespace Novacoin public void SetDestination(CKeyID ID) { codeBytes.Clear(); - AddOp(instruction.OP_DUP); - AddOp(instruction.OP_HASH160); + AddInstruction(instruction.OP_DUP); + AddInstruction(instruction.OP_HASH160); AddHash(ID); - AddOp(instruction.OP_EQUALVERIFY); - AddOp(instruction.OP_CHECKSIG); + AddInstruction(instruction.OP_EQUALVERIFY); + AddInstruction(instruction.OP_CHECKSIG); } /// @@ -410,9 +460,9 @@ namespace Novacoin public void SetDestination(CScriptID ID) { codeBytes.Clear(); - AddOp(instruction.OP_HASH160); + AddInstruction(instruction.OP_HASH160); AddHash(ID); - AddOp(instruction.OP_EQUAL); + AddInstruction(instruction.OP_EQUAL); } /// @@ -428,31 +478,39 @@ namespace Novacoin /// /// Amount of required signatures. /// Set of public keys. - public void SetMultiSig(int nRequired, IEnumerable keys) + public void SetMultiSig(int nRequired, CPubKey[] keys) { codeBytes.Clear(); - AddOp(ScriptCode.EncodeOP_N(nRequired)); + AddInstruction(ScriptCode.EncodeOP_N(nRequired)); - foreach (CPubKey key in keys) + foreach (var key in keys) { - PushData(key.PublicBytes.ToList()); + PushData(key); } - AddOp(ScriptCode.EncodeOP_N(keys.Count())); - AddOp(instruction.OP_CHECKMULTISIG); + AddInstruction(ScriptCode.EncodeOP_N(keys.Length)); + AddInstruction(instruction.OP_CHECKMULTISIG); } /// /// Access to script code. /// - public IEnumerable Bytes + public static implicit operator byte[] (CScript script) { - get { return codeBytes; } + return script.codeBytes.ToArray(); + } + + /// + /// Script size + /// + public int Size + { + get { return codeBytes.Count; } } public CScriptID ScriptID { - get { return new CScriptID(Hash160.Compute160(codeBytes)); } + get { return new CScriptID(Hash160.Compute160(codeBytes.ToArray())); } } /// @@ -461,11 +519,11 @@ namespace Novacoin /// Code listing public override string ToString() { - StringBuilder sb = new StringBuilder(); - ByteQueue wCodeBytes = new ByteQueue(codeBytes); + var sb = new StringBuilder(); + var wCodeBytes = new ByteQueue(ref codeBytes); - instruction opcode; // Current opcode - IEnumerable pushArgs; // OP_PUSHDATAn argument + instruction opcode; // Current instruction + byte[] pushArgs; // OP_PUSHDATAn argument while (ScriptCode.GetOp(ref wCodeBytes, out opcode, out pushArgs)) { if (sb.Length != 0) @@ -487,4 +545,3 @@ namespace Novacoin } } } -