LCOV - code coverage report
Current view: top level - src/sapling - sapling_validation.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 54 74 73.0 %
Date: 2025-02-23 09:33:43 Functions: 3 3 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2016-2020 The ZCash developers
       2             : // Copyright (c) 2020-2021 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "sapling/sapling_validation.h"
       7             : 
       8             : #include "consensus/consensus.h" // for MAX_BLOCK_SIZE_CURRENT
       9             : #include "script/interpreter.h" // for SigHash
      10             : #include "consensus/validation.h" // for CValidationState
      11             : #include "util/system.h" // for error()
      12             : #include "consensus/upgrades.h" // for CurrentEpochBranchId()
      13             : 
      14             : #include <librustzcash.h>
      15             : 
      16             : namespace SaplingValidation {
      17             : 
      18             : // Verifies that Shielded txs are properly formed and performs content-independent checks
      19      891269 : bool CheckTransaction(const CTransaction& tx, CValidationState& state, CAmount& nValueOut)
      20             : {
      21      891269 :     bool hasSaplingData = tx.hasSaplingData();
      22             : 
      23             :     // v1 must not have shielded data.
      24      891269 :     if (!tx.isSaplingVersion() && hasSaplingData) {
      25           0 :         return state.DoS(100, error("%s: Not Sapling version with Sapling data", __func__ ),
      26             :                          REJECT_INVALID, "bad-txns-form-not-sapling");
      27             :     }
      28             : 
      29             :     // if the tx has no shielded data, return true. No check needed.
      30      891269 :     if (!hasSaplingData) {
      31             :         return true;
      32             :     }
      33             : 
      34             :     // From here, all of the checks are done in v3+ transactions.
      35             : 
      36             :     // if the tx has shielded data, cannot be a coinstake, coinbase, zcspend and zcmint
      37        5072 :     if (tx.IsCoinStake() || tx.IsCoinBase() || tx.HasZerocoinSpendInputs() || tx.HasZerocoinMintOutputs())
      38           6 :         return state.DoS(100, error("%s: Sapling version with invalid data", __func__),
      39             :                          REJECT_INVALID, "bad-txns-invalid-sapling");
      40             : 
      41             :     // Upgrade enforced, basic version rules passing, let's check it
      42        5070 :     return CheckTransactionWithoutProofVerification(tx, state, nValueOut);
      43             : }
      44             : 
      45        5072 : bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState& state, CAmount& nValueOut)
      46             : {
      47             :     // Basic checks that don't depend on any context
      48        5072 :     const Consensus::Params& consensus = Params().GetConsensus();
      49             :     // If the tx got to this point, must be v3+.
      50        5072 :     assert(tx.isSaplingVersion());
      51             : 
      52             :     // Check for non-zero valueBalance when there are no Sapling inputs or outputs
      53        5072 :     if (tx.sapData->vShieldedSpend.empty() && tx.sapData->vShieldedOutput.empty() && tx.sapData->valueBalance != 0) {
      54           0 :         return state.DoS(100, error("%s: tx.sapData->valueBalance has no sources or sinks", __func__ ),
      55             :                          REJECT_INVALID, "bad-txns-valuebalance-nonzero");
      56             :     }
      57             : 
      58             :     // Check for overflow valueBalance
      59        5072 :     if (tx.sapData->valueBalance > consensus.nMaxMoneyOut || tx.sapData->valueBalance < -consensus.nMaxMoneyOut) {
      60           0 :         return state.DoS(100, error("%s: abs(tx.sapData->valueBalance) too large", __func__),
      61             :                          REJECT_INVALID, "bad-txns-valuebalance-toolarge");
      62             :     }
      63             : 
      64        5072 :     if (tx.sapData->valueBalance < 0) {
      65             :         // NB: negative valueBalance "takes" money from the transparent value pool just as outputs do
      66        4791 :         nValueOut += -tx.sapData->valueBalance;
      67             : 
      68        4791 :         if (!consensus.MoneyRange(nValueOut)) {
      69           0 :             return state.DoS(100, error("%s: txout total out of range", __func__ ),
      70             :                              REJECT_INVALID, "bad-txns-txouttotal-toolarge");
      71             :         }
      72             :     }
      73             : 
      74             :     // Ensure input values do not exceed consensus.nMaxMoneyOut
      75             :     // We have not resolved the txin values at this stage,
      76             :     // but we do know what the shielded tx claim to add
      77             :     // to the value pool.
      78             : 
      79             :     // NB: positive valueBalance "adds" money to the transparent value pool, just as inputs do
      80        5072 :     if (tx.sapData->valueBalance > 0 && !consensus.MoneyRange(tx.sapData->valueBalance)) {
      81           0 :         return state.DoS(100, error("%s: txin total out of range", __func__ ),
      82             :                          REJECT_INVALID, "bad-txns-txintotal-toolarge");
      83             :     }
      84             : 
      85             :     // Check for duplicate sapling nullifiers in this transaction
      86        5072 :     {
      87        5072 :         std::set<uint256> vSaplingNullifiers;
      88        5463 :         for (const SpendDescription& spend_desc : tx.sapData->vShieldedSpend) {
      89         392 :             if (vSaplingNullifiers.count(spend_desc.nullifier))
      90           3 :                 return state.DoS(100, error("%s: duplicate nullifiers", __func__ ),
      91             :                                  REJECT_INVALID, "bad-spend-description-nullifiers-duplicate");
      92             : 
      93         391 :             vSaplingNullifiers.insert(spend_desc.nullifier);
      94             :         }
      95             :     }
      96             : 
      97        5071 :     return true;
      98             : }
      99             : 
     100             : /**
     101             : * Check a transaction contextually against a set of consensus rules valid at a given block height.
     102             : *
     103             : * Notes:
     104             : * 1. AcceptToMemoryPool calls CheckTransaction and this function.
     105             : * 2. ProcessNewBlock calls AcceptBlock, which calls CheckBlock (which calls CheckTransaction)
     106             : *    and ContextualCheckBlock (which calls this function).
     107             : * 3. For consensus rules that relax restrictions (where a transaction that is invalid at
     108             : *    nHeight can become valid at a later height), we make the bans conditional on not
     109             : *    being in Initial Block Download mode.
     110             : * 4. The isInitBlockDownload argument is a function parameter to assist with testing.
     111             : *
     112             : */
     113      663443 : bool ContextualCheckTransaction(
     114             :         const CTransaction& tx,
     115             :         CValidationState &state,
     116             :         const CChainParams& chainparams,
     117             :         const int nHeight,
     118             :         const bool isMined,
     119             :         bool isInitBlockDownload)
     120             : {
     121      663443 :     const int DOS_LEVEL_BLOCK = 100;
     122             :     // DoS level set to 10 to be more forgiving.
     123      663443 :     const int DOS_LEVEL_MEMPOOL = 10;
     124             : 
     125             :     // For constricting rules, we don't need to account for IBD mode.
     126      663443 :     auto dosLevelConstricting = isMined ? DOS_LEVEL_BLOCK : DOS_LEVEL_MEMPOOL;
     127             :     // For rules that are relaxing (or might become relaxing when a future
     128             :     // network upgrade is implemented), we need to account for IBD mode.
     129      663443 :     auto dosLevelPotentiallyRelaxing = isMined ? DOS_LEVEL_BLOCK : (
     130      178059 :             isInitBlockDownload ? 0 : DOS_LEVEL_MEMPOOL);
     131             : 
     132             :     // If Sapling is not active return quickly and don't perform any check here.
     133             :     // basic data checks are performed in CheckTransaction which is ALWAYS called before ContextualCheckTransaction.
     134      663443 :     if (!chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V5_0)) {
     135             :         // If the v5 upgrade was not enforced, then let's not perform any check
     136      622366 :         if (tx.IsShieldedTx()) {
     137           0 :             return state.DoS(dosLevelConstricting, error("%s: Sapling not activated", __func__),
     138             :                              REJECT_INVALID, "bad-txns-invalid-sapling-act");
     139             :         }
     140      622366 :         return true;
     141             :     }
     142             : 
     143             :     // Reject transactions with invalid version
     144       41077 :     if (!tx.isSaplingVersion() && tx.hasSaplingData()) {
     145           0 :         return state.DoS(
     146             :                 dosLevelConstricting,
     147           0 :                 error("%s: Sapling version too low", __func__ ),
     148             :                 REJECT_INVALID, "bad-tx-sapling-version-too-low");
     149             :     }
     150             : 
     151       41077 :     bool hasShieldedData = tx.hasSaplingData();
     152             :     // A coinbase/coinstake transaction cannot have output descriptions nor shielded spends
     153       41077 :     if (tx.IsCoinBase() || tx.IsCoinStake()) {
     154       31937 :         if (hasShieldedData)
     155           0 :             return state.DoS(
     156             :                     dosLevelPotentiallyRelaxing,
     157           0 :                     error("%s: coinbase/coinstake has output/spend descriptions", __func__ ),
     158             :                     REJECT_INVALID, "bad-cs-has-shielded-data");
     159             :     }
     160             : 
     161       41077 :     if (hasShieldedData) {
     162        4338 :         uint256 dataToBeSigned;
     163             : 
     164        4338 :         if (tx.HasExchangeAddr() && Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V5_6)) {
     165           3 :             return state.DoS(100, error("%s: Sapling version with invalid data", __func__),
     166             :                 REJECT_INVALID, "bad-txns-exchange-addr-has-sapling");
     167             :         }
     168             : 
     169             :         // Empty output script.
     170        8674 :         CScript scriptCode;
     171        4337 :         try {
     172        4337 :             dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, SIGVERSION_SAPLING);
     173           0 :         } catch (const std::logic_error& ex) {
     174             :             // A logic error should never occur because we pass NOT_AN_INPUT and
     175             :             // SIGHASH_ALL to SignatureHash().
     176           0 :             return state.DoS(100, error("%s: error computing signature hash", __func__ ),
     177             :                              REJECT_INVALID, "error-computing-signature-hash");
     178             :         }
     179             : 
     180             :         // Sapling verification process
     181        4337 :         auto ctx = librustzcash_sapling_verification_ctx_init();
     182             : 
     183        4674 :         for (const SpendDescription &spend : tx.sapData->vShieldedSpend) {
     184         337 :             if (!librustzcash_sapling_check_spend(
     185             :                     ctx,
     186             :                     spend.cv.begin(),
     187             :                     spend.anchor.begin(),
     188             :                     spend.nullifier.begin(),
     189             :                     spend.rk.begin(),
     190             :                     spend.zkproof.begin(),
     191             :                     spend.spendAuthSig.begin(),
     192         337 :                     dataToBeSigned.begin())) {
     193           0 :                 librustzcash_sapling_verification_ctx_free(ctx);
     194           0 :                 return state.DoS(
     195             :                         dosLevelPotentiallyRelaxing,
     196           0 :                         error("%s: Sapling spend description invalid", __func__ ),
     197             :                         REJECT_INVALID, "bad-txns-sapling-spend-description-invalid");
     198             :             }
     199             :         }
     200             : 
     201        9115 :         for (const OutputDescription &output : tx.sapData->vShieldedOutput) {
     202        4778 :             if (!librustzcash_sapling_check_output(
     203             :                     ctx,
     204             :                     output.cv.begin(),
     205             :                     output.cmu.begin(),
     206             :                     output.ephemeralKey.begin(),
     207             :                     output.zkproof.begin())) {
     208           0 :                 librustzcash_sapling_verification_ctx_free(ctx);
     209             :                 // This should be a non-contextual check, but we check it here
     210             :                 // as we need to pass over the outputs anyway in order to then
     211             :                 // call librustzcash_sapling_final_check().
     212           0 :                 return state.DoS(100, error("%s: Sapling output description invalid", __func__ ),
     213             :                                  REJECT_INVALID, "bad-txns-sapling-output-description-invalid");
     214             :             }
     215             :         }
     216             : 
     217        8674 :         if (!librustzcash_sapling_final_check(
     218             :                 ctx,
     219        4337 :                 tx.sapData->valueBalance,
     220        4337 :                 tx.sapData->bindingSig.begin(),
     221        4337 :                 dataToBeSigned.begin())) {
     222           0 :             librustzcash_sapling_verification_ctx_free(ctx);
     223           0 :             return state.DoS(
     224             :                     dosLevelPotentiallyRelaxing,
     225           0 :                     error("%s: Sapling binding signature invalid", __func__ ),
     226             :                     REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid");
     227             :         }
     228             : 
     229        4337 :         librustzcash_sapling_verification_ctx_free(ctx);
     230             :     }
     231             :     return true;
     232             : }
     233             : 
     234             : 
     235             : } // End SaplingValidation namespace

Generated by: LCOV version 1.14