public byte[] CreatePublicKeyScript(byte[] publicKey) { var publicKeyHash = RIPEMD160Static.ComputeHash(SHA256Static.ComputeHash(publicKey)); using (var publicKeyScript = new ScriptBuilder()) { publicKeyScript.WriteOp(ScriptOp.OP_DUP); publicKeyScript.WriteOp(ScriptOp.OP_HASH160); publicKeyScript.WritePushData(publicKeyHash); publicKeyScript.WriteOp(ScriptOp.OP_EQUALVERIFY); publicKeyScript.WriteOp(ScriptOp.OP_CHECKSIG); //Debug.WriteLine("Public Script: {0}".Format2(publicKeyScript.GetScript().ToHexDataString())); return(publicKeyScript.GetScript()); } }
public byte[] CreateOutputFromPublicKey(byte[] publicKey) { var publicKeyHash = RIPEMD160Static.ComputeHash(SHA256Static.ComputeHash(publicKey)); return(CreateOutputFromPublicKeyHash(publicKeyHash)); }
private bool ExecuteOps(byte[] scriptPubKey, Transaction tx, int inputIndex, byte[] script, out Stack stack, out Stack altStack) { stack = new Stack(); altStack = new Stack(); using (var scriptStream = new MemoryStream(script)) using (var opReader = new BinaryReader(scriptStream)) { while (opReader.BaseStream.Position < script.Length) { var opByte = opReader.ReadByte(); var op = (ScriptOp)Enum.ToObject(typeof(ScriptOp), opByte); if (logger.IsTraceEnabled) { logger.Trace("Executing {0} with stack count: {1}", OpName(opByte), stack.Count); } switch (op) { // Constants case ScriptOp.OP_PUSHDATA1: { if (opReader.BaseStream.Position + 1 >= script.Length) { return(false); } var length = opReader.ReadByte(); stack.PushBytes(opReader.ReadExactly(length)); } break; case ScriptOp.OP_PUSHDATA2: { if (opReader.BaseStream.Position + 2 >= script.Length) { return(false); } var length = opReader.ReadUInt16(); stack.PushBytes(opReader.ReadExactly(length)); } break; case ScriptOp.OP_PUSHDATA4: { if (opReader.BaseStream.Position + 4 >= script.Length) { return(false); } var length = opReader.ReadUInt32(); stack.PushBytes(opReader.ReadExactly(length.ToIntChecked())); } break; // Flow control case ScriptOp.OP_NOP: { } break; // Stack case ScriptOp.OP_DROP: { if (stack.Count < 1) { return(false); } var value = stack.PopBytes(); if (logger.IsTraceEnabled) { logger.Trace("{0} dropped {1}", OpName(opByte), value); } } break; case ScriptOp.OP_DUP: { if (stack.Count < 1) { return(false); } var value = stack.PeekBytes(); stack.PushBytes(value); if (logger.IsTraceEnabled) { logger.Trace("{0} duplicated {2}", OpName(opByte), value); } } break; // Splice // Bitwise logic case ScriptOp.OP_EQUAL: case ScriptOp.OP_EQUALVERIFY: { if (stack.Count < 2) { return(false); } var value1 = stack.PopBytes(); var value2 = stack.PopBytes(); var result = value1.SequenceEqual(value2); stack.PushBool(result); if (logger.IsTraceEnabled) { logger.Trace( @"{0} compared values: value1: {1} value2: {2} result: {3}", OpName(opByte), value1, value2, result); } if (op == ScriptOp.OP_EQUALVERIFY) { if (result) { stack.PopBool(); } else { return(false); } } } break; // Arithmetic // Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output. // Crypto case ScriptOp.OP_SHA256: { if (stack.Count < 1) { return(false); } var value = stack.PopBytes().ToArray(); var hash = SHA256Static.ComputeHash(value); stack.PushBytes(hash); if (logger.IsTraceEnabled) { logger.Trace( @"{0} hashed value: value: {1} hash: {2}", OpName(opByte), value, hash); } } break; case ScriptOp.OP_HASH160: { if (stack.Count < 1) { return(false); } var value = stack.PopBytes().ToArray(); var hash = RIPEMD160Static.ComputeHash(SHA256Static.ComputeHash(value)); stack.PushBytes(hash); if (logger.IsTraceEnabled) { logger.Trace( @"{0} hashed value: value: {1} hash: {2}", OpName(opByte), value, hash); } } break; case ScriptOp.OP_CHECKSIG: case ScriptOp.OP_CHECKSIGVERIFY: { if (stack.Count < 2) { return(false); } var pubKey = stack.PopBytes().ToArray(); var sig = stack.PopBytes().ToArray(); var startTime = DateTime.UtcNow; byte hashType; byte[] txSignature, txSignatureHash; var result = VerifySignature(scriptPubKey, tx, sig, pubKey, inputIndex, out hashType, out txSignature, out txSignatureHash); stack.PushBool(result); var finishTime = DateTime.UtcNow; if (logger.IsTraceEnabled) { logger.Trace( @"{0} executed in {9} ms: tx: {1} inputIndex: {2} pubKey: {3} sig: {4} hashType: {5} txSignature: {6} txSignatureHash: {7} result: {8}", OpName(opByte), new byte[0] /*tx.ToRawBytes()*/, inputIndex, pubKey, sig, hashType, txSignature, txSignatureHash, result, (finishTime - startTime).TotalMilliseconds.ToString("0")); } if (op == ScriptOp.OP_CHECKSIGVERIFY) { if (result) { stack.PopBool(); } else { return(false); } } } break; // Pseudo-words // These words are used internally for assisting with transaction matching. They are invalid if used in actual scripts. // Reserved words // Any opcode not assigned is also reserved. Using an unassigned opcode makes the transaction invalid. default: //OP_PUSHBYTES1-75 if (opByte >= (int)ScriptOp.OP_PUSHBYTES1 && opByte <= (int)ScriptOp.OP_PUSHBYTES75) { stack.PushBytes(opReader.ReadExactly(opByte)); if (logger.IsTraceEnabled) { logger.Trace("{0} loaded {1} bytes onto the stack: {2}", OpName(opByte), opByte, stack.PeekBytes()); } } // Unknown op else { var message = $"Invalid operation in tx {tx.Hash} input {inputIndex}: {new[] { opByte }.ToHexNumberString()} {OpName(opByte)}"; //logger.Warn(message); throw new Exception(message); } break; } if (logger.IsTraceEnabled) { logger.Trace(new string('-', 80)); } } } // TODO verify no if/else blocks left over // TODO not entirely sure what default return should be return(true); }