From 2ce22c25083d6b8ff95fb2e8a62da60de708701b Mon Sep 17 00:00:00 2001 From: Some Random Crypto Guy Date: Sun, 15 Dec 2024 22:28:14 +0000 Subject: [PATCH] partial working serialisation --- .../cryptonote_boost_serialization.h | 24 ++- .../cryptonote_format_utils.cpp | 4 +- src/cryptonote_core/blockchain.cpp | 8 +- src/cryptonote_core/cryptonote_core.cpp | 3 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 193 +++++------------- src/cryptonote_core/tx_verification_utils.cpp | 2 +- src/device/device_ledger.cpp | 6 +- src/multisig/multisig_tx_builder_ringct.cpp | 3 +- src/ringct/rctSigs.cpp | 171 +++++++++++++--- src/ringct/rctSigs.h | 11 +- src/ringct/rctTypes.cpp | 3 + src/ringct/rctTypes.h | 27 ++- src/serialization/json_object.cpp | 31 +++ src/serialization/json_object.h | 3 + src/wallet/wallet2.cpp | 11 +- tests/unit_tests/bulletproofs.cpp | 4 +- tests/unit_tests/ringct.cpp | 2 +- tests/unit_tests/serialization.cpp | 5 +- 18 files changed, 303 insertions(+), 208 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index a3c7a16ce..41fa0e2c7 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -350,6 +350,14 @@ namespace boost a & x.D; } + template + inline void serialize(Archive &a, rct::zk_proof &x, const boost::serialization::version_type ver) + { + a & x.R; + a & x.z1; + a & x.z2; + } + template inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver) { @@ -403,7 +411,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus && x.type != rct::RCTTypeFullProofs) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -413,6 +421,10 @@ namespace boost serializeOutPk(a, x.outPk, ver); a & x.txnFee; a & x.p_r; + if (x.type == rct::RCTTypeFullProofs) { + a & x.pr_proof; + //a & x.sa_proofs; + } } template @@ -438,7 +450,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus && x.type != rct::RCTTypeFullProofs) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -448,6 +460,10 @@ namespace boost serializeOutPk(a, x.outPk, ver); a & x.txnFee; a & x.p_r; + if (x.type == rct::RCTTypeFullProofs) { + a & x.pr_proof; + //a & x.sa_proofs; + } //-------------- a & x.p.rangeSigs; if (x.p.rangeSigs.empty()) @@ -459,7 +475,7 @@ namespace boost a & x.p.MGs; if (ver >= 1u) a & x.p.CLSAGs; - if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus) + if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus || x.type == rct::RCTTypeFullProofs) a & x.p.pseudoOuts; } @@ -501,5 +517,5 @@ namespace boost } BOOST_CLASS_VERSION(rct::rctSigPrunable, 2) -BOOST_CLASS_VERSION(rct::rctSig, 2) +BOOST_CLASS_VERSION(rct::rctSig, 3) BOOST_CLASS_VERSION(rct::multisig_out, 1) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index a88a15bce..12ce3491a 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -110,7 +110,7 @@ namespace cryptonote uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs) { const rct::rctSig &rv = tx.rct_signatures; - const bool plus = rv.type == rct::RCTTypeBulletproofPlus; + const bool plus = (rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs); const uint64_t bp_base = (32 * ((plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2) const size_t n_outputs = tx.vout.size(); if (n_padded_outputs <= 2) @@ -517,7 +517,7 @@ namespace cryptonote { CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits::max(), "get_pruned_transaction_weight does not support non pruned txes"); CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits::max(), "get_pruned_transaction_weight does not support v1 txes"); - CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus, + CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus || tx.rct_signatures.type == rct::RCTTypeFullProofs, std::numeric_limits::max(), "Unsupported rct_signatures type in get_pruned_transaction_weight"); CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits::max(), "empty vin"); CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits::max(), "empty vin"); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0e839323c..3e2dca563 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1497,6 +1497,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl switch (version) { case HF_VERSION_BULLETPROOF_PLUS: case HF_VERSION_ENABLE_N_OUTS: + case HF_VERSION_FULL_PROOFS: if (b.miner_tx.amount_burnt > 0) { CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt"); money_in_use += b.miner_tx.amount_burnt; @@ -3709,7 +3710,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); @@ -3750,7 +3751,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus) + else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus || rv.type == rct::RCTTypeFullProofs) { if (!tx.pruned) { @@ -3997,7 +3998,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, false, "Transaction spends at least one output which is too young"); // Warn that new RCT types are present, and thus the cache is not being used effectively - static constexpr const std::uint8_t RCT_CACHE_TYPE = rct::RCTTypeBulletproofPlus; + static constexpr const std::uint8_t RCT_CACHE_TYPE = rct::RCTTypeFullProofs; if (tx.rct_signatures.type > RCT_CACHE_TYPE) { MWARNING("RCT cache is not caching new verification results. Please update RCT_CACHE_TYPE!"); @@ -4040,6 +4041,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, case rct::RCTTypeBulletproof2: case rct::RCTTypeCLSAG: case rct::RCTTypeBulletproofPlus: + case rct::RCTTypeFullProofs: { if (!ver_rct_non_semantics_simple_cached(tx, pubkeys, m_rct_ver_cache, RCT_CACHE_TYPE, hf_version)) { diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 252cddfd2..20defc2db 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -970,6 +970,7 @@ namespace cryptonote rvv.push_back(&rv); // delayed batch verification break; case rct::RCTTypeBulletproofPlus: + case rct::RCTTypeFullProofs: if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus)) { MERROR_VER("Bulletproof_plus does not have canonical form"); @@ -996,7 +997,7 @@ namespace cryptonote { if (!tx_info[n].result) continue; - if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus) + if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs) continue; if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures, tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt : diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 04ea0dc35..ae5ee897c 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -116,132 +116,6 @@ namespace cryptonote return inv; } - rct::proofV SAProof_Gen(const std::vector &vout, const size_t change_index, const crypto::secret_key &x_change) { - - // Declare a return structure - rct::proofV proofV{}; - - // Sanity checks - CHECK_AND_ASSERT_THROW_MES(vout.size(), "SAProof_Gen() failed - no outputs provided"); - CHECK_AND_ASSERT_THROW_MES(vout.size()>=change_index, "SAProof_Gen() failed - invalid change_index provided"); - CHECK_AND_ASSERT_THROW_MES(x_change != rct::zero(), "SAProof_Gen() failed - invalid x_change key provided"); - - // Create a buffer for the hashable data - size_t size = vout.size() * (32+32); - std::vector buffer; - buffer.resize(size); - uint8_t *ptr = buffer.data(); - - // Iterate over the outputs - rct::keyV scalars; - rct::keyV commitments; - rct::keyV pubkeys; - for (size_t j=0; j> 1), pubkey.data, sizeof(rct::key)); - ptr += sizeof(rct::key); - } - CHECK_AND_ASSERT_THROW_MES(scalars.size() == vout.size(), "in SAProof_Gen() : incorrect number of scalars"); - CHECK_AND_ASSERT_THROW_MES(commitments.size() == vout.size(), "in SAProof_Gen() : incorrect number of commitments"); - CHECK_AND_ASSERT_THROW_MES(pubkeys.size() == vout.size(), "in SAProof_Gen() : incorrect number of pubkeys"); - - // Calculate the hash - rct::key hash; - rct::cn_fast_hash(hash, buffer.data(), buffer.size()); - rct::key c = rct::scalarmultBase(hash); - - for (size_t j=0; j &vout, const size_t change_index) { - // Sanity checks - CHECK_AND_ASSERT_THROW_MES(proofs.size() == vout.size(), "PRProof_Ver() failed - proof count does not match output count"); - CHECK_AND_ASSERT_THROW_MES(change_index < vout.size(), "PRProof_Ver() failed - invalid change index provided"); - - // Extract the proof for the change output - we don't care about the others because they're dummy proofs - const auto &proof = proofs[change_index]; - const rct::key &R = proof.R; // Commitment - const rct::key &z_x = proof.z1; // z_x value - const rct::key &z_y = proof.z2; // z_y value - - // Extract the public key of the change output - crypto::public_key pubkey = crypto::null_pkey; - CHECK_AND_ASSERT_THROW_MES(!cryptonote::get_output_public_key(vout[change_index], pubkey), "PRProof_Ver() failed - could not retrieve output public key"); - - // Convert the public key to rct format - rct::key P = rct::pk2rct(pubkey); - - // Recompute the challenge hash - size_t size = vout.size() * (32 + 32); - std::vector buffer(size); - uint8_t *ptr = buffer.data(); - - for (size_t j = 0; j < vout.size(); ++j) { - // Retrieve commitments and public keys for hashing - const rct::key &commitment = proofs[j].R; - crypto::public_key pubkey_temp; - CHECK_AND_ASSERT_THROW_MES(!cryptonote::get_output_public_key(vout[j], pubkey_temp), "PRProof_Ver() failed - could not retrieve public key"); - - // Copy data into the buffer - std::memcpy(ptr, commitment.bytes, sizeof(rct::key)); - std::memcpy(ptr + (size >> 1), pubkey_temp.data, sizeof(rct::key)); - ptr += sizeof(rct::key); - } - - // Compute the challenge hash - rct::key hash; - rct::cn_fast_hash(hash, buffer.data(), buffer.size()); - rct::key c = rct::scalarmultBase(hash); - - // Verify the proof for the change output - // Recalculate the expected commitment using the formula: z_x * G = R + c * P - rct::key expected_commitment = rct::addKeys(R, rct::scalarmultKey(c, P)); - - // Verify z_x * G matches the expected commitment - if (!rct::equalKeys(rct::scalarmultBase(z_x), expected_commitment)) { - return false; // Verification failed - } - - // Verify z_y * G matches the original commitment - if (!rct::equalKeys(rct::scalarmultBase(z_y), R)) { - return false; // Verification failed - } - - // All checks passed - return true; - } - //--------------------------------------------------------------- void classify_addresses(const std::vector &destinations, const boost::optional& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress) { @@ -715,6 +589,7 @@ namespace cryptonote switch (hard_fork_version) { case HF_VERSION_BULLETPROOF_PLUS: case HF_VERSION_ENABLE_N_OUTS: + case HF_VERSION_FULL_PROOFS: // SRCG: subtract 20% that will be rewarded to staking users CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero"); tx.amount_burnt = amount / 5; @@ -982,6 +857,7 @@ namespace cryptonote uint64_t summary_outs_money = 0; //fill outputs size_t output_index = 0; + crypto::secret_key x_change = crypto::null_skey; uint8_t change_index = 0; for(const tx_destination_entry& dst_entr: destinations) { @@ -1037,9 +913,34 @@ namespace cryptonote if (hf_version >= HF_VERSION_ENABLE_N_OUTS && tx.type == cryptonote::transaction_type::TRANSFER) { - // Calculate the spend authority proof - crypto::secret_key x_change = crypto::null_skey; - tx.rct_signatures.sa_proof = SAProof_Gen(tx.vout, change_index, x_change); + if (hf_version >= HF_VERSION_FULL_PROOFS) { + + // Get the secret spend key for the change element + crypto::secret_key spend_skey = crypto::null_skey; + if (sender_account_keys.m_multisig_keys.empty()) + { + // if not multisig, use normal spend skey + spend_skey = sender_account_keys.m_spend_secret_key; + } + else + { + // if multisig, use sum of multisig privkeys (local account's share of aggregate spend key) + for (const auto &multisig_key : sender_account_keys.m_multisig_keys) + { + sc_add((unsigned char*)spend_skey.data, + (const unsigned char*)multisig_key.data, + (const unsigned char*)spend_skey.data); + } + } + + // Obtain a separate key_derivation for the P_change output + // (using the TX public key and the sender's private view key) + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation); + CHECK_AND_ASSERT_MES(hwdev.generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation), false, "Failed to generate key_derivation for P_change"); + + // Calculate the secret spend key "x_change" for the P_change output + CHECK_AND_ASSERT_MES(hwdev.derive_secret_key(derivation, change_index, spend_skey, x_change), false, "Failed to derive secret key for P_change"); + } // Get the output public key for the change output crypto::public_key P_change = crypto::null_pkey; @@ -1292,22 +1193,24 @@ namespace cryptonote rct::ctkeyV outSk; if (use_simple_rct) tx.rct_signatures = rct::genRctSimple( - rct::hash2rct(tx_prefix_hash), - inSk, - destinations, - tx_type, - source_asset, - destination_asset_types, - inamounts, - outamounts, - fee, - mixRing, - amount_keys, - index, - outSk, - rct_config, - hwdev - ); + rct::hash2rct(tx_prefix_hash), + inSk, + destinations, + tx_type, + source_asset, + destination_asset_types, + inamounts, + outamounts, + fee, + mixRing, + amount_keys, + index, + outSk, + rct_config, + hwdev, + rct::sk2rct(x_change), + change_index + ); else tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, rct_config, hwdev); // same index assumption memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey)); diff --git a/src/cryptonote_core/tx_verification_utils.cpp b/src/cryptonote_core/tx_verification_utils.cpp index e4bc74b4f..775368f55 100644 --- a/src/cryptonote_core/tx_verification_utils.cpp +++ b/src/cryptonote_core/tx_verification_utils.cpp @@ -130,7 +130,7 @@ bool ver_rct_non_semantics_simple_cached // mixring. Future versions of the protocol may differ in this regard, but if this assumptions // holds true in the future, enable the verification hash by modifying the `untested_tx` // condition below. - const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeBulletproofPlus; + const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeFullProofs; VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here."); // Don't cache older (or newer) rctSig types diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 009ee16ca..e85c12ede 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1929,7 +1929,7 @@ namespace hw { // ====== Aout, Bout, AKout, C, v, k ====== kv_offset = data_offset; - if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { + if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs) { C_offset = kv_offset+ (8)*outputs_size; } else { C_offset = kv_offset+ (32+32)*outputs_size; @@ -1946,7 +1946,7 @@ namespace hw { offset = set_command_header(INS_VALIDATE, 0x02, i+1); //options this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ; - this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus)?0x02:0x00; + this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs)?0x02:0x00; offset += 1; //is_subaddress this->buffer_send[offset] = outKeys.is_subaddress; @@ -1967,7 +1967,7 @@ namespace hw { memmove(this->buffer_send+offset, data+C_offset,32); offset += 32; C_offset += 32; - if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { + if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus || type==rct::RCTTypeFullProofs) { //k memset(this->buffer_send+offset, 0, 32); offset += 32; diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp index d17e993d6..7c07cb465 100644 --- a/src/multisig/multisig_tx_builder_ringct.cpp +++ b/src/multisig/multisig_tx_builder_ringct.cpp @@ -920,14 +920,13 @@ static bool set_tx_rct_signatures( rct::genC(rv.p_r, difference, 0); if (rv.type == rct::RCTTypeFullProofs) { rv.pr_proof = PRProof_Gen(difference); - //rv.sa_proof = SAProof_Gen(); } } // check balance if reconstructing the tx else { rv.p.pseudoOuts = unsigned_tx.rct_signatures.p.pseudoOuts; rv.pr_proof = unsigned_tx.rct_signatures.pr_proof; // should verify this during reconstruction - rv.sa_proof = unsigned_tx.rct_signatures.sa_proof; // should verify this during reconstruction + //rv.sa_proofs = unsigned_tx.rct_signatures.sa_proofs; // should verify this during reconstruction rv.p_r = unsigned_tx.rct_signatures.p_r; if (num_sources != rv.p.pseudoOuts.size()) return false; diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 9be094845..9bbdf7202 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1098,6 +1098,101 @@ namespace rct { return index; } + std::vector SAProof_Gen(const keyV &pubkeys, const key &x_change, const size_t change_index) { + + // Declare a return structure + std::vector proofs{}; + + // Sanity checks + CHECK_AND_ASSERT_THROW_MES(pubkeys.size(), "SAProof_Gen() failed - no output pubkeys provided"); + CHECK_AND_ASSERT_THROW_MES(pubkeys.size() > change_index, "SAProof_Gen() failed - invalid change_index provided"); + CHECK_AND_ASSERT_THROW_MES(!rct::equalKeys(x_change, rct::zero()), "SAProof_Gen() failed - invalid x_change key provided"); + + // Iterate over the outputs + rct::keyV scalars; + rct::keyV commitments; + for (size_t j=0; j &proofs, const keyV &pubkeys, const size_t change_index) { + // Sanity checks + CHECK_AND_ASSERT_THROW_MES(proofs.size() == pubkeys.size(), "PRProof_Ver() failed - proof count does not match output count"); + CHECK_AND_ASSERT_THROW_MES(change_index < pubkeys.size(), "PRProof_Ver() failed - invalid change index provided"); + + // Recompute the challenge hash + keyV challenge_keys; + challenge_keys.reserve(pubkeys.size() * 2); + for (const auto &proof_entr: proofs) { + challenge_keys.push_back(proof_entr.R); + } + challenge_keys.insert(challenge_keys.end(), pubkeys.begin(), pubkeys.end()); + rct::key c = rct::hash_to_scalar(challenge_keys); + sc_reduce32(c.bytes); + + // Extract the proof for the change output - we don't care about the others because they're dummy proofs + const auto &proof = proofs[change_index]; + const rct::key &R = proof.R; // Commitment + const rct::key &z_x = proof.z1; // z_x value + const rct::key &z_y = proof.z2; // z_y value + const rct::key P = pubkeys[change_index]; + + // Verify the proof for the change output + // Recalculate the expected commitment using the formula: z_x * G = R + c * P + rct::key expected_commitment = rct::addKeys(R, rct::scalarmultKey(c, P)); + + // Verify z_x * G matches the expected commitment + if (!rct::equalKeys(rct::scalarmultBase(z_x), expected_commitment)) { + return false; // Verification failed + } + + // Verify z_y * G matches the original commitment + if (!rct::equalKeys(rct::scalarmultBase(z_y), R)) { + return false; // Verification failed + } + + // All checks passed + return true; + } + //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the @@ -1170,21 +1265,24 @@ namespace rct { //RCT simple //for post-rct only rctSig genRctSimple( - const key &message, - const ctkeyV & inSk, - const keyV & destinations, - const cryptonote::transaction_type tx_type, - const std::string& in_asset_type, - const std::vector & destination_asset_types, - const std::vector &inamounts, - 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 key &message, + const ctkeyV & inSk, + const keyV & destinations, + const cryptonote::transaction_type tx_type, + const std::string& in_asset_type, + const std::vector & destination_asset_types, + const std::vector &inamounts, + 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 key &x_change, + const size_t change_index + ) { const bool bulletproof_or_plus = rct_config.range_proof_type > RangeProofBorromean; CHECK_AND_ASSERT_THROW_MES(destination_asset_types.size() == destinations.size(), "Different number of amount_keys/destinations"); @@ -1369,24 +1467,37 @@ namespace rct { rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], 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::RCTTypeFullProofs) { + rv.sa_proofs = SAProof_Gen(destinations, x_change, change_index); +#ifdef DBG + CHECK_AND_ASSERT_THROW_MES(SAProof_Ver(rv.sa_proofs, destinations, change_index), "SAProof_Ver() failed on recently created proof"); +#endif + } + */ + return rv; } rctSig genRctSimple( - const key &message, - const ctkeyV & inSk, - const ctkeyV & inPk, - const keyV & destinations, - const cryptonote::transaction_type tx_type, - const std::string& in_asset_type, - const std::vector & destination_asset_types, - const std::vector &inamounts, - const std::vector &outamounts, - const keyV &amount_keys, - xmr_amount txnFee, - unsigned int mixin, - const RCTConfig &rct_config, - hw::device &hwdev + const key &message, + const ctkeyV & inSk, + const ctkeyV & inPk, + const keyV & destinations, + const cryptonote::transaction_type tx_type, + const std::string& in_asset_type, + const std::vector & destination_asset_types, + const std::vector &inamounts, + const std::vector &outamounts, + const keyV &amount_keys, + xmr_amount txnFee, + unsigned int mixin, + const RCTConfig &rct_config, + hw::device &hwdev, + const key &x_change, + const size_t change_index ) { std::vector index; index.resize(inPk.size()); @@ -1397,7 +1508,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev); + return genRctSimple(message, inSk, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev, x_change, change_index); } //RingCT protocol diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 33c12f066..38aa1eaad 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -88,6 +88,9 @@ namespace rct { zk_proof PRProof_Gen(const rct::key &difference); bool PRProof_Ver(const rct::key &C, const zk_proof &proof); + std::vector SAProof_Gen(const keyV &pubkeys, const key &x_change, const size_t change_index); + bool SAProof_Ver(const std::vector &proofs, const keyV &pubkeys, const size_t change_index); + //proveRange and verRange //proveRange gives C, and mask such that \sumCi = C // c.f. https://eprint.iacr.org/2015/1098 section 5.1 @@ -145,7 +148,9 @@ namespace rct { xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, - hw::device &hwdev + hw::device &hwdev, + const key &x_change = rct::zero(), + const size_t change_index = 0 ); rctSig genRctSimple( const key & message, @@ -162,7 +167,9 @@ namespace rct { const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, - hw::device &hwdev + hw::device &hwdev, + const key &x_change = rct::zero(), + const size_t change_index = 0 ); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index c22b0524f..31a9d1a67 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -197,6 +197,7 @@ namespace rct { case RCTTypeBulletproof2: case RCTTypeCLSAG: case RCTTypeBulletproofPlus: + case RCTTypeFullProofs: return true; default: return false; @@ -221,6 +222,7 @@ namespace rct { switch (type) { case RCTTypeBulletproofPlus: + case RCTTypeFullProofs: return true; default: return false; @@ -245,6 +247,7 @@ namespace rct { { case RCTTypeCLSAG: case RCTTypeBulletproofPlus: + case RCTTypeFullProofs: return true; default: return false; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 74783bd3e..43b3ec827 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -101,7 +101,6 @@ namespace rct { FIELD(z2) END_SERIALIZE() }; - typedef std::vector proofV; //containers For CT operations //if it's representing a private ctkey then "dest" contains the secret key of the address @@ -341,10 +340,10 @@ namespace rct { xmr_amount txnFee; // contains b key p_r; zk_proof pr_proof; // p_r - proofV sa_proof; + std::vector sa_proofs; // spend authority proofs rctSigBase() : - type(RCTTypeNull), message{}, mixRing{}, pseudoOuts{}, ecdhInfo{}, outPk{}, txnFee(0), p_r{}, pr_proof{}, sa_proof{} + type(RCTTypeNull), message{}, mixRing{}, pseudoOuts{}, ecdhInfo{}, outPk{}, txnFee(0), p_r{}, pr_proof{}, sa_proofs{} {} template class Archive> @@ -422,7 +421,22 @@ namespace rct { if (type == RCTTypeFullProofs) { FIELD(pr_proof) - FIELD(sa_proof) + /* + uint32_t nsap = sa_proofs.size(); + VARINT_FIELD(nsap) + ar.tag("sa_proofs"); + ar.begin_array(); + if (nsap > outputs) + return false; + PREPARE_CUSTOM_VECTOR_SERIALIZATION(nsap, sa_proofs); + for (size_t i = 0; i < nsap; ++i) + { + FIELDS(sa_proofs[i]) + if (nsap - i > 1) + ar.delimit_array(); + } + ar.end_array(); + */ } return ar.good(); } @@ -438,7 +452,7 @@ namespace rct { FIELD(p_r) if (type == RCTTypeFullProofs) { FIELD(pr_proof) - FIELD(sa_proof) + //FIELD(sa_proofs) } END_SERIALIZE() }; @@ -820,6 +834,7 @@ VARIANT_TAG(debug_archive, rct::multisig_kLRki, "rct::multisig_kLRki"); VARIANT_TAG(debug_archive, rct::multisig_out, "rct::multisig_out"); VARIANT_TAG(debug_archive, rct::clsag, "rct::clsag"); VARIANT_TAG(debug_archive, rct::BulletproofPlus, "rct::bulletproof_plus"); +VARIANT_TAG(debug_archive, rct::zk_proof, "rct::zk_proof"); VARIANT_TAG(binary_archive, rct::key, 0x90); VARIANT_TAG(binary_archive, rct::key64, 0x91); @@ -838,6 +853,7 @@ VARIANT_TAG(binary_archive, rct::multisig_kLRki, 0x9d); VARIANT_TAG(binary_archive, rct::multisig_out, 0x9e); VARIANT_TAG(binary_archive, rct::clsag, 0x9f); VARIANT_TAG(binary_archive, rct::BulletproofPlus, 0xa0); +VARIANT_TAG(binary_archive, rct::zk_proof, 0xa1); VARIANT_TAG(json_archive, rct::key, "rct_key"); VARIANT_TAG(json_archive, rct::key64, "rct_key64"); @@ -856,5 +872,6 @@ VARIANT_TAG(json_archive, rct::multisig_kLRki, "rct_multisig_kLR"); VARIANT_TAG(json_archive, rct::multisig_out, "rct_multisig_out"); VARIANT_TAG(json_archive, rct::clsag, "rct_clsag"); VARIANT_TAG(json_archive, rct::BulletproofPlus, "rct_bulletproof_plus"); +VARIANT_TAG(json_archive, rct::zk_proof, "rct_zk_proof"); #endif /* RCTTYPES_H */ diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index bef0906dc..5bce34ac5 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1173,6 +1173,10 @@ void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask)); INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee); INSERT_INTO_JSON_OBJECT(dest, p_r, sig.p_r); + if (sig.type == rct::RCTTypeFullProofs) { + INSERT_INTO_JSON_OBJECT(dest, pr_proof, sig.pr_proof); + INSERT_INTO_JSON_OBJECT(dest, sa_proofs, sig.sa_proofs); + } } // prunable @@ -1210,6 +1214,10 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) GET_FROM_JSON_OBJECT(val, sig.outPk, commitments); GET_FROM_JSON_OBJECT(val, sig.txnFee, fee); GET_FROM_JSON_OBJECT(val, sig.p_r, p_r); + if (sig.type == rct::RCTTypeFullProofs) { + GET_FROM_JSON_OBJECT(val, sig.pr_proof, pr_proof); + GET_FROM_JSON_OBJECT(val, sig.sa_proofs, sa_proofs); + } } // prunable @@ -1465,6 +1473,29 @@ void fromJsonValue(const rapidjson::Value& val, rct::clsag& sig) GET_FROM_JSON_OBJECT(val, sig.D, D); } +void toJsonValue(rapidjson::Writer& dest, const rct::zk_proof& proof) +{ + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, R, proof.R); + INSERT_INTO_JSON_OBJECT(dest, z1, proof.z1); + INSERT_INTO_JSON_OBJECT(dest, z2, proof.z2); + + dest.EndObject(); +} + +void fromJsonValue(const rapidjson::Value& val, rct::zk_proof& proof) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("zk_proof (rct::zk_proof)"); + } + + GET_FROM_JSON_OBJECT(val, proof.R, R); + GET_FROM_JSON_OBJECT(val, proof.z1, z1); + GET_FROM_JSON_OBJECT(val, proof.z2, z2); +} + void toJsonValue(rapidjson::Writer& dest, const cryptonote::rpc::DaemonInfo& info) { dest.StartObject(); diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 3868ab3f8..c0022985b 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -307,6 +307,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig); void toJsonValue(rapidjson::Writer& dest, const rct::clsag& sig); void fromJsonValue(const rapidjson::Value& val, rct::clsag& sig); +void toJsonValue(rapidjson::Writer& dest, const rct::zk_proof& p); +void fromJsonValue(const rapidjson::Value& val, rct::zk_proof& p); + void toJsonValue(rapidjson::Writer& dest, const cryptonote::rpc::DaemonInfo& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9d6f2009a..419ab5cfb 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2147,6 +2147,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & case rct::RCTTypeBulletproof2: case rct::RCTTypeCLSAG: case rct::RCTTypeBulletproofPlus: + case rct::RCTTypeFullProofs: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); @@ -10263,11 +10264,11 @@ void wallet2::transfer_selected_rct(std::vector destination_asset_types; for (size_t i = 0; i < destinations.size(); ++i) - destination_asset_types.push_back("FULM"); + destination_asset_types.push_back("SAL"); rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, available, mixRing, amount_keys, index, outSk, rct_config, hw::get_device("default")); ASSERT_TRUE(rct::verRctSimple(s)); diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index bbecd6fe5..6dc2061e8 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -538,7 +538,7 @@ static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amount } } - const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; + const rct::RCTConfig rct_config { RangeProofBorromean, 0 }; return genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3, rct_config, hw::get_device("default")); } diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 823ab609b..9331ec013 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -608,10 +608,11 @@ TEST(Serialization, serializes_ringct_types) //compute rct data with mixin 3 const rct::RCTConfig rct_config{ rct::RangeProofPaddedBulletproof, 2 }; cryptonote::transaction_type tx_type = cryptonote::transaction_type::TRANSFER; - std::string in_asset_type = "FULM"; + std::string in_asset_type = "SAL"; std::vector destination_asset_types; for (size_t i = 0; i < destinations.size(); ++i) - destination_asset_types.push_back("FULM"); + destination_asset_types.push_back("SAL"); + s0 = rct::genRctSimple(rct::zero(), sc, pc, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, amounts, amount_keys, 0, 3, rct_config, hw::get_device("default")); ASSERT_FALSE(s0.p.MGs.empty());