public override bool Equals(object obj) { if (this == obj) { return(true); } if (obj == null) { return(false); } ScriptChunk other = (ScriptChunk)obj; return(this.Opcode == other.Opcode && this.startLocationInProgram == other.startLocationInProgram && this.Data.Equals(other.Data)); }
/// <summary> /// Returns the public key in this script. If a script contains two constants and nothing else, it is assumed to /// be a scriptSig (input) for a pay-to-address output and the second constant is returned (the first is the /// signature). If a script contains a constant and an ScriptOpCodes.OP_CHECKSIG opcode, the constant is returned as it is /// assumed to be a direct pay-to-key scriptPubKey (output) and the first constant is the public key. /// </summary> public byte[] GetPubKey() { Thrower.If(this.Chunks.Count() != 2).Throw <ScriptException>("Script not of right size, expecting 2 but got " + this.Chunks.Count()); ScriptChunk firstChunk = this.Chunks.ElementAt(0); ScriptChunk secondChunk = this.Chunks.ElementAt(1); if (firstChunk.Data != null && firstChunk.Data.Length > 2 && secondChunk.Data != null && secondChunk.Data.Length > 2) { // If we have two large constants assume the input to a pay-to-address output. return(secondChunk.Data); } if (secondChunk.EqualsOpCode(ScriptOpCodes.OP_CHECKSIG) && firstChunk.Data != null && firstChunk.Data.Length > 2) { // A large constant followed by an ScriptOpCodes.OP_CHECKSIG is the key. return(firstChunk.Data); } throw new ScriptException("Script did not match expected form: " + this); }
/// <summary> /// Adds the given chunk at the given index in the program /// </summary> public ScriptBuilder AddChunk(int index, ScriptChunk chunk) { this.chunks.Insert(index, chunk); return(this); }
/// <summary> /// Adds the given chunk to the end of the program /// </summary> public ScriptBuilder AddChunk(ScriptChunk chunk) { return(this.AddChunk(this.chunks.Count(), chunk)); }
/// <summary> /// <p>To run a script, first we parse it which breaks it up into chunks representing pushes of data or logical /// opcodes. Then we can run the parsed chunks.</p> /// /// <p>The reason for this split, instead of just interpreting directly, is to make it easier /// to reach into a programs structure and pull out bits of data without having to run it. /// This is necessary to render the to/from addresses of transactions in a user interface. /// Bitcoin Core does something similar.</p> /// </summary> /// <param name="program"> /// The program. /// </param> /// <exception cref="ScriptException"> /// </exception> private void Parse(byte[] program) { this.Chunks = new List <ScriptChunk>(5); // Common size. using (var stream = new MemoryStream(program)) { using (var reader = new BinaryReader(stream)) { var initialSize = reader.BaseStream.Length; while (reader.Available() > 0) { var startLocationInProgram = reader.BaseStream.Position; int opcode = reader.ReadByte(); long dataToRead = -1; if (opcode >= 0 && opcode < ScriptOpCodes.OP_PUSHDATA1) { // Read some bytes of data, where how many is the opcode value itself. dataToRead = opcode; } else if (opcode == ScriptOpCodes.OP_PUSHDATA1) { if (reader.Available() < 1) { throw new ScriptException("Unexpected end of script"); } dataToRead = reader.ReadInt16(); } else if (opcode == ScriptOpCodes.OP_PUSHDATA2) { // Read a short, then read that many bytes of data. if (reader.Available() < 2) { throw new ScriptException("Unexpected end of script"); } dataToRead = reader.ReadInt32(); } else if (opcode == ScriptOpCodes.OP_PUSHDATA4) { // Read a uint32, then read that many bytes of data. // Though this is allowed, because its value cannot be > 520, it should never actually be used if (reader.Available() < 4) { throw new ScriptException("Unexpected end of script"); } dataToRead = reader.ReadInt64(); } ScriptChunk chunk; if (dataToRead == -1) { chunk = new ScriptChunk(opcode, null, startLocationInProgram); } else { if (dataToRead > reader.Available()) { throw new ScriptException("Push of data element that is larger than remaining data"); } byte[] data = reader.ReadBytes((int)dataToRead); chunk = new ScriptChunk(opcode, data, startLocationInProgram); } this.Chunks.Add(chunk); } } } }