diff --git a/src/main.cpp b/src/main.cpp index d11ca5bae..cbdd8a18d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -792,6 +792,7 @@ bool ContextualCheckTransaction( bool saplingActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING); bool isSprout = !overwinterActive; bool heartwoodActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD); + bool canopyActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY); // If Sprout rules apply, reject transactions which are intended for Overwinter and beyond if (isSprout && tx.fOverwintered) { @@ -919,19 +920,29 @@ bool ContextualCheckTransaction( } // SaplingNotePlaintext::decrypt() checks note commitment validity. - if (!SaplingNotePlaintext::decrypt( + auto encPlaintext = SaplingNotePlaintext::decrypt( output.encCiphertext, output.ephemeralKey, outPlaintext->esk, outPlaintext->pk_d, - output.cmu) - ) { + output.cmu); + if (!encPlaintext) { return state.DoS( DOS_LEVEL_BLOCK, error("CheckTransaction(): coinbase output description has invalid encCiphertext"), REJECT_INVALID, "bad-cb-output-desc-invalid-encct"); } + + // ZIP 212: Check that the note plaintexts use the v2 note plaintext + // version. + if (canopyActive != (encPlaintext->get_lead_byte() == 0x02)) { + return state.DoS( + DOS_LEVEL_BLOCK, + error("CheckTransaction(): coinbase output description has invalid note plaintext version"), + REJECT_INVALID, + "bad-cb-output-desc-invalid-note-plaintext-version"); + } } } } diff --git a/src/zcash/Note.hpp b/src/zcash/Note.hpp index a130267a7..a56a2310e 100644 --- a/src/zcash/Note.hpp +++ b/src/zcash/Note.hpp @@ -171,6 +171,9 @@ public: uint256 rcm() const; uint256 generate_esk() const; + bool get_lead_byte() const { + return leadByte; + } }; class SaplingOutgoingPlaintext