diff --git a/src/carrot_core/output_set_finalization.cpp b/src/carrot_core/output_set_finalization.cpp index 38ccb5c84..9f94f97e2 100644 --- a/src/carrot_core/output_set_finalization.cpp +++ b/src/carrot_core/output_set_finalization.cpp @@ -160,6 +160,7 @@ void get_output_enote_proposals(const std::vector &norm const crypto::key_image &tx_first_key_image, std::vector &output_enote_proposals_out, encrypted_payment_id_t &encrypted_payment_id_out, + size_t change_index_out, std::vector> *payment_proposal_order_out) { output_enote_proposals_out.clear(); @@ -240,6 +241,7 @@ void get_output_enote_proposals(const std::vector &norm } // construct selfsend enotes, preferring internal enotes over special enotes when possible + crypto::public_key change_address; for (size_t i = 0; i < num_selfsend_proposals; ++i) { const CarrotPaymentProposalSelfSendV1 &selfsend_payment_proposal = selfsend_payment_proposals.at(i); @@ -267,6 +269,11 @@ void get_output_enote_proposals(const std::vector &norm { CARROT_THROW(std::invalid_argument, "neither a view-balance nor view-incoming device was provided"); } + + if (selfsend_payment_proposal.enote_type == CarrotEnoteType::CHANGE) + { + change_address = output_entry.first.enote.onetime_address; + } } // sort enotes by K_o @@ -274,6 +281,16 @@ void get_output_enote_proposals(const std::vector &norm { return a.first.enote.onetime_address < b.first.enote.onetime_address; }; std::sort(sortable_data.begin(), sortable_data.end(), sort_output_enote_proposal); + // get the change index + for (size_t i = 0; i < sortable_data.size(); ++i) + { + if (sortable_data[i].first.enote.onetime_address == change_address) + { + change_index_out = i; + break; + } + } + // collect output_enote_proposals_out and payment_proposal_order_out output_enote_proposals_out.reserve(num_proposals); if (payment_proposal_order_out) diff --git a/src/carrot_core/output_set_finalization.h b/src/carrot_core/output_set_finalization.h index 1e37340c8..d27afdc8e 100644 --- a/src/carrot_core/output_set_finalization.h +++ b/src/carrot_core/output_set_finalization.h @@ -110,6 +110,7 @@ void get_output_enote_proposals(const std::vector &norm const crypto::key_image &tx_first_key_image, std::vector &output_enote_proposals_out, encrypted_payment_id_t &encrypted_payment_id_out, + size_t change_index_out = 0, std::vector> *payment_proposal_order_out = nullptr); /** * brief: get_coinbase_output_enotes - convert a *finalized* set of payment proposals into coinbase output enotes diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 347e122dc..1bb6a4c34 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1500,7 +1500,7 @@ namespace rct { //RCT simple //for post-rct only rctSig genRctSimpleCarrot( - const key & message, + const key &message, const carrot_ctkeyV & inSk, const keyV & destinations, const cryptonote::transaction_type tx_type, @@ -1510,7 +1510,6 @@ namespace rct { const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, - const keyV &amount_keys, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, @@ -1518,144 +1517,98 @@ namespace rct { const rct::salvium_data_t &salvium_data, const key &x_change, const key &y_change, - const size_t change_index, - const key &key_yF + const size_t change_index ) { - CHECK_AND_ASSERT_THROW_MES(rct_config.range_proof_type == RangeProofPaddedBulletproof, "Borromean range proofs no longer supported"); - CHECK_AND_ASSERT_THROW_MES(destination_asset_types.size() == destinations.size(), "Different number of amount_keys/destinations"); - CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); - CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); - CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); - CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); - CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk"); - CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk"); - for (size_t n = 0; n < mixRing.size(); ++n) { - CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing"); - } + CHECK_AND_ASSERT_THROW_MES(rct_config.range_proof_type == RangeProofPaddedBulletproof, "Borromean range proofs no longer supported"); + CHECK_AND_ASSERT_THROW_MES(destination_asset_types.size() == destinations.size(), "Different number of amount_keys/destinations"); + CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); + CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); + CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); + CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk"); + CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk"); + for (size_t n = 0; n < mixRing.size(); ++n) { + CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing"); + } - rctSig rv; - switch (rct_config.bp_version) - { - case 0: - case 6: - rv.type = RCTTypeSalviumOne; - break; - default: - ASSERT_MES_AND_THROW("Unsupported BP version: " << rct_config.bp_version); - } + rctSig rv; + switch (rct_config.bp_version) + { + case 0: + case 6: + rv.type = RCTTypeSalviumOne; + break; + default: + ASSERT_MES_AND_THROW("Unsupported BP version: " << rct_config.bp_version); + } - rv.message = message; - rv.outPk.resize(destinations.size()); - rv.ecdhInfo.resize(destinations.size()); + rv.message = message; + rv.mixRing = mixRing; + keyV &pseudoOuts = rv.p.pseudoOuts; + pseudoOuts.resize(inamounts.size()); + rv.p.TCLSAGs.resize(inamounts.size()); + key sumpouts = zero(); //sum pseudoOut masks + keyV a(inamounts.size()); - size_t i; - keyV masks(destinations.size()); //sk mask.. - outSk.resize(destinations.size()); - for (i = 0; i < destinations.size(); i++) { + key sumout = zero(); + for (size_t i = 0; i < outSk.size(); ++i) + { + sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); + } - //add destination to sig - rv.outPk[i].dest = copy(destinations[i]); - } + bool audit = (tx_type == cryptonote::transaction_type::AUDIT && rv.type == RCTTypeSalviumZero && salvium_data.salvium_data_type == rct::SalviumZeroAudit); + for (size_t i = 0 ; i < inamounts.size(); i++) { + if (audit) + a[i] = rct::zero(); + else + skGen(a[i]); + sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); + genC(pseudoOuts[i], a[i], inamounts[i]); + } - rv.p.bulletproofs.clear(); - rv.p.bulletproofs_plus.clear(); - CHECK_AND_ASSERT_THROW_MES(rct_config.range_proof_type == rct::RangeProofPaddedBulletproof, - "Unsupported range proof type: " << rct_config.range_proof_type); - { - rct::keyV C, masks; - if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) - { - // use a fake bulletproof for speed - rv.p.bulletproofs_plus.push_back(make_dummy_bulletproof_plus(outamounts, C, masks)); - } - else - { - const epee::span keys{&amount_keys[0], amount_keys.size()}; - rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, outamounts, keys, hwdev)); - + key difference; + sc_sub(difference.bytes, sumpouts.bytes, sumout.bytes); + genC(rv.p_r, difference, 0); + DP(rv.p_r); + + // set salvium_data + if (rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumZero || rv.type == RCTTypeSalviumOne) { + rv.salvium_data.pr_proof = PRProof_Gen(difference); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof"); + CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), "PRProof_Ver() failed on recently created proof"); #endif - } - for (i = 0; i < outamounts.size(); ++i) - { - rv.outPk[i].mask = rct::scalarmult8(C[i]); - outSk[i].mask = masks[i]; - } - } - - key sumout = zero(); - for (i = 0; i < outSk.size(); ++i) - { - sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); - - //mask amount and mask - rv.ecdhInfo[i].mask = copy(outSk[i].mask); - rv.ecdhInfo[i].amount = d2h(outamounts[i]); - hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], is_rct_bulletproof_plus(rv.type)); - } - - //set txn fee - rv.txnFee = txnFee; -// TODO: unused ?? -// key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - rv.mixRing = mixRing; - keyV &pseudoOuts = rv.p.pseudoOuts; - pseudoOuts.resize(inamounts.size()); - rv.p.TCLSAGs.resize(inamounts.size()); - key sumpouts = zero(); //sum pseudoOut masks - keyV a(inamounts.size()); - bool audit = (tx_type == cryptonote::transaction_type::AUDIT && rv.type == RCTTypeSalviumZero && salvium_data.salvium_data_type == rct::SalviumZeroAudit); - for (i = 0 ; i < inamounts.size(); i++) { - if (audit) - a[i] = rct::zero(); - else - skGen(a[i]); - sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); - genC(pseudoOuts[i], a[i], inamounts[i]); - } - key difference; - sc_sub(difference.bytes, sumpouts.bytes, sumout.bytes); - genC(rv.p_r, difference, 0); - DP(rv.p_r); - if (rv.type == RCTTypeFullProofs || rv.type == RCTTypeSalviumZero || rv.type == RCTTypeSalviumOne) { - rv.salvium_data.pr_proof = PRProof_Gen(difference); + rv.salvium_data.salvium_data_type = salvium_data.salvium_data_type; + if (audit) { + // SRCG: populate the audit proof here + rv.salvium_data.input_verification_data = salvium_data.input_verification_data; + rv.salvium_data.spend_pubkey = salvium_data.spend_pubkey; + rv.salvium_data.enc_view_privkey_str = salvium_data.enc_view_privkey_str; + rv.salvium_data.cz_proof = PRProof_Gen(outSk[0].mask); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.p_r, rv.salvium_data.pr_proof), "PRProof_Ver() failed on recently created proof"); -#endif - rv.salvium_data.salvium_data_type = salvium_data.salvium_data_type; - if (audit) { - // SRCG: populate the audit proof here - rv.salvium_data.input_verification_data = salvium_data.input_verification_data; - rv.salvium_data.spend_pubkey = salvium_data.spend_pubkey; - rv.salvium_data.enc_view_privkey_str = salvium_data.enc_view_privkey_str; - rv.salvium_data.cz_proof = PRProof_Gen(outSk[0].mask); -#ifdef DBG - CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.outPk[0].mask, rv.salvium_data.cz_proof), "PRProof_Ver() failed on recently created change proof"); -#endif - } - } - - // Check if spend authority proof is needed (only for TRANSFER TXs) - if (tx_type == cryptonote::transaction_type::TRANSFER && rv.type == rct::RCTTypeSalviumOne) { - rv.salvium_data.sa_proof = SAProof_Gen(destinations[change_index], x_change, y_change, key_yF); -#ifdef DBG - CHECK_AND_ASSERT_THROW_MES(SAProof_Ver(rv.salvium_data.sa_proof, destinations[change_index], key_yF), "SAProof_Ver() failed on recently created proof"); + CHECK_AND_ASSERT_THROW_MES(PRProof_Ver(rv.outPk[0].mask, rv.salvium_data.cz_proof), "PRProof_Ver() failed on recently created change proof"); #endif } - - key full_message = get_pre_mlsag_hash(rv,hwdev); + } - for (i = 0 ; i < inamounts.size(); i++) - { - if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) - rv.p.TCLSAGs[i] = make_dummy_tclsag(rv.mixRing[i].size()); - else - rv.p.TCLSAGs[i] = proveRctTCLSAGSimple(full_message, rv.mixRing[i], inSk[i].x, inSk[i].y, inSk[i].mask, a[i], pseudoOuts[i], index[i], hwdev); - } + // Check if spend authority proof is needed (only for TRANSFER TXs) + if (tx_type == cryptonote::transaction_type::TRANSFER && rv.type == rct::RCTTypeSalviumOne) { + rv.salvium_data.sa_proof = SAProof_Gen(destinations[change_index], x_change, y_change, key_yF); +#ifdef DBG + CHECK_AND_ASSERT_THROW_MES(SAProof_Ver(rv.salvium_data.sa_proof, destinations[change_index], key_yF), "SAProof_Ver() failed on recently created proof"); +#endif + } - return rv; + // gen tclsags + key full_message = get_pre_mlsag_hash(rv, hwdev); + for (size_t i = 0 ; i < inamounts.size(); i++) + { + if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) + rv.p.TCLSAGs[i] = make_dummy_tclsag(rv.mixRing[i].size()); + else + rv.p.TCLSAGs[i] = proveRctTCLSAGSimple(full_message, rv.mixRing[i], inSk[i].x, inSk[i].y, inSk[i].mask, a[i], pseudoOuts[i], index[i], hwdev); + } + + return rv; } //RCT simple diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 452eb0a99..1bb159db8 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -139,7 +139,7 @@ namespace rct { rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimpleCarrot( - const key & message, + const key &message, const carrot_ctkeyV & inSk, const keyV & destinations, const cryptonote::transaction_type tx_type, @@ -149,16 +149,14 @@ namespace rct { const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, - const keyV &amount_keys, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev, const rct::salvium_data_t &salvium_data, - const key &x_change = rct::zero(), - const key &y_change = rct::zero(), - const size_t change_index = 0, - const key &key_yF = rct::zero() + const key &x_change, + const key &y_change, + const size_t change_index ); rctSig genRctSimple( const key & message, diff --git a/src/wallet/tx_builder.cpp b/src/wallet/tx_builder.cpp index 816dc7e6c..6f6f4797f 100644 --- a/src/wallet/tx_builder.cpp +++ b/src/wallet/tx_builder.cpp @@ -760,14 +760,16 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( LOG_PRINT_L3("Getting output enote proposals"); std::vector output_enote_proposals; carrot::encrypted_payment_id_t encrypted_payment_id; + size_t change_index; carrot::get_output_enote_proposals(tx_proposal.normal_payment_proposals, selfsend_payment_proposal_cores, tx_proposal.dummy_encrypted_payment_id, - /*s_view_balance_dev=*/nullptr, //! @TODO: internal + &w.get_carrot_account().s_view_balance_dev, &addr_dev, tx_proposal.key_images_sorted.at(0), output_enote_proposals, - encrypted_payment_id); + encrypted_payment_id, + change_index); CHECK_AND_ASSERT_THROW_MES(output_enote_proposals.size() == n_outputs, "finalize_all_proofs_from_transfer_details: unexpected number of output enote proposals"); @@ -932,22 +934,48 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( std::vector outamounts; rct::keyV destinations; std::vector destination_asset_types; - for (size_t i = 0; i < tx.vout.size(); ++i) + rct::ctkeyV outSk; + for (const auto &oep : output_enote_proposals) { - crypto::public_key output_public_key; - bool ok = get_output_public_key(tx.vout[i], output_public_key); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, - "Failed to get output public key for tx.vout[" + std::to_string(i) + "]"); - std::string output_asset_type; - ok = cryptonote::get_output_asset_type(tx.vout[i], output_asset_type); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, - "Failed to get output asset type for tx.vout[" + std::to_string(i) + "]"); - destinations.push_back(rct::pk2rct(output_public_key)); - destination_asset_types.push_back(output_asset_type); - outamounts.push_back(tx.vout[i].amount); - amount_out += tx.vout[i].amount; + destinations.push_back(rct::pk2rct(oep.enote.onetime_address)); + destination_asset_types.push_back(oep.enote.asset_type); + outamounts.push_back(oep.amount); + amount_out += oep.amount; + + rct::ctkey key; + key.mask = rct::sk2rct(oep.amount_blinding_factor); + outSk.push_back(key); } + // change output x, y + crypto::public_key change_address_spend_pubkey; + for (const auto &p :selfsend_payment_proposal_cores) { + if (p.enote_type == carrot::CarrotEnoteType::CHANGE) { + change_address_spend_pubkey = p.destination_address_spend_pubkey; + } + } + const carrot::RCTOutputEnoteProposal &change_enote_proposal = output_enote_proposals.at(change_index); + const carrot::input_context_t input_context = carrot::make_carrot_input_context(tx_proposal.key_images_sorted.at(0)); + crypto::hash s_sender_receiver; + w.get_carrot_account().s_view_balance_dev.make_internal_sender_receiver_secret( + change_enote_proposal.enote.enote_ephemeral_pubkey, + input_context, + s_sender_receiver); + crypto::secret_key sender_extension_g; + carrot::make_carrot_onetime_address_extension_g(s_sender_receiver, change_enote_proposal.enote.amount_commitment, sender_extension_g); + crypto::secret_key sender_extension_t; + carrot::make_carrot_onetime_address_extension_t(s_sender_receiver, change_enote_proposal.enote.amount_commitment, sender_extension_t); + crypto::secret_key change_x, change_y; + bool r = w.get_carrot_account().try_searching_for_opening_for_onetime_address( + change_address_spend_pubkey, + sender_extension_g, + sender_extension_t, + change_x, + change_y + ); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, + "Failed to search for opening for onetime address"); + // mixRing indexing is done the other way round for simple rct::ctkeyM mixRing(sources.size()); for (size_t i = 0; i < sources.size(); ++i) @@ -968,7 +996,6 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( // store proofs crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev); - rct::ctkeyV outSk; rct::salvium_data_t salvium_data; salvium_data.salvium_data_type = rct::SalviumOne; tx.rct_signatures = rct::genRctSimpleCarrot( @@ -984,13 +1011,15 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details( mixRing, index, outSk, - rct_config, + rct::RCTConfig { + rct::RangeProofType::RangeProofPaddedBulletproof, + 6, + }, hwdev, salvium_data, - rct::sk2rct(x_change), - rct::sk2rct(y_change), - change_index, - key_yF + rct::sk2rct(change_x), + rct::sk2rct(change_y), + change_index ); tx.pruned = false;