private static BitcoinStream CreateHashWriter(HashVersion version) { BitcoinStream stream = new BitcoinStream(new MemoryStream(), true); stream.Type = SerializationType.Hash; stream.TransactionOptions = version == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness; return(stream); }
private static BitcoinStream CreateHashWriter(HashVersion version) { HashStream hs = new HashStream(); BitcoinStream stream = new BitcoinStream(hs, true); stream.Type = SerializationType.Hash; stream.TransactionOptions = version == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness; return stream; }
private BitcoinStream CreateHashWriter(HashVersion version) { var hs = CreateSignatureHashStream(); BitcoinStream stream = new BitcoinStream(hs, true); stream.Type = SerializationType.Hash; stream.TransactionOptions = version == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness; return(stream); }
public static ScriptSigs CombineSignatures(Script scriptPubKey, TransactionChecker checker, ScriptSigs input1, ScriptSigs input2) { if (scriptPubKey == null) { scriptPubKey = new Script(); } var scriptSig1 = input1.ScriptSig; var scriptSig2 = input2.ScriptSig; HashVersion hashVersion = HashVersion.Original; var isWitness = input1.WitSig != WitScript.Empty || input2.WitSig != WitScript.Empty; if (isWitness) { scriptSig1 = input1.WitSig.ToScript(); scriptSig2 = input2.WitSig.ToScript(); hashVersion = HashVersion.Witness; } var context = new ScriptEvaluationContext(); context.ScriptVerify = ScriptVerify.StrictEnc; context.EvalScript(scriptSig1, checker, hashVersion); var stack1 = context.Stack.AsInternalArray(); context = new ScriptEvaluationContext(); context.ScriptVerify = ScriptVerify.StrictEnc; context.EvalScript(scriptSig2, checker, hashVersion); var stack2 = context.Stack.AsInternalArray(); var result = CombineSignatures(scriptPubKey, checker, stack1, stack2, hashVersion); if (result == null) { return(scriptSig1.Length < scriptSig2.Length ? input2 : input1); } if (!isWitness) { return new ScriptSigs() { ScriptSig = result, WitSig = WitScript.Empty } } ; else { return(new ScriptSigs() { ScriptSig = input1.ScriptSig.Length < input2.ScriptSig.Length ? input2.ScriptSig : input1.ScriptSig, WitSig = new WitScript(result) }); } }
protected static BitcoinStream CreateHashWriter(HashVersion version) { var hs = new HashStream(); var stream = new BitcoinStream(hs, true) { Type = SerializationType.Hash, TransactionOptions = version == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness }; return(stream); }
public static byte[] GenerateHash(byte[] input, HashVersion version) { byte[] output = null; switch (version) { case HashVersion.SHA1: output = GenerateSHA1Hash(input); break; case HashVersion.SHA512: output = GenerateSHA512Hash(input); break; default: output = null; break; } return(output); }
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (sigversion == HashVersion.WitnessV0) { if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney) { throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput)); } uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts() : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence() : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs() : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(this.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(this.Version); // PoS Time sss.ReadWrite(this.Time); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(spentOutput.Value.Satoshi); sss.ReadWrite(Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(LockTime); // Sighash type sss.ReadWrite((uint)nHashType); return(GetHash(sss)); } bool fAnyoneCanPay = (nHashType & SigHash.AnyoneCanPay) != 0; bool fHashSingle = ((byte)nHashType & 0x1f) == (byte)SigHash.Single; bool fHashNone = ((byte)nHashType & 0x1f) == (byte)SigHash.None; if (nIn >= Inputs.Count) { return(uint256.One); } if (fHashSingle) { if (nIn >= Outputs.Count) { return(uint256.One); } } var stream = CreateHashWriter(sigversion); stream.ReadWrite(Version); stream.ReadWrite(Time); uint nInputs = (uint)(fAnyoneCanPay ? 1 : Inputs.Count); stream.ReadWriteAsVarInt(ref nInputs); for (int nInput = 0; nInput < nInputs; nInput++) { if (fAnyoneCanPay) { nInput = nIn; } stream.ReadWrite(Inputs[nInput].PrevOut); if (nInput != nIn) { stream.ReadWrite(Script.Empty); } else { WriteScriptCode(stream, scriptCode); } if (nInput != nIn && (fHashSingle || fHashNone)) { stream.ReadWrite((uint)0); } else { stream.ReadWrite(Inputs[nInput].Sequence); } } uint nOutputs = (uint)(fHashNone ? 0 : (fHashSingle ? nIn + 1 : Outputs.Count)); stream.ReadWriteAsVarInt(ref nOutputs); for (int nOutput = 0; nOutput < nOutputs; nOutput++) { if (fHashSingle && nOutput != nIn) { this.Outputs.CreateNewTxOut().ReadWrite(stream); } else { Outputs[nOutput].ReadWrite(stream); } } stream.ReadWriteStruct(LockTime); stream.ReadWrite((uint)nHashType); return(GetHash(stream)); }
public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { return(txTo.GetSignatureHash(scriptCode, nIn, nHashType, amount, sigversion, precomputedTransactionData)); }
//https://en.bitcoin.it/wiki/OP_CHECKSIG public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if(sigversion == HashVersion.Witness) { if(amount == null) throw new ArgumentException("The amount of the output being signed must be provided", "amount"); uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null ? GetHashPrevouts(txTo) : precomputedTransactionData.HashPrevouts; } if((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null ? GetHashSequence(txTo) : precomputedTransactionData.HashSequence; } if(((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null ? GetHashOutputs(txTo) : precomputedTransactionData.HashOutputs; } else if(((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < txTo.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(txTo.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(txTo.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(txTo.Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(amount.Satoshi); sss.ReadWrite((uint)txTo.Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(txTo.LockTime); // Sighash type sss.ReadWrite((uint)nHashType); return GetHash(sss); } if(nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return uint256.One; } var hashType = nHashType & (SigHash)31; // Check for invalid use of SIGHASH_SINGLE if(hashType == SigHash.Single) { if(nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return uint256.One; } } var scriptCopy = new Script(scriptCode._Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes()); //Set all TxIn script to empty string foreach(var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if(hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } else if(hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for(var i = 0; i < txCopy.Outputs.Count; i++) { if(i == nIn) continue; txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } if((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); return GetHash(stream); }
public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount = null, HashVersion sigversion = HashVersion.Original) { return(SignatureHash(scriptCode, txTo, nIn, nHashType, amount, sigversion, null)); }
private static Script CombineMultisig(Script scriptPubKey, TransactionChecker checker, byte[][] sigs1, byte[][] sigs2, HashVersion hashVersion) { // Combine all the signatures we've got: List<TransactionSignature> allsigs = new List<TransactionSignature>(); foreach(var v in sigs1) { if(TransactionSignature.IsValid(v)) { allsigs.Add(new TransactionSignature(v)); } } foreach(var v in sigs2) { if(TransactionSignature.IsValid(v)) { allsigs.Add(new TransactionSignature(v)); } } var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey); if(multiSigParams == null) throw new InvalidOperationException("The scriptPubKey is not a valid multi sig"); Dictionary<PubKey, TransactionSignature> sigs = new Dictionary<PubKey, TransactionSignature>(); foreach(var sig in allsigs) { foreach(var pubkey in multiSigParams.PubKeys) { if(sigs.ContainsKey(pubkey)) continue; // Already got a sig for this pubkey ScriptEvaluationContext eval = new ScriptEvaluationContext(); if(eval.CheckSig(sig, pubkey, scriptPubKey, checker, hashVersion)) { sigs.AddOrReplace(pubkey, sig); } } } // Now build a merged CScript: int nSigsHave = 0; Script result = new Script(OpcodeType.OP_0); // pop-one-too-many workaround foreach(var pubkey in multiSigParams.PubKeys) { if(sigs.ContainsKey(pubkey)) { result += Op.GetPushOp(sigs[pubkey].ToBytes()); nSigsHave++; } if(nSigsHave >= multiSigParams.SignatureCount) break; } // Fill any missing with OP_0: for(int i = nSigsHave ; i < multiSigParams.SignatureCount ; i++) result += OpcodeType.OP_0; return result; }
private static Script CombineSignatures(Script scriptPubKey, TransactionChecker checker, byte[][] sigs1, byte[][] sigs2, HashVersion hashVersion) { var template = StandardScripts.GetTemplateFromScriptPubKey(scriptPubKey); if(template is PayToWitPubKeyHashTemplate) { scriptPubKey = new KeyId(scriptPubKey.ToBytes(true).SafeSubarray(1, 20)).ScriptPubKey; template = StandardScripts.GetTemplateFromScriptPubKey(scriptPubKey); } if(template == null || template is TxNullDataTemplate) return PushAll(Max(sigs1, sigs2)); if(template is PayToPubkeyTemplate || template is PayToPubkeyHashTemplate) if(sigs1.Length == 0 || sigs1[0].Length == 0) return PushAll(sigs2); else return PushAll(sigs1); if(template is PayToScriptHashTemplate || template is PayToWitTemplate) { if(sigs1.Length == 0 || sigs1[sigs1.Length - 1].Length == 0) return PushAll(sigs2); if(sigs2.Length == 0 || sigs2[sigs2.Length - 1].Length == 0) return PushAll(sigs1); var redeemBytes = sigs1[sigs1.Length - 1]; var redeem = new Script(redeemBytes); sigs1 = sigs1.Take(sigs1.Length - 1).ToArray(); sigs2 = sigs2.Take(sigs2.Length - 1).ToArray(); Script result = CombineSignatures(redeem, checker, sigs1, sigs2, hashVersion); result += Op.GetPushOp(redeemBytes); return result; } if(template is PayToMultiSigTemplate) { return CombineMultisig(scriptPubKey, checker, sigs1, sigs2, hashVersion); } return null; }
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (nIn >= Inputs.Count && (uint)nIn != NOT_AN_INPUT) // second condition is always true, NBitcoin restricts us to transparent txs only, left to match the original code { throw new InvalidOperationException("Input index is out of range"); } if (fOverwintered && (nVersionGroupId == OVERWINTER_VERSION_GROUP_ID || nVersionGroupId == SAPLING_VERSION_GROUP_ID)) { uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; uint256 hashJoinSplits = uint256.Zero; uint256 hashShieldedSpends = uint256.Zero; uint256 hashShieldedOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts() : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence() : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs() : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count) { using (var ss = new BLAKE2bWriter(ZCASH_OUTPUTS_HASH_PERSONALIZATION)) { ss.ReadWrite(Outputs[nIn]); hashOutputs = ss.GetHash(); } } if (vjoinsplit.Any()) { hashJoinSplits = GetJoinSplitsHash(); } if (vShieldedSpend.Any()) { hashShieldedSpends = GetShieldedSpendsHash(); } if (vShieldedOutput.Any()) { hashShieldedOutputs = GetShieldedOutputsHash(); } var branchId = nBranchId.HasValue ? nBranchId.Value : Version == OVERWINTER_VERSION ? OVERWINTER_BRANCH_ID : Version == SAPLING_VERSION ? SAPLING_BRANCH_ID : 0; var branchIdData = BitConverter.IsLittleEndian ? BitConverter.GetBytes(branchId) : BitConverter.GetBytes(branchId).Reverse().ToArray(); var personal = Encoding.ASCII.GetBytes("ZcashSigHash") .Concat(branchIdData) .ToArray(); using (var ss = new BLAKE2bWriter(personal)) { // Version var nVersion = Version; ss.ReadWriteVersionEncoded(ref nVersion, ref fOverwintered); ss.ReadWrite(nVersionGroupId); // Input prevouts/nSequence (none/all, depending on flags) ss.ReadWrite(hashPrevouts); ss.ReadWrite(hashSequence); // Outputs (none/one/all, depending on flags) ss.ReadWrite(hashOutputs); // JoinSplits ss.ReadWrite(hashJoinSplits); if (nVersionGroupId == SAPLING_VERSION_GROUP_ID) { // Spend descriptions ss.ReadWrite(hashShieldedSpends); // Output descriptions ss.ReadWrite(hashShieldedOutputs); } // Locktime ss.ReadWriteStruct(LockTime); // Expiry height ss.ReadWrite(nExpiryHeight); if (nVersionGroupId == SAPLING_VERSION_GROUP_ID) { // Sapling value balance ss.ReadWrite(valueBalance); } // Sighash type ss.ReadWrite((uint)nHashType); // If this hash is for a transparent input signature // (i.e. not for txTo.joinSplitSig): if ((uint)nIn != NOT_AN_INPUT) // always true, NBitcoin restricts us to transparent txs only, left to match the original code { // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contained in hashSequence. ss.ReadWrite(Inputs[nIn].PrevOut); ss.ReadWrite(scriptCode); ss.ReadWrite(amount.Satoshi); ss.ReadWrite(Inputs[nIn].Sequence.Value); } return(ss.GetHash()); } } else { // Check for invalid use of SIGHASH_SINGLE if (((uint)nHashType & 0x1f) == (uint)SigHash.Single) { if (nIn >= Outputs.Count) { throw new InvalidOperationException("No matching output for SIGHASH_SINGLE"); } } var scriptCopy = new Script(scriptCode.ToOps().Where(op => op.Code != OpcodeType.OP_CODESEPARATOR)); var txCopy = new ZcashTransaction(this.ToHex()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (nHashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (nHashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } // clean JS signature // see https://github.com/zcash/zcash/blob/e868f8247faea8cc74aef69262d93bdeacc82c53/src/script/interpreter.cpp#L1053 txCopy.joinSplitSig = new byte[64]; //Serialize TxCopy, append 4 byte hashtypecode using (var hs = CreateSignatureHashStream()) { BitcoinStream stream = new BitcoinStream(hs, true); stream.Type = SerializationType.Hash; stream.TransactionOptions = sigversion == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness; txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); return(hs.GetHash()); } } }
//https://en.bitcoin.it/wiki/OP_CHECKSIG public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount = null, HashVersion sigversion = HashVersion.Original) { if (txTo.useForkID == true) { //Because Bitcoin Cash hashes similar to Segwit hashes (uses amount of input), treat it as a segwit transaction for hashing sigversion = HashVersion.Witness; } if (sigversion == HashVersion.Witness) { if (amount == null) { throw new ArgumentException("The amount of the output being signed must be provided", "amount"); } uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { BitcoinStream ss = CreateHashWriter(sigversion); foreach (var input in txTo.Inputs) { ss.ReadWrite(input.PrevOut); } hashPrevouts = GetHash(ss); // TODO: cache this value for all signatures in a transaction } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { BitcoinStream ss = CreateHashWriter(sigversion); foreach (var input in txTo.Inputs) { ss.ReadWrite((uint)input.Sequence); } hashSequence = GetHash(ss); // TODO: cache this value for all signatures in a transaction } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { BitcoinStream ss = CreateHashWriter(sigversion); foreach (var txout in txTo.Outputs) { ss.ReadWrite(txout); } hashOutputs = GetHash(ss); // TODO: cache this value for all signatures in a transaction } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < txTo.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(txTo.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(txTo.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(txTo.Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(amount.Satoshi); sss.ReadWrite((uint)txTo.Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(txTo.LockTime); // Sighash type uint write_nHashType = (uint)nHashType; if (txTo.useForkID == true) { //Bitshift the hashtype with the zero-byte forkID if present for BCH write_nHashType |= 0x00 << 8; //ForkID for BCH is 0, bitshift most significant digits } sss.ReadWrite(write_nHashType); return(GetHash(sss)); } if (nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return(uint256.One); } // Check for invalid use of SIGHASH_SINGLE if (nHashType == SigHash.Single) { if (nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return(uint256.One); } } var scriptCopy = new Script(scriptCode._Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes(), txTo.hasTimeStamp); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; var hashType = nHashType & (SigHash)31; if (hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); if (txTo.useHASH256 == true) { return(GetHash(stream)); } else { return(GetSHA256Hash(stream)); } }
private static Script CombineSignatures(Script scriptPubKey, TransactionChecker checker, byte[][] sigs1, byte[][] sigs2, HashVersion hashVersion) { var template = StandardScripts.GetTemplateFromScriptPubKey(scriptPubKey); if (template is PayToWitPubKeyHashTemplate) { scriptPubKey = new KeyId(scriptPubKey.ToBytes(true).SafeSubarray(1, 20)).ScriptPubKey; template = StandardScripts.GetTemplateFromScriptPubKey(scriptPubKey); } if (template == null || template is TxNullDataTemplate) { return(PushAll(Max(sigs1, sigs2))); } if (template is PayToPubkeyTemplate || template is PayToPubkeyHashTemplate) { if (sigs1.Length == 0 || sigs1[0].Length == 0) { return(PushAll(sigs2)); } else { return(PushAll(sigs1)); } } if (template is PayToScriptHashTemplate || template is PayToWitTemplate) { if (sigs1.Length == 0 || sigs1[sigs1.Length - 1].Length == 0) { return(PushAll(sigs2)); } if (sigs2.Length == 0 || sigs2[sigs2.Length - 1].Length == 0) { return(PushAll(sigs1)); } var redeemBytes = sigs1[sigs1.Length - 1]; var redeem = new Script(redeemBytes); sigs1 = sigs1.Take(sigs1.Length - 1).ToArray(); sigs2 = sigs2.Take(sigs2.Length - 1).ToArray(); Script result = CombineSignatures(redeem, checker, sigs1, sigs2, hashVersion); result += Op.GetPushOp(redeemBytes); return(result); } if (template is PayToMultiSigTemplate) { return(CombineMultisig(scriptPubKey, checker, sigs1, sigs2, hashVersion)); } return(null); }
//https://en.bitcoin.it/wiki/OP_CHECKSIG public static uint256 SignatureHash(Script scriptCode, Transaction txTo, int nIn, SigHash nHashType, Money amount = null, HashVersion sigversion = HashVersion.Original) { if(sigversion == HashVersion.Witness) { if(amount == null) throw new ArgumentException("The amount of the output being signed must be provided", "amount"); uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if((nHashType & SigHash.AnyoneCanPay) == 0) { BitcoinStream ss = CreateHashWriter(sigversion); foreach(var input in txTo.Inputs) { ss.ReadWrite(input.PrevOut); } hashPrevouts = GetHash(ss); // TODO: cache this value for all signatures in a transaction } if((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { BitcoinStream ss = CreateHashWriter(sigversion); foreach(var input in txTo.Inputs) { ss.ReadWrite((uint)input.Sequence); } hashSequence = GetHash(ss); // TODO: cache this value for all signatures in a transaction } if(((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { BitcoinStream ss = CreateHashWriter(sigversion); foreach(var txout in txTo.Outputs) { ss.ReadWrite(txout); } hashOutputs = GetHash(ss); // TODO: cache this value for all signatures in a transaction } else if(((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < txTo.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(txTo.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(txTo.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(txTo.Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(amount.Satoshi); sss.ReadWrite((uint)txTo.Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(txTo.LockTime); // Sighash type sss.ReadWrite((uint)nHashType); return GetHash(sss); } if(nIn >= txTo.Inputs.Count) { Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return uint256.One; } // Check for invalid use of SIGHASH_SINGLE if(nHashType == SigHash.Single) { if(nIn >= txTo.Outputs.Count) { Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return uint256.One; } } var scriptCopy = new Script(scriptCode._Script); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = new Transaction(txTo.ToBytes()); //Set all TxIn script to empty string foreach(var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; var hashType = nHashType & (SigHash)31; if(hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } else if(hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for(var i = 0 ; i < txCopy.Outputs.Count ; i++) { if(i == nIn) continue; txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn)) input.Sequence = 0; } if((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); return GetHash(stream); }
private static Script CombineMultisig(Script scriptPubKey, TransactionChecker checker, byte[][] sigs1, byte[][] sigs2, HashVersion hashVersion) { // Combine all the signatures we've got: List <TransactionSignature> allsigs = new List <TransactionSignature>(); foreach (var v in sigs1) { if (TransactionSignature.IsValid(v)) { allsigs.Add(new TransactionSignature(v)); } } foreach (var v in sigs2) { if (TransactionSignature.IsValid(v)) { allsigs.Add(new TransactionSignature(v)); } } var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey); if (multiSigParams == null) { throw new InvalidOperationException("The scriptPubKey is not a valid multi sig"); } Dictionary <PubKey, TransactionSignature> sigs = new Dictionary <PubKey, TransactionSignature>(); foreach (var sig in allsigs) { foreach (var pubkey in multiSigParams.PubKeys) { if (sigs.ContainsKey(pubkey)) { continue; // Already got a sig for this pubkey } ScriptEvaluationContext eval = new ScriptEvaluationContext(); if (eval.CheckSig(sig, pubkey, scriptPubKey, checker, hashVersion)) { sigs.AddOrReplace(pubkey, sig); } } } // Now build a merged CScript: int nSigsHave = 0; Script result = new Script(OpcodeType.OP_0); // pop-one-too-many workaround foreach (var pubkey in multiSigParams.PubKeys) { if (sigs.ContainsKey(pubkey)) { result += Op.GetPushOp(sigs[pubkey].ToBytes()); nSigsHave++; } if (nSigsHave >= multiSigParams.SignatureCount) { break; } } // Fill any missing with OP_0: for (int i = nSigsHave; i < multiSigParams.SignatureCount; i++) { result += OpcodeType.OP_0; } return(result); }
private static BitcoinStream CreateHashWriter(HashVersion version) { BitcoinStream stream = new BitcoinStream(new MemoryStream(), true); stream.Type = SerializationType.Hash; stream.TransactionOptions = version == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness; return stream; }
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { uint nForkHashType = (uint)nHashType; if (UsesForkId(nHashType)) { nForkHashType |= ForkId << 8; } if ((SupportSegwit && sigversion == HashVersion.Witness) || UsesForkId(nHashType)) { if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney) { throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput)); } uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts() : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence() : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs() : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(this.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(this.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(spentOutput.Value.Satoshi); sss.ReadWrite((uint)Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(LockTime); // Sighash type sss.ReadWrite((uint)nForkHashType); return(GetHash(sss)); } if (nIn >= Inputs.Count) { return(uint256.One); } var hashType = nHashType & (SigHash)31; // Check for invalid use of SIGHASH_SINGLE if (hashType == SigHash.Single) { if (nIn >= Outputs.Count) { return(uint256.One); } } var scriptCopy = scriptCode.Clone(); scriptCode = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = GetConsensusFactory().CreateTransaction(); txCopy.FromBytes(this.ToBytes()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nForkHashType); return(GetHash(stream)); }
/* * if (nIn >= txTo.vin.size()) * { * printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); * return 1; * } * CTransaction txTmp(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.FindAndDelete(CScript(OP_CODESEPARATOR)); * * // Blank out other inputs' signatures * for (unsigned int i = 0; i < txTmp.vin.size(); i++) * txTmp.vin[i].scriptSig = CScript(); * txTmp.vin[nIn].scriptSig = scriptCode; * * // Blank out some of the outputs * if ((nHashType & 0x1f) == SIGHASH_NONE) * { * // Wildcard payee * txTmp.vout.clear(); * * // Let the others update at will * for (unsigned int i = 0; i < txTmp.vin.size(); i++) * if (i != nIn) * txTmp.vin[i].nSequence = 0; * } * else if ((nHashType & 0x1f) == SIGHASH_SINGLE) * { * // Only lock-in the txout payee at same index as txin * unsigned int nOut = nIn; * if (nOut >= txTmp.vout.size()) * { * printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); * return 1; * } * txTmp.vout.resize(nOut+1); * for (unsigned int i = 0; i < nOut; i++) * txTmp.vout[i].SetNull(); * * // Let the others update at will * for (unsigned int i = 0; i < txTmp.vin.size(); i++) * if (i != nIn) * txTmp.vin[i].nSequence = 0; * } * * // Blank out other inputs completely, not recommended for open transactions * if (nHashType & SIGHASH_ANYONECANPAY) * { * txTmp.vin[0] = txTmp.vin[nIn]; * txTmp.vin.resize(1); * } * * // Serialize and hash * CHashWriter ss(SER_GETHASH, 0); * ss << txTmp << nHashType; * return ss.GetHash(); */ public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (nIn >= Inputs.Count) { //Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return(uint256.One); } var scriptCopy = new Script(scriptCode.ToBytes()); scriptCopy = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var hashType = nHashType & (SigHash)31; // Check for invalid use of SIGHASH_SINGLE //if (hashType == SigHash.Single) //{ // if (nIn >= Outputs.Count) // { // //Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); // return uint256.One; // } //} var txCopy = (SolarCoinTransaction)GetConsensusFactory().CreateTransaction(); txCopy.FromBytes(this.ToBytes()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (hashType == SigHash.Single) { if (nIn >= Outputs.Count) { //Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return(uint256.One); } //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < nIn; i++) { //if (i == nIn) // continue; txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. //txCopy.Inputs[0].ScriptSig = scriptCopy; } var previousType = txCopy.NType; txCopy.NType = (uint)PrimaryActions.SER_GETHASH; //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); var txHash = GetHash(stream); txCopy.NType = previousType; return(txHash); }