LCOV - code coverage report
Current view: top level - src/sapling - transaction_builder.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 214 236 90.7 %
Date: 2025-02-23 09:33:43 Functions: 24 25 96.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-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/transaction_builder.h"
       7             : 
       8             : #include "script/sign.h"
       9             : #include "utilmoneystr.h"
      10             : #include "consensus/upgrades.h"
      11             : #include "policy/policy.h"
      12             : #include "validation.h"
      13             : 
      14             : #include <librustzcash.h>
      15             : 
      16          99 : SpendDescriptionInfo::SpendDescriptionInfo(const libzcash::SaplingExpandedSpendingKey& _expsk,
      17             :                                            const libzcash::SaplingNote& _note,
      18             :                                            const uint256& _anchor,
      19          99 :                                            const SaplingWitness& _witness):
      20             :    expsk(_expsk),
      21             :    note(_note),
      22             :    anchor(_anchor),
      23         198 :    witness(_witness)
      24             : {
      25          99 :     librustzcash_sapling_generate_r(alpha.begin());
      26          99 : }
      27             : 
      28        1323 : Optional<OutputDescription> OutputDescriptionInfo::Build(void* ctx) {
      29        2646 :     auto cmu = this->note.cmu();
      30        1323 :     if (!cmu) {
      31           0 :         return nullopt;
      32             :     }
      33             : 
      34        2646 :     libzcash::SaplingNotePlaintext notePlaintext(this->note, this->memo);
      35             : 
      36        2646 :     auto res = notePlaintext.encrypt(this->note.pk_d);
      37        1323 :     if (!res) {
      38           0 :         return nullopt;
      39             :     }
      40        1323 :     auto enc = res.get();
      41        1323 :     auto encryptor = enc.second;
      42             : 
      43        1323 :     libzcash::SaplingPaymentAddress address(this->note.d, this->note.pk_d);
      44        2646 :     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
      45        1323 :     ss << address;
      46        2646 :     std::vector<unsigned char> addressBytes(ss.begin(), ss.end());
      47             : 
      48        1323 :     OutputDescription odesc;
      49        1323 :     if (!librustzcash_sapling_output_proof(
      50             :             ctx,
      51        1323 :             encryptor.get_esk().begin(),
      52        1323 :             addressBytes.data(),
      53        1323 :             this->note.r.begin(),
      54             :             this->note.value(),
      55             :             odesc.cv.begin(),
      56             :             odesc.zkproof.begin())) {
      57           0 :         return nullopt;
      58             :     }
      59             : 
      60        1323 :     odesc.cmu = *cmu;
      61        1323 :     odesc.ephemeralKey = encryptor.get_epk();
      62        1323 :     odesc.encCiphertext = enc.first;
      63             : 
      64        1323 :     libzcash::SaplingOutgoingPlaintext outPlaintext(this->note.pk_d, encryptor.get_esk());
      65        1323 :     odesc.outCiphertext = outPlaintext.encrypt(
      66        1323 :         this->ovk,
      67             :         odesc.cv,
      68             :         odesc.cmu,
      69        1323 :         encryptor);
      70             : 
      71        1323 :     return odesc;
      72             : }
      73             : 
      74             : // Dummy constants used during fee-calculation loop
      75         479 : static OutputDescription CreateDummyOD()
      76             : {
      77         479 :     OutputDescription dummyOD;
      78         479 :     dummyOD.cv = UINT256_MAX;
      79         479 :     dummyOD.cmu = UINT256_MAX;
      80         479 :     dummyOD.ephemeralKey = UINT256_MAX;
      81         479 :     dummyOD.encCiphertext = {{0xff}};
      82         479 :     dummyOD.outCiphertext = {{0xff}};
      83         479 :     dummyOD.zkproof = {{0xff}};
      84         479 :     return dummyOD;
      85             : }
      86         479 : static SpendDescription CreateDummySD()
      87             : {
      88         479 :     SpendDescription dummySD;
      89         479 :     dummySD.cv = UINT256_MAX;
      90         479 :     dummySD.anchor = UINT256_MAX;
      91         479 :     dummySD.nullifier = UINT256_MAX;
      92         479 :     dummySD.rk = UINT256_MAX;
      93         479 :     dummySD.zkproof = {{0xff}};
      94         479 :     dummySD.spendAuthSig = {{0xff}};
      95         479 :     return dummySD;
      96             : }
      97             : 
      98             : const OutputDescription DUMMY_SHIELD_OUT = CreateDummyOD();
      99             : const SpendDescription DUMMY_SHIELD_SPEND = CreateDummySD();
     100             : const SaplingTxData::binding_sig_t DUMMY_SHIELD_BINDSIG = {{0xff}};
     101             : 
     102             : 
     103        3373 : TransactionBuilderResult::TransactionBuilderResult(const CTransaction& tx) : maybeTx(tx) {}
     104             : 
     105           4 : TransactionBuilderResult::TransactionBuilderResult(const std::string& error) : maybeError(error) {}
     106             : 
     107           1 : bool TransactionBuilderResult::IsTx() { return maybeTx != nullopt; }
     108             : 
     109           0 : bool TransactionBuilderResult::IsError() { return maybeError != nullopt; }
     110             : 
     111         144 : CTransaction TransactionBuilderResult::GetTxOrThrow() {
     112         144 :     if (maybeTx) {
     113         144 :         return maybeTx.get();
     114             :     } else {
     115           0 :         throw std::runtime_error("Failed to build transaction: " + GetError());
     116             :     }
     117             : }
     118             : 
     119        3228 : Optional<CTransaction> TransactionBuilderResult::GetTx() {
     120        3228 :     return maybeTx;
     121             : }
     122             : 
     123           4 : std::string TransactionBuilderResult::GetError() {
     124           4 :     if (maybeError) {
     125           4 :         return maybeError.get();
     126             :     } else {
     127             :         // This can only happen if isTx() is true in which case we should not call getError()
     128           0 :         throw std::runtime_error("getError() was called in TransactionBuilderResult, but the result was not initialized as an error.");
     129             :     }
     130             : }
     131             : 
     132        1248 : TransactionBuilder::TransactionBuilder(
     133             :     const Consensus::Params& _consensusParams,
     134        1248 :     CKeyStore* _keystore) :
     135             :     consensusParams(_consensusParams),
     136        1248 :     keystore(_keystore)
     137             : {
     138        1248 :     Clear();
     139        1248 : }
     140             : 
     141        2302 : void TransactionBuilder::Clear()
     142             : {
     143        2302 :     mtx = CMutableTransaction();
     144        2302 :     mtx.nVersion = CTransaction::TxVersion::SAPLING;
     145        2302 :     spends.clear();
     146        2302 :     outputs.clear();
     147        2302 :     tIns.clear();
     148        2302 :     saplingChangeAddr = nullopt;
     149        2302 :     tChangeAddr = nullopt;
     150        2302 :     fee = -1;   // Verified in Build(). Must be set before.
     151        2302 : }
     152             : 
     153         100 : void TransactionBuilder::AddSaplingSpend(
     154             :     const libzcash::SaplingExpandedSpendingKey& expsk,
     155             :     const libzcash::SaplingNote& note,
     156             :     const uint256& anchor,
     157             :     const SaplingWitness& witness)
     158             : {
     159             :     // Sanity check: cannot add Sapling spend to pre-Sapling transaction
     160         100 :     if (mtx.nVersion < CTransaction::TxVersion::SAPLING) {
     161           0 :         throw std::runtime_error("TransactionBuilder cannot add Sapling spend to pre-Sapling transaction");
     162             :     }
     163             : 
     164             :     // Consistency check: all anchors must equal the first one
     165         100 :     if (spends.size() > 0 && spends[0].anchor != anchor) {
     166           1 :         throw std::runtime_error("Anchor does not match previously-added Sapling spends.");
     167             :     }
     168             : 
     169          99 :     spends.emplace_back(expsk, note, anchor, witness);
     170          99 :     mtx.sapData->valueBalance += note.value();
     171          99 : }
     172             : 
     173        2396 : void TransactionBuilder::AddSaplingOutput(
     174             :     const uint256& ovk,
     175             :     const libzcash::SaplingPaymentAddress& to,
     176             :     CAmount value,
     177             :     const std::array<unsigned char, ZC_MEMO_SIZE>& memo)
     178             : {
     179             :     // Sanity check: cannot add Sapling output to pre-Sapling transaction
     180        2396 :     if (mtx.nVersion < CTransaction::TxVersion::SAPLING) {
     181           0 :         throw std::runtime_error("TransactionBuilder cannot add Sapling output to pre-Sapling transaction");
     182             :     }
     183             : 
     184        2396 :     auto note = libzcash::SaplingNote(to, value);
     185        2396 :     outputs.emplace_back(ovk, note, memo);
     186        2396 :     mtx.sapData->valueBalance -= value;
     187        2396 : }
     188             : 
     189        2375 : void TransactionBuilder::AddTransparentInput(const COutPoint& utxo, const CScript& scriptPubKey, CAmount value)
     190             : {
     191        2375 :     if (keystore == nullptr) {
     192           1 :         throw std::runtime_error("Cannot add transparent inputs to a TransactionBuilder without a keystore");
     193             :     }
     194             : 
     195        2374 :     mtx.vin.emplace_back(utxo);
     196        2374 :     tIns.emplace_back(scriptPubKey, value);
     197        2374 : }
     198             : 
     199        2116 : void TransactionBuilder::AddTransparentOutput(const CTxOut& out)
     200             : {
     201        4232 :     std::vector<std::vector<unsigned char> > vSolutions;
     202        2116 :     txnouttype whichType;
     203        2116 :     if (!Solver(out.scriptPubKey, whichType, vSolutions))
     204           1 :         throw std::runtime_error("Transaction builder: invalid script for transparent output");
     205        2115 :     mtx.vout.push_back(out);
     206        2115 : }
     207             : 
     208        2078 : void TransactionBuilder::AddTransparentOutput(const CTxDestination& dest, CAmount value)
     209             : {
     210        4156 :     AddTransparentOutput(CTxOut(value, GetScriptForDestination(dest)));
     211        2077 : }
     212             : 
     213        2290 : void TransactionBuilder::SetFee(CAmount _fee)
     214             : {
     215        2290 :     this->fee = _fee;
     216        2290 : }
     217             : 
     218           1 : void TransactionBuilder::SendChangeTo(const libzcash::SaplingPaymentAddress& changeAddr, const uint256& ovk)
     219             : {
     220           1 :     saplingChangeAddr = std::make_pair(ovk, changeAddr);
     221           1 :     tChangeAddr = nullopt;
     222           1 : }
     223             : 
     224        2082 : void TransactionBuilder::SendChangeTo(const CTxDestination& changeAddr)
     225             : {
     226        2082 :     if (!IsValidDestination(changeAddr)) {
     227           1 :         throw std::runtime_error("Invalid change address, not a valid taddr.");
     228             :     }
     229             : 
     230        2081 :     tChangeAddr = changeAddr;
     231        2081 :     saplingChangeAddr = nullopt;
     232        2081 : }
     233             : 
     234        1231 : TransactionBuilderResult TransactionBuilder::ProveAndSign()
     235             : {
     236             :     //
     237             :     // Sapling spend descriptions
     238             :     //
     239        1231 :     if (!spends.empty() || !outputs.empty()) {
     240             : 
     241        1228 :         auto ctx = librustzcash_sapling_proving_ctx_init();
     242             : 
     243             :         // Create Sapling OutputDescriptions
     244        2551 :         for (auto output : outputs) {
     245             :             // Check this out here as well to provide better logging.
     246        1323 :             if (!output.note.cmu()) {
     247           0 :                 librustzcash_sapling_proving_ctx_free(ctx);
     248           0 :                 return TransactionBuilderResult("Output is invalid");
     249             :             }
     250             : 
     251        2646 :             auto odesc = output.Build(ctx);
     252        1323 :             if (!odesc) {
     253           0 :                 librustzcash_sapling_proving_ctx_free(ctx);
     254           0 :                 return TransactionBuilderResult("Failed to create output description");
     255             :             }
     256             : 
     257        1323 :             mtx.sapData->vShieldedOutput.push_back(odesc.get());
     258             :         }
     259             : 
     260             :         // Create Sapling SpendDescriptions
     261        1301 :         for (auto spend : spends) {
     262          73 :             auto cm = spend.note.cmu();
     263          73 :             auto nf = spend.note.nullifier(
     264         146 :                     spend.expsk.full_viewing_key(), spend.witness.position());
     265          73 :             if (!cm || !nf) {
     266           0 :                 librustzcash_sapling_proving_ctx_free(ctx);
     267           0 :                 return TransactionBuilderResult("Spend is invalid");
     268             :             }
     269             : 
     270         146 :             CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
     271         146 :             ss << spend.witness.path();
     272         146 :             std::vector<unsigned char> witness(ss.begin(), ss.end());
     273             : 
     274          73 :             SpendDescription sdesc;
     275          73 :             if (!librustzcash_sapling_spend_proof(
     276             :                     ctx,
     277          73 :                     spend.expsk.full_viewing_key().ak.begin(),
     278          73 :                     spend.expsk.nsk.begin(),
     279          73 :                     spend.note.d.data(),
     280          73 :                     spend.note.r.begin(),
     281          73 :                     spend.alpha.begin(),
     282             :                     spend.note.value(),
     283          73 :                     spend.anchor.begin(),
     284          73 :                     witness.data(),
     285             :                     sdesc.cv.begin(),
     286             :                     sdesc.rk.begin(),
     287             :                     sdesc.zkproof.data())) {
     288           0 :                 librustzcash_sapling_proving_ctx_free(ctx);
     289           0 :                 return TransactionBuilderResult("Spend proof failed");
     290             :             }
     291             : 
     292          73 :             sdesc.anchor = spend.anchor;
     293          73 :             sdesc.nullifier = *nf;
     294          73 :             mtx.sapData->vShieldedSpend.push_back(sdesc);
     295             :         }
     296             : 
     297             :         //
     298             :         // Signatures
     299             :         //
     300             : 
     301             :         // Empty output script.
     302        1228 :         uint256 dataToBeSigned;
     303        2456 :         CScript scriptCode;
     304        1228 :         try {
     305        1228 :             dataToBeSigned = SignatureHash(scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0, SIGVERSION_SAPLING);
     306           0 :         } catch (const std::logic_error& ex) {
     307           0 :             librustzcash_sapling_proving_ctx_free(ctx);
     308           0 :             return TransactionBuilderResult("Could not construct signature hash: " + std::string(ex.what()));
     309             :         }
     310             : 
     311             :         // Create Sapling spendAuth and binding signatures
     312        1301 :         for (size_t i = 0; i < spends.size(); i++) {
     313          73 :             librustzcash_sapling_spend_sig(
     314          73 :                     spends[i].expsk.ask.begin(),
     315          73 :                     spends[i].alpha.begin(),
     316          73 :                     dataToBeSigned.begin(),
     317          73 :                     mtx.sapData->vShieldedSpend[i].spendAuthSig.data());
     318             :         }
     319             : 
     320        2456 :         librustzcash_sapling_binding_sig(
     321             :                 ctx,
     322        1228 :                 mtx.sapData->valueBalance,
     323        1228 :                 dataToBeSigned.begin(),
     324        1228 :                 mtx.sapData->bindingSig.data());
     325             : 
     326        1228 :         librustzcash_sapling_proving_ctx_free(ctx);
     327             :     }
     328             : 
     329             :     // Transparent signatures
     330        2462 :     CTransaction txNewConst(mtx);
     331        2552 :     for (int nIn = 0; nIn < (int) mtx.vin.size(); nIn++) {
     332        2642 :         auto tIn = tIns[nIn];
     333        2642 :         SignatureData sigdata;
     334        1321 :         bool signSuccess = ProduceSignature(
     335        1321 :             TransactionSignatureCreator(
     336             :                 keystore, &txNewConst, nIn, tIn.value, SIGHASH_ALL),
     337             :             tIn.scriptPubKey, sigdata, SIGVERSION_SAPLING, false);
     338             : 
     339        1321 :         if (!signSuccess) {
     340           0 :             return TransactionBuilderResult("Failed to sign transaction");
     341             :         } else {
     342        1321 :             UpdateTransaction(mtx, nIn, sigdata);
     343             :         }
     344             :     }
     345             : 
     346        1231 :     return TransactionBuilderResult(CTransaction(mtx));
     347             : }
     348             : 
     349        2142 : TransactionBuilderResult TransactionBuilder::AddDummySignatures()
     350             : {
     351        2142 :     if (!spends.empty() || !outputs.empty()) {
     352             :         // Add Dummy Sapling OutputDescriptions
     353        4371 :         for (unsigned int i = 0; i < outputs.size(); i++) {
     354        2231 :             mtx.sapData->vShieldedOutput.push_back(DUMMY_SHIELD_OUT);
     355             :         }
     356             :         // Add Dummy Sapling SpendDescriptions
     357        2219 :         for (unsigned int i = 0; i < spends.size(); i++) {
     358          79 :             mtx.sapData->vShieldedSpend.push_back(DUMMY_SHIELD_SPEND);
     359             :         }
     360             :         // Add Dummy Binding sig
     361        2140 :         mtx.sapData->bindingSig = DUMMY_SHIELD_BINDSIG;
     362             :     }
     363             : 
     364             :     // Add Dummmy Transparent signatures
     365        4284 :     CTransaction txNewConst(mtx);
     366        4266 :     for (int nIn = 0; nIn < (int) mtx.vin.size(); nIn++) {
     367        4248 :         auto tIn = tIns[nIn];
     368        4248 :         SignatureData sigdata;
     369        2124 :         if (!ProduceSignature(DummySignatureCreator(keystore), tIn.scriptPubKey, sigdata, SIGVERSION_SAPLING, false)) {
     370           0 :             return TransactionBuilderResult("Failed to sign transaction");
     371             :         } else {
     372        2124 :             UpdateTransaction(mtx, nIn, sigdata);
     373             :         }
     374             :     }
     375             : 
     376        2142 :     return TransactionBuilderResult(CTransaction(mtx));
     377             : }
     378             : 
     379        1086 : void TransactionBuilder::ClearProofsAndSignatures()
     380             : {
     381             :     // Clear Sapling output descriptions
     382        1086 :     mtx.sapData->vShieldedOutput.clear();
     383             : 
     384             :     // Clear Sapling spend descriptions
     385        1086 :     mtx.sapData->vShieldedSpend.clear();
     386             : 
     387             :     // Clear Binding sig
     388        1086 :     mtx.sapData->bindingSig = {{0}};
     389             : 
     390             :     // Clear Transparent signatures
     391        2158 :     for (CTxIn& in : mtx.vin) in.scriptSig = CScript();
     392        1086 : }
     393             : 
     394        2291 : TransactionBuilderResult TransactionBuilder::Build(bool fDummySig)
     395             : {
     396             :     //
     397             :     // Consistency checks
     398             :     //
     399             :     // Valid fee
     400        2291 :     if (fee < 0) {
     401           0 :         return TransactionBuilderResult("Fee cannot be negative");
     402             :     }
     403             : 
     404             :     // Valid change
     405        2291 :     CAmount change = mtx.sapData->valueBalance - fee;
     406        4665 :     for (auto& tIn : tIns) {
     407        2374 :         change += tIn.value;
     408             :     }
     409        2333 :     for (auto& tOut : mtx.vout) {
     410          42 :         change -= tOut.nValue;
     411             :     }
     412        2291 :     if (change < 0) {
     413           6 :         return TransactionBuilderResult("Change cannot be negative");
     414             :     }
     415             : 
     416             :     //
     417             :     // Change output
     418             :     //
     419             : 
     420        2288 :     if (change > 0) {
     421             :         // If we get here and the change is dust, add it to the fee
     422        2149 :         CAmount dustThreshold = (spends.empty() && outputs.empty()) ? GetDustThreshold(dustRelayFee)
     423        2144 :                                                                     : GetShieldedDustThreshold(dustRelayFee);
     424        2149 :         if (change > dustThreshold) {
     425             :             // Send change to the specified change address. If no change address
     426             :             // was set, send change to the first Sapling address given as input
     427             :             // (A t-address can only be used as the change address if explicitly set.)
     428        2148 :             if (saplingChangeAddr) {
     429           1 :                 AddSaplingOutput(saplingChangeAddr->first, saplingChangeAddr->second, change);
     430        2147 :             } else if (tChangeAddr) {
     431             :                 // tChangeAddr has already been validated.
     432        2075 :                 AddTransparentOutput(*tChangeAddr, change);
     433          72 :             } else if (!spends.empty()) {
     434          71 :                 auto fvk = spends[0].expsk.full_viewing_key();
     435          71 :                 auto note = spends[0].note;
     436          71 :                 libzcash::SaplingPaymentAddress changeAddr(note.d, note.pk_d);
     437          71 :                 AddSaplingOutput(fvk.ovk, changeAddr, change);
     438             :             } else {
     439           2 :                 return TransactionBuilderResult("Could not determine change address");
     440             :             }
     441             :         } else {
     442             :             // Not used after, but update for consistency
     443           1 :             fee += change;
     444           1 :             change = 0;
     445             :         }
     446             :     }
     447             : 
     448        2287 :     return fDummySig ? AddDummySignatures() : ProveAndSign();
     449             : }

Generated by: LCOV version 1.14