From 7245f32835c5d2419fa4692b8873bacd08fe3b10 Mon Sep 17 00:00:00 2001 From: Ariel Date: Wed, 17 Jan 2018 12:48:10 +0200 Subject: [PATCH] update SignatureHash according to Overwinter spec with help from str4d --- src/primitives/transaction.h | 17 ++++++--- src/script/interpreter.cpp | 72 +++++++++++++++++++++++++++--------- src/script/interpreter.h | 2 +- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 28347d6a5..654a68b8a 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -383,12 +383,7 @@ public: *const_cast(&fOverwintered) = header >> 31; *const_cast(&this->nVersion) = header & 0x7FFFFFFF; } else { - // When serializing v1 and v2, the 4 byte header is nVersion - uint32_t header = this->nVersion; - // When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion - if (fOverwintered) { - header |= 1 << 31; - } + uint32_t header = GetHeader(); READWRITE(header); } nVersion = this->nVersion; @@ -428,6 +423,16 @@ public: return hash; } + uint32_t GetHeader() const { + // When serializing v1 and v2, the 4 byte header is nVersion + uint32_t header = this->nVersion; + // When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion + if (fOverwintered) { + header |= 1 << 31; + } + return header; + } + // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f436688f4..66153477e 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -5,6 +5,7 @@ #include "interpreter.h" +#include "consensus/upgrades.h" #include "primitives/transaction.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" @@ -1056,8 +1057,17 @@ public: } }; +const unsigned char ZCASH_PREVOUTS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','P','r','e','v','o','u','t','H','a','s','h'}; +const unsigned char ZCASH_SEQUENCE_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','S','e','q','u','e','n','c','H','a','s','h'}; +const unsigned char ZCASH_OUTPUTS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'}; +const unsigned char ZCASH_JOINSPLITS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] = + {'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'}; + uint256 GetPrevoutHash(const CTransaction& txTo) { - CHashWriter ss(SER_GETHASH, 0); + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_PREVOUTS_HASH_PERSONALIZATION); for (unsigned int n = 0; n < txTo.vin.size(); n++) { ss << txTo.vin[n].prevout; } @@ -1065,7 +1075,7 @@ uint256 GetPrevoutHash(const CTransaction& txTo) { } uint256 GetSequenceHash(const CTransaction& txTo) { - CHashWriter ss(SER_GETHASH, 0); + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SEQUENCE_HASH_PERSONALIZATION); for (unsigned int n = 0; n < txTo.vin.size(); n++) { ss << txTo.vin[n].nSequence; } @@ -1073,13 +1083,22 @@ uint256 GetSequenceHash(const CTransaction& txTo) { } uint256 GetOutputsHash(const CTransaction& txTo) { - CHashWriter ss(SER_GETHASH, 0); + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_OUTPUTS_HASH_PERSONALIZATION); for (unsigned int n = 0; n < txTo.vout.size(); n++) { ss << txTo.vout[n]; } return ss.GetHash(); } +uint256 GetJoinSplitsHash(const CTransaction& txTo) { + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_JOINSPLITS_HASH_PERSONALIZATION); + for (unsigned int n = 0; n < txTo.vjoinsplit.size(); n++) { + ss << txTo.vjoinsplit[n]; + } + ss << txTo.joinSplitPubKey; + return ss.GetHash(); +} + } // anon namespace PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) @@ -1087,6 +1106,7 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) hashPrevouts = GetPrevoutHash(txTo); hashSequence = GetSequenceHash(txTo); hashOutputs = GetOutputsHash(txTo); + hashJoinSplits = GetJoinSplitsHash(txTo); } SigVersion SignatureHashVersion(const CTransaction& txTo) @@ -1118,6 +1138,7 @@ uint256 SignatureHash( uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; + uint256 hashJoinSplits; if (!(nHashType & SIGHASH_ANYONECANPAY)) { hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); @@ -1127,37 +1148,54 @@ uint256 SignatureHash( hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); } - if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { - CHashWriter ss(SER_GETHASH, 0); + CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_OUTPUTS_HASH_PERSONALIZATION); ss << txTo.vout[nIn]; hashOutputs = ss.GetHash(); } - CHashWriter ss(SER_GETHASH, 0); - // Version - ss << txTo.nVersion; + if (!txTo.vjoinsplit.empty()) { + hashJoinSplits = cache ? cache->hashJoinSplits : GetJoinSplitsHash(txTo); + } + + uint32_t leConsensusBranchId = htole32(consensusBranchId); + unsigned char personalization[16] = {}; + memcpy(personalization, "ZcashSigHash", 12); + memcpy(personalization+12, &leConsensusBranchId, 4); + + CBLAKE2bWriter ss(SER_GETHASH, 0, personalization); + // Header + ss << txTo.GetHeader(); + // Version group ID + ss << txTo.nVersionGroupId; // Input prevouts/nSequence (none/all, depending on flags) ss << hashPrevouts; ss << 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 contained in hashSequence. - if (nIn != NOT_AN_INPUT) - ss << txTo.vin[nIn].prevout; - ss << scriptCode; - ss << amount; - if (nIn != NOT_AN_INPUT) - ss << txTo.vin[nIn].nSequence; // Outputs (none/one/all, depending on flags) ss << hashOutputs; + // JoinSplits + ss << hashJoinSplits; // Locktime ss << txTo.nLockTime; + // Expiry height + ss << txTo.nExpiryHeight; // Sighash type ss << nHashType; + // If this hash is for a transparent input signature + // (i.e. not for txTo.joinSplitSig): + if (nIn != NOT_AN_INPUT){ + // 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 << txTo.vin[nIn].prevout; + ss << scriptCode; + ss << amount; + ss << txTo.vin[nIn].nSequence; + } + return ss.GetHash(); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 6f1408dfd..7f2956eec 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -90,7 +90,7 @@ enum struct PrecomputedTransactionData { - uint256 hashPrevouts, hashSequence, hashOutputs; + uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits; PrecomputedTransactionData(const CTransaction& tx); };