diff --git a/src/script/interpreter.h b/src/script/interpreter.h --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -113,7 +113,8 @@ ScriptError *serror); uint256 SignatureHash(const CScript &scriptCode, const CTransaction &txTo, - unsigned int nIn, uint32_t nHashType, const Amount amount, + unsigned int nIn, SigHashType sigHashType, + const Amount amount, const PrecomputedTransactionData *cache = nullptr, uint32_t flags = SCRIPT_ENABLE_SIGHASH_FORKID); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -185,21 +185,20 @@ return true; } -static uint32_t GetHashType(const valtype &vchSig) { +static SigHashType GetHashType(const valtype &vchSig) { if (vchSig.size() == 0) { - return 0; + return SigHashType(0); } - return vchSig[vchSig.size() - 1]; + return SigHashType(vchSig[vchSig.size() - 1]); } static void CleanupScriptCode(CScript &scriptCode, const std::vector &vchSig, uint32_t flags) { // Drop the signature in scripts when SIGHASH_FORKID is not used. - uint32_t nHashType = GetHashType(vchSig); - if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID) || - !(nHashType & SIGHASH_FORKID)) { + SigHashType sigHashType = GetHashType(vchSig); + if (!(flags & SCRIPT_ENABLE_SIGHASH_FORKID) || !sigHashType.hasForkId()) { scriptCode.FindAndDelete(CScript(vchSig)); } } @@ -208,9 +207,7 @@ if (vchSig.size() == 0) { return false; } - uint32_t nHashType = - GetHashType(vchSig) & ~(SIGHASH_ANYONECANPAY | SIGHASH_FORKID); - if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) { + if (!GetHashType(vchSig).hasSupportedBaseSigHashType()) { return false; } @@ -238,7 +235,7 @@ if (!IsDefinedHashtypeSignature(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE); } - bool usesForkId = GetHashType(vchSig) & SIGHASH_FORKID; + bool usesForkId = GetHashType(vchSig).hasForkId(); bool forkIdEnabled = flags & SCRIPT_ENABLE_SIGHASH_FORKID; if (!forkIdEnabled && usesForkId) { return set_error(serror, SCRIPT_ERR_ILLEGAL_FORKID); @@ -1349,29 +1346,30 @@ } uint256 SignatureHash(const CScript &scriptCode, const CTransaction &txTo, - unsigned int nIn, uint32_t nHashType, const Amount amount, + unsigned int nIn, SigHashType sigHashType, + const Amount amount, const PrecomputedTransactionData *cache, uint32_t flags) { - if ((nHashType & SIGHASH_FORKID) && - (flags & SCRIPT_ENABLE_SIGHASH_FORKID)) { + if (sigHashType.hasForkId() && (flags & SCRIPT_ENABLE_SIGHASH_FORKID)) { uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; - if (!(nHashType & SIGHASH_ANYONECANPAY)) { + if (!sigHashType.hasAnyoneCanPay()) { hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); } - if (!(nHashType & SIGHASH_ANYONECANPAY) && - (nHashType & 0x1f) != SIGHASH_SINGLE && - (nHashType & 0x1f) != SIGHASH_NONE) { + if (!sigHashType.hasAnyoneCanPay() && + (sigHashType.getBaseSigHashType() != BaseSigHashType::SINGLE) && + (sigHashType.getBaseSigHashType() != BaseSigHashType::NONE)) { hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); } - if ((nHashType & 0x1f) != SIGHASH_SINGLE && - (nHashType & 0x1f) != SIGHASH_NONE) { + if ((sigHashType.getBaseSigHashType() != BaseSigHashType::SINGLE) && + (sigHashType.getBaseSigHashType() != BaseSigHashType::NONE)) { hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); - } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && - nIn < txTo.vout.size()) { + } else if ((sigHashType.getBaseSigHashType() == + BaseSigHashType::SINGLE) && + (nIn < txTo.vout.size())) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; hashOutputs = ss.GetHash(); @@ -1395,7 +1393,7 @@ // Locktime ss << txTo.nLockTime; // Sighash type - ss << nHashType; + ss << sigHashType; return ss.GetHash(); } @@ -1408,20 +1406,20 @@ } // Check for invalid use of SIGHASH_SINGLE - if ((nHashType & 0x1f) == SIGHASH_SINGLE) { - if (nIn >= txTo.vout.size()) { - // nOut out of range - return one; - } + if ((sigHashType.getBaseSigHashType() == BaseSigHashType::SINGLE) && + (nIn >= txTo.vout.size())) { + // nOut out of range + return one; } // Wrapper to serialize only the necessary parts of the transaction being // signed - CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); + CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, + sigHashType.getRawSigHashType()); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); - ss << txTmp << nHashType; + ss << txTmp << sigHashType; return ss.GetHash(); } @@ -1444,10 +1442,10 @@ if (vchSig.empty()) { return false; } - uint32_t nHashType = GetHashType(vchSig); + SigHashType sigHashType = GetHashType(vchSig); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, sigHashType, amount, this->txdata, flags); if (!VerifySignature(vchSig, pubkey, sighash)) { diff --git a/src/script/sighashtype.h b/src/script/sighashtype.h --- a/src/script/sighashtype.h +++ b/src/script/sighashtype.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_SCRIPT_HASH_TYPE_H #define BITCOIN_SCRIPT_HASH_TYPE_H +#include "serialize.h" + #include #include @@ -17,8 +19,15 @@ SIGHASH_ANYONECANPAY = 0x80, }; -/** Base signature hash types */ +/** + * Base signature hash types + * Base sig hash types not defined in this enum may be used, but they will be + * represented as UNSUPPORTED. See transaction + * c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73 for an + * example where an unsupported base sig hash of 0 was used. + */ enum class BaseSigHashType : uint32_t { + UNSUPPORTED = 0, ALL = SIGHASH_ALL, NONE = SIGHASH_NONE, SINGLE = SIGHASH_SINGLE @@ -32,12 +41,7 @@ public: explicit SigHashType() : sigHash(SIGHASH_ALL) {} - explicit SigHashType(uint32_t sigHashIn) : sigHash(sigHashIn) { - if (((sigHash & 0x1f) < SIGHASH_ALL) || - ((sigHash & 0x1f) > SIGHASH_SINGLE)) { - throw std::runtime_error("Base sighash must be specified"); - } - } + explicit SigHashType(uint32_t sigHashIn) : sigHash(sigHashIn) {} SigHashType withBaseSigHash(BaseSigHashType baseSigHashType) const { return SigHashType((sigHash & ~0x1f) | uint32_t(baseSigHashType)); @@ -57,6 +61,12 @@ return BaseSigHashType(sigHash & 0x1f); } + bool hasSupportedBaseSigHashType() const { + BaseSigHashType baseType = getBaseSigHashType(); + return baseType >= BaseSigHashType::ALL && + baseType <= BaseSigHashType::SINGLE; + } + bool hasForkId() const { return (sigHash & SIGHASH_FORKID) == SIGHASH_FORKID; } @@ -66,6 +76,10 @@ } uint32_t getRawSigHashType() const { return sigHash; } + + template void Serialize(Stream &s) const { + ::Serialize(s, getRawSigHashType()); + } }; #endif // BITCOIN_SCRIPT_HASH_TYPE_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -29,8 +29,7 @@ return false; } - uint256 hash = SignatureHash(scriptCode, *txTo, nIn, - sigHashType.getRawSigHashType(), amount); + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, sigHashType, amount); if (!key.Sign(hash, vchSig)) { return false; } diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -9,6 +9,7 @@ #include "script/ismine.h" #include "script/script.h" #include "script/script_error.h" +#include "script/sighashtype.h" #include "script/sign.h" #include "test/test_bitcoin.h" #include "uint256.h" @@ -22,7 +23,7 @@ CScript sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transaction, int whichIn) { uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, - SIGHASH_ALL, Amount(0)); + SigHashType(), Amount(0)); CScript result; // CHECKMULTISIG bug workaround diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -10,6 +10,7 @@ #include "rpc/server.h" #include "script/script.h" #include "script/script_error.h" +#include "script/sighashtype.h" #include "script/sign.h" #include "test/scriptflags.h" #include "test/sigutil.h" @@ -311,7 +312,8 @@ TestBuilder &PushSig(const CKey &key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, Amount amount = Amount(0)) { - uint256 hash = SignatureHash(script, spendTx, 0, nHashType, amount); + uint256 hash = + SignatureHash(script, spendTx, 0, SigHashType(nHashType), amount); std::vector vchSig, r, s; uint32_t iter = 0; do { @@ -1151,7 +1153,7 @@ CScript sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transaction) { uint256 hash = - SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, Amount(0)); + SignatureHash(scriptPubKey, transaction, 0, SigHashType(), Amount(0)); CScript result; // @@ -1426,17 +1428,19 @@ // A couple of partially-signed versions: std::vector sig1; uint256 hash1 = - SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, Amount(0)); + SignatureHash(scriptPubKey, txTo, 0, SigHashType(), Amount(0)); BOOST_CHECK(keys[0].Sign(hash1, sig1)); sig1.push_back(SIGHASH_ALL); std::vector sig2; - uint256 hash2 = - SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, Amount(0)); + uint256 hash2 = SignatureHash( + scriptPubKey, txTo, 0, + SigHashType().withBaseSigHash(BaseSigHashType::NONE), Amount(0)); BOOST_CHECK(keys[1].Sign(hash2, sig2)); sig2.push_back(SIGHASH_NONE); std::vector sig3; - uint256 hash3 = - SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, Amount(0)); + uint256 hash3 = SignatureHash( + scriptPubKey, txTo, 0, + SigHashType().withBaseSigHash(BaseSigHashType::SINGLE), Amount(0)); BOOST_CHECK(keys[2].Sign(hash3, sig3)); sig3.push_back(SIGHASH_SINGLE); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -143,7 +143,8 @@ uint256 sh, sho; sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType); - sh = SignatureHash(scriptCode, txTo, nIn, nHashType, Amount(0)); + sh = SignatureHash(scriptCode, txTo, nIn, SigHashType(nHashType), + Amount(0)); #if defined(PRINT_SIGHASH_JSON) CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << txTo; @@ -210,7 +211,8 @@ continue; } - sh = SignatureHash(scriptCode, *tx, nIn, nHashType, Amount(0)); + sh = SignatureHash(scriptCode, *tx, nIn, SigHashType(nHashType), + Amount(0)); BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); } } diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -10,6 +10,7 @@ #include "pubkey.h" #include "random.h" #include "script/scriptcache.h" +#include "script/sighashtype.h" #include "script/sign.h" #include "script/standard.h" #include "test/sigutil.h" @@ -53,7 +54,7 @@ // Sign: std::vector vchSig; uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, - SIGHASH_ALL | SIGHASH_FORKID, + SigHashType().withForkId(true), coinbaseTxns[0].vout[0].nValue); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); @@ -191,7 +192,7 @@ { std::vector vchSig; uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, - SIGHASH_ALL | SIGHASH_FORKID, + SigHashType().withForkId(true), coinbaseTxns[0].vout[0].nValue); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); @@ -274,7 +275,7 @@ std::vector vchSig; uint256 hash = SignatureHash( spend_tx.vout[1].scriptPubKey, invalid_with_cltv_tx, 0, - SIGHASH_ALL | SIGHASH_FORKID, spend_tx.vout[1].nValue); + SigHashType().withForkId(true), spend_tx.vout[1].nValue); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; @@ -309,7 +310,7 @@ std::vector vchSig; uint256 hash = SignatureHash( spend_tx.vout[2].scriptPubKey, invalid_with_csv_tx, 0, - SIGHASH_ALL | SIGHASH_FORKID, spend_tx.vout[2].nValue); + SigHashType().withForkId(true), spend_tx.vout[2].nValue); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;