Esempio n. 1
0
        private uint256 GetHashPrevouts()
        {
            using (var ss = new BLAKE2bWriter(ZCASH_PREVOUTS_HASH_PERSONALIZATION))
            {
                foreach (var input in Inputs)
                {
                    ss.ReadWrite(input.PrevOut);
                }

                return(ss.GetHash());
            }
        }
Esempio n. 2
0
        private uint256 GetShieldedOutputsHash()
        {
            using (var ss = new BLAKE2bWriter(ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION))
            {
                foreach (var sout in vShieldedOutput)
                {
                    ss.ReadWrite(sout);
                }

                return(ss.GetHash());
            }
        }
Esempio n. 3
0
        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());
            }
        }
Esempio n. 4
0
        private uint256 GetHashOutputs()
        {
            using (var ss = new BLAKE2bWriter(ZCASH_OUTPUTS_HASH_PERSONALIZATION))
            {
                foreach (var txout in Outputs)
                {
                    ss.ReadWrite(txout);
                }

                return(ss.GetHash());
            }
        }
Esempio n. 5
0
        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());
            }
        }
Esempio n. 6
0
        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());
            }
        }
Esempio n. 7
0
        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());
                }
            }
        }