public uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (UsesForkId(nHashType)) { uint nForkHashType = (uint)nHashType; nForkHashType |= (uint)ForkID << 8; 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 < Outputs.Count) { CoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(Outputs[nIn]); hashOutputs = GetHash(ss); } CoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(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(nForkHashType); return(GetHash(sss)); } if (nIn >= Inputs.Count) { return(uint256.One); } SigHash hashType = nHashType & (SigHash)31; // Check for invalid use of SIGHASH_SINGLE if (hashType == SigHash.Single) { if (nIn >= Outputs.Count) { return(uint256.One); } } Script scriptCopy = scriptCode.Clone(); scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); if (!CustomTransaction.GetCustomTransaction(CurrencyID, out Transaction txCopy)) { txCopy = new Transaction(); } txCopy.FromBytes(this.ToBytes()); //Set all TxIn script to empty string foreach (TxIn 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 (TxIn 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 (int 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 (TxIn 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. TxIn 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 CoinStream stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); //stream.ReadWrite(nForkHashType); return(GetHash(stream)); }
public virtual string SignTransaction(string aTxData, ICurrencyTransaction aValidationInfo) { if (!CustomTransaction.GetCustomTransaction(Id, out Transaction tx, aTxData, Network)) { tx = new Transaction(aTxData, Network); } // check how much is being spent and insure all is spent. BigInteger lTotalSent = 0; foreach (ITransactionUnit linput in aValidationInfo.Inputs) { lTotalSent += linput.Amount; } BigInteger lTotalSpending = aValidationInfo.TxFee; foreach (ITransactionUnit lOutput in aValidationInfo.Outputs) { lTotalSpending += lOutput.Amount; } if (lTotalSent != lTotalSpending) { throw new Exception("The total of the inputs does not equal the total outputs."); } // Check output amounts in the transaction to sign. int lValidCount = 0; foreach (TxOut lOutput in tx.Outputs) { string lAddress = lOutput.GetAddress(Network); foreach (ITransactionUnit lValOut in aValidationInfo.Outputs) { if (lAddress == lValOut.Address) { if (lOutput.Value.Satoshi == lValOut.Amount) { lValidCount++; } } } } if (lValidCount != aValidationInfo.Outputs.Length) { throw new Exception("Transaction to sign is not correct."); } List <CCKey> lKeys = new List <CCKey>(); List <ICoin> lCoins = new List <ICoin>(); for (int i = 0; i < tx.Inputs.Count; i++) { if (!FAddressLookup.TryGetValue(aValidationInfo.Inputs[i].Address, out long lIndex)) { throw new Exception(string.Format("Address {0} does not exist.", aValidationInfo.Inputs[i].Address)); } lKeys.Add(GetCCKey(lIndex)); ITransactionUnit lPrevOut = aValidationInfo.Inputs .Where(lInput => lInput.TxID == tx.Inputs[i].PrevOut.Hash.ToString() && lInput.Index == tx.Inputs[i].PrevOut.N) .FirstOrDefault(); if (lPrevOut == null) { throw new Exception("Failed to found previous tx out information"); } lCoins.Add(new Coin(tx.Inputs[i].PrevOut, new TxOut() { ScriptPubKey = tx.Inputs[i].ScriptSig, Value = new Money((long)lPrevOut.Amount) })); tx.Inputs[i].ScriptSig = Script.Empty; } var lHexTest = tx.ToHex(); tx.Sign(lKeys.ToArray(), lCoins.ToArray()); return(tx.ToHex()); }