private uint256 GetShieldedSpendsHash() { using (var ss = new BLAKE2bWriter(ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION)) { foreach (var spend in vShieldedSpend) { ss.ReadWrite(spend.cv); ss.ReadWrite(spend.anchor); ss.ReadWrite(spend.nullifier); ss.ReadWrite(spend.rk); ss.ReadWrite(ref spend.zkproof); } return(ss.GetHash()); } }
private uint256 GetJoinSplitsHash() { using (var ss = new BLAKE2bWriter(ZCASH_JOINSPLITS_HASH_PERSONALIZATION)) { // provide version info to joinSplit serializer ss.Version = Version; ss.Overwintered = fOverwintered; foreach (var js in vjoinsplit) { ss.ReadWrite(js); } ss.ReadWrite(joinSplitPubKey); return(ss.GetHash()); } }
private uint256 GetShieldedOutputsHash() { using (var ss = new BLAKE2bWriter(ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION)) { foreach (var sout in vShieldedOutput) { ss.ReadWrite(sout); } return(ss.GetHash()); } }
private uint256 GetHashPrevouts() { using (var ss = new BLAKE2bWriter(ZCASH_PREVOUTS_HASH_PERSONALIZATION)) { foreach (var input in Inputs) { ss.ReadWrite(input.PrevOut); } return(ss.GetHash()); } }
private uint256 GetHashSequence() { using (var ss = new BLAKE2bWriter(ZCASH_SEQUENCE_HASH_PERSONALIZATION)) { foreach (var input in Inputs) { ss.ReadWrite((uint)input.Sequence); } return(ss.GetHash()); } }
private uint256 GetHashOutputs() { using (var ss = new BLAKE2bWriter(ZCASH_OUTPUTS_HASH_PERSONALIZATION)) { foreach (var txout in Outputs) { ss.ReadWrite(txout); } return(ss.GetHash()); } }
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()); } } }