From 5f53c8fbe13792659254efa00c59fabda6bdf768 Mon Sep 17 00:00:00 2001 From: sowle Date: Thu, 8 Feb 2024 17:43:01 +0100 Subject: [PATCH] knowing a DL for transaction pub key is now being checked along with the balance proof (double Schnorr proof, share Fiat-Shamir challenge) --- src/currency_core/blockchain_storage.cpp | 4 +- src/currency_core/currency_basic.h | 13 ++-- src/currency_core/currency_format_utils.cpp | 61 +++++++++---------- src/currency_core/currency_format_utils.h | 2 +- .../currency_format_utils_transactions.cpp | 2 +- .../currency_format_utils_transactions.h | 18 +++++- src/wallet/wallet2.cpp | 13 ++-- src/wallet/wallet_public_structs_defs.h | 2 - 8 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 8de65d46..febc64c9 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -4110,7 +4110,7 @@ bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::has CHECK_AND_ASSERT_MES(!avc.asset_op_history, false, "asset with id " << avc.asset_id << " has already been registered"); avc.amount_to_validate = ado.descriptor.current_supply; - CHECK_AND_ASSERT_MES(validate_asset_operation_amount_proof(avc), false, "asset operation validation failed!"); + CHECK_AND_ASSERT_MES(validate_asset_operation_amount_commitment(avc), false, "asset operation validation failed!"); assets_container::t_value_type local_asset_history = AUTO_VAL_INIT(local_asset_history); local_asset_history.push_back(ado); @@ -4163,7 +4163,7 @@ bool blockchain_storage::put_asset_info(const transaction& tx, const crypto::has if (need_to_validate_balance_proof) { - bool r = validate_asset_operation_amount_proof(avc); + bool r = validate_asset_operation_amount_commitment(avc); CHECK_AND_ASSERT_MES(r, false, "Balance proof validation failed for asset_descriptor_operation"); } diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 8da2e671..874f80b3 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -438,18 +438,21 @@ namespace currency END_BOOST_SERIALIZATION() }; - // 1) for txs without ZC inputs: proves that balance point = lin(G) (cancels out G component of outputs' amount commitments, asset tags assumed to be H (native coin) and non-blinded) - // 2) for txs with ZC inputs: proves that balance point = lin(X) (cancels out X component of blinded asset tags within amount commitments for both outputs and inputs (pseudo outs)) + // First part of a double Schnorr proof: + // 1) for txs without ZC inputs: proves that balance point = lin(G) (cancels out G component of outputs' amount commitments, asset tags assumed to be H (native coin) and non-blinded) + // 2) for txs with ZC inputs: proves that balance point = lin(X) (cancels out X component of blinded asset tags within amount commitments for both outputs and inputs (pseudo outs)) + // Second part: + // proof of knowing transaction secret key (with respect to G) struct zc_balance_proof { - crypto::generic_schnorr_sig_s ss; + crypto::generic_double_schnorr_sig_s dss; BEGIN_SERIALIZE_OBJECT() - FIELD(ss) + FIELD(dss) END_SERIALIZE() BEGIN_BOOST_SERIALIZATION() - BOOST_SERIALIZE(ss) + BOOST_SERIALIZE(dss) END_BOOST_SERIALIZATION() }; diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index f3b5f341..bdc7218a 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -301,8 +301,8 @@ namespace currency #ifndef NDEBUG CHECK_AND_ASSERT_MES(commitment_to_zero == secret_x * crypto::c_point_G, false, "internal error: commitment_to_zero is malformed (G)"); #endif - r = crypto::generate_schnorr_sig(tx_id, commitment_to_zero, secret_x, proof.ss); - CHECK_AND_ASSERT_MES(r, false, "generate_schnorr_sig (G) failed"); + r = crypto::generate_double_schnorr_sig(tx_id, commitment_to_zero, secret_x, ogc.tx_pub_key_p, ogc.tx_key.sec, proof.dss); + CHECK_AND_ASSERT_MES(r, false, "generate_double_schnorr_sig (G, G) failed"); } else // i.e. zc_inputs_count != 0 { @@ -326,8 +326,8 @@ namespace currency bool commitment_to_zero_is_sane = commitment_to_zero == secret_x * crypto::c_point_X; CHECK_AND_ASSERT_MES(commitment_to_zero_is_sane, false, "internal error: commitment_to_zero is malformed (X)"); #endif - r = crypto::generate_schnorr_sig(tx_id, commitment_to_zero, secret_x, proof.ss); - CHECK_AND_ASSERT_MES(r, false, "generate_schnorr_sig (X) failed"); + r = crypto::generate_double_schnorr_sig(tx_id, commitment_to_zero, secret_x, ogc.tx_pub_key_p, ogc.tx_key.sec, proof.dss); + CHECK_AND_ASSERT_MES(r, false, "genergenerate_double_schnorr_sigate_schnorr_sig (X, G) failed"); } return true; @@ -447,11 +447,9 @@ namespace currency tx = AUTO_VAL_INIT_T(transaction); tx.version = tx_version; - keypair txkey_local{}; - if (!tx_one_time_key_to_use) - txkey_local = keypair::generate(); - const keypair& txkey = tx_one_time_key_to_use ? *tx_one_time_key_to_use : txkey_local; - add_tx_pub_key_to_extra(tx, txkey.pub); + tx_generation_context tx_gen_context{}; + tx_gen_context.set_tx_key(tx_one_time_key_to_use ? *tx_one_time_key_to_use : keypair::generate()); + add_tx_pub_key_to_extra(tx, tx_gen_context.tx_key.pub); if (extra_nonce.size()) if (!add_tx_extra_userdata(tx, extra_nonce)) return false; @@ -488,7 +486,6 @@ namespace currency } // fill outputs - tx_generation_context tx_gen_context{}; tx_gen_context.resize(zc_ins_count, destinations.size()); // auxiliary data for each output uint64_t output_index = 0; std::set deriv_cache; @@ -496,7 +493,7 @@ namespace currency { finalized_tx result = AUTO_VAL_INIT(result); uint8_t tx_outs_attr = 0; - r = construct_tx_out(d, txkey.sec, output_index, tx, deriv_cache, account_keys(), + r = construct_tx_out(d, tx_gen_context.tx_key.sec, output_index, tx, deriv_cache, account_keys(), tx_gen_context.asset_id_blinding_masks[output_index], tx_gen_context.amount_blinding_masks[output_index], tx_gen_context.blinded_asset_ids[output_index], tx_gen_context.amount_commitments[output_index], result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "construct_tx_out failed, output #" << output_index << ", amount: " << print_money_brief(d.amount)); @@ -576,7 +573,7 @@ namespace currency return true; } //----------------------------------------------------------------------------------------------- - bool validate_asset_operation_amount_proof(asset_op_verification_context& context)// const transaction& tx, const crypto::hash& tx_id, const asset_descriptor_operation& ado, crypto::public_key& asset_id) + bool validate_asset_operation_amount_commitment(asset_op_verification_context& context) { CHECK_AND_ASSERT_MES(count_type_in_variant_container(context.tx.proofs) == 1, false, "asset_operation_proof not present or present more than once"); const asset_operation_proof& aop = get_type_in_variant_container_by_ref(context.tx.proofs); @@ -608,6 +605,8 @@ namespace currency bool r = get_type_in_variant_container(tx.proofs, balance_proof); CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof is missing in tx proofs"); + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + size_t zc_inputs_count = 0; uint64_t bare_inputs_sum = additional_inputs_amount_and_fees_for_mining_tx; for(auto& vin : tx.vin) @@ -645,7 +644,7 @@ namespace currency { if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_REGISTER || ado.operation_type == ASSET_DESCRIPTOR_OPERATION_EMIT) { - // opt_amount_commitment supposed to be validated earlier in validate_asset_operation() + // amount_commitment supposed to be validated earlier in validate_asset_operation_amount_commitment() sum_of_pseudo_out_amount_commitments += crypto::point_t(ado.amount_commitment); // *1/8 } else if (ado.operation_type == ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN) @@ -675,13 +674,13 @@ namespace currency CHECK_AND_ASSERT_MES(zc_inputs_count == zc_sigs_count, false, "zc inputs count (" << zc_inputs_count << ") and zc sigs count (" << zc_sigs_count << ") missmatch"); if (zc_inputs_count > 0) { - r = crypto::verify_schnorr_sig(tx_id, commitment_to_zero.to_public_key(), balance_proof.ss); - CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof (X) is invalid"); + r = crypto::verify_double_schnorr_sig(tx_id, commitment_to_zero, tx_pub_key, balance_proof.dss); + CHECK_AND_ASSERT_MES(r, false, "verify_double_schnorr_sig (X, G) is invalid"); } else { - r = crypto::verify_schnorr_sig(tx_id, commitment_to_zero.to_public_key(), balance_proof.ss); - CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof (G) is invalid"); + r = crypto::verify_double_schnorr_sig(tx_id, commitment_to_zero, tx_pub_key, balance_proof.dss); + CHECK_AND_ASSERT_MES(r, false, "verify_double_schnorr_sig (G, G) is invalid"); } return true; } @@ -2438,15 +2437,14 @@ namespace currency LOG_PRINT_YELLOW("WARNING: tx v1 should not use ZC inputs", LOG_LEVEL_0); } + tx_generation_context& gen_context = result.ftp.gen_context; - - keypair txkey = AUTO_VAL_INIT(txkey); if (!append_mode) { - txkey = keypair::generate(); + gen_context.set_tx_key(keypair::generate()); //deterministic_generate_tx_onetime_key(key_images_total, sender_account_keys, txkey); - add_tx_pub_key_to_extra(tx, txkey.pub); - one_time_tx_secret_key = txkey.sec; + add_tx_pub_key_to_extra(tx, gen_context.tx_key.pub); + one_time_tx_secret_key = gen_context.tx_key.sec; //add flags etc_tx_flags16_t e = AUTO_VAL_INIT(e); @@ -2455,13 +2453,12 @@ namespace currency //include offers if need tx.attachment = attachments; - encrypt_attachments(tx, sender_account_keys, crypt_destination_addr, txkey, result.derivation); + encrypt_attachments(tx, sender_account_keys, crypt_destination_addr, gen_context.tx_key, result.derivation); } else { - txkey.pub = get_tx_pub_key_from_extra(tx); - txkey.sec = one_time_tx_secret_key; - CHECK_AND_ASSERT_MES(txkey.pub != null_pkey && txkey.sec != null_skey, false, "In append mode both public and secret keys must be provided"); + gen_context.set_tx_key(keypair{get_tx_pub_key_from_extra(tx), one_time_tx_secret_key}); + CHECK_AND_ASSERT_MES(gen_context.tx_key.pub != null_pkey && gen_context.tx_key.sec != null_skey, false, "In append mode both public and secret keys must be provided"); //separately encrypt attachments without putting extra result.derivation = get_encryption_key_derivation(true, tx, sender_account_keys); @@ -2471,7 +2468,7 @@ namespace currency std::vector extra_local = extra; std::vector attachments_local = attachments; - encrypt_attach_visitor v(was_attachment_crypted_entries, derivation, txkey, account_public_address(), sender_account_keys); + encrypt_attach_visitor v(was_attachment_crypted_entries, derivation, gen_context.tx_key, account_public_address(), sender_account_keys); for (auto& a : attachments_local) boost::apply_visitor(v, a); for (auto& a : extra_local) @@ -2499,8 +2496,6 @@ namespace currency // OUTs // std::vector shuffled_dsts(destinations); - //size_t outputs_to_be_constructed = shuffled_dsts.size(); - tx_generation_context& gen_context = result.ftp.gen_context; gen_context.resize(zc_inputs_count, tx.vout.size() + shuffled_dsts.size()); // ASSET oprations handling @@ -2510,7 +2505,7 @@ namespace currency pado = get_type_in_variant_container(tx.extra); if (pado) { - bool r = construct_tx_handle_ado(sender_account_keys, ftp, *pado, gen_context, txkey, shuffled_dsts); + bool r = construct_tx_handle_ado(sender_account_keys, ftp, *pado, gen_context, gen_context.tx_key, shuffled_dsts); CHECK_AND_ASSERT_MES(r, false, "Failed to construct_tx_handle_ado()"); if (ftp.pevents_dispatcher) ftp.pevents_dispatcher->RAISE_DEBUG_EVENT(wde_construct_tx_handle_asset_descriptor_operation{ pado }); } @@ -2533,7 +2528,7 @@ namespace currency if (!(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) && all_inputs_are_obviously_native_coins && gen_context.ao_asset_id == currency::null_pkey) dst_entr.flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // all inputs are obviously native coins -- all outputs must have explicit asset ids (unless there's an asset emission) - r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, + r = construct_tx_out(dst_entr, gen_context.tx_key.sec, output_index, tx, deriv_cache, sender_account_keys, gen_context.asset_id_blinding_masks[output_index], gen_context.amount_blinding_masks[output_index], gen_context.blinded_asset_ids[output_index], gen_context.amount_commitments[output_index], result, tx_outs_attr); CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out"); @@ -2642,7 +2637,7 @@ namespace currency else { // NLSAG - r = generate_NLSAG_sig(tx_hash_for_signature, tx_prefix_hash, i_ + input_starter_index, source_entry, sender_account_keys, in_contexts[i_mapped], txkey, flags, tx, &ss_ring_s); + r = generate_NLSAG_sig(tx_hash_for_signature, tx_prefix_hash, i_ + input_starter_index, source_entry, sender_account_keys, in_contexts[i_mapped], gen_context.tx_key, flags, tx, &ss_ring_s); CHECK_AND_ASSERT_MES(r, false, "generate_NLSAG_sig failed"); } @@ -3180,7 +3175,7 @@ namespace currency if (P_prime.to_public_key() != zo.stealth_address) return false; - crypto::point_t Q_prime = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * 8 * crypto::point_t(addr.view_public_key); // Q' * 8 =? Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V + crypto::point_t Q_prime = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(addr.view_public_key).modify_mul8(); // Q' * 8 =? Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V if (Q_prime != crypto::point_t(zo.concealing_point).modify_mul8()) return false; diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index bb1dbb70..7896ae5d 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -264,7 +264,7 @@ namespace currency const std::vector& vouts, zc_outs_range_proof& result); bool check_tx_bare_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0); bool check_tx_balance(const transaction& tx, const crypto::hash& tx_id, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0); - bool validate_asset_operation_amount_proof(asset_op_verification_context& context); + bool validate_asset_operation_amount_commitment(asset_op_verification_context& context); const char* get_asset_operation_type_string(size_t asset_operation_type, bool short_name = false); //--------------------------------------------------------------- bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 029eca38..291010a6 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -398,7 +398,7 @@ namespace currency CATCH_ENTRY2(std::vector{}); } //--------------------------------------------------------------- - bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context, const crypto::secret_key& onet_time_key) + bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context) { //TODO: Implement this function before mainnet #ifdef TESTNET diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index 2fc8beaf..aeaa551a 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -243,6 +243,12 @@ namespace currency amount_blinding_masks.size() == outs_count; } + void set_tx_key(const keypair& kp) + { + tx_key = kp; + tx_pub_key_p = crypto::point_t(tx_key.pub); + } + // per output data std::vector asset_ids; std::vector blinded_asset_ids; // generate_zc_outs_range_proof @@ -274,6 +280,10 @@ namespace currency crypto::scalar_t ao_amount_blinding_mask {}; // generate_tx_balance_proof generate_ZC_sig bool ao_commitment_in_outputs = false; + // per tx data + keypair tx_key {}; // + crypto::point_t tx_pub_key_p = crypto::c_point_0; // == tx_key.pub + // consider redesign, some data may possibly be excluded from kv serialization -- sowle BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(asset_ids) @@ -297,6 +307,8 @@ namespace currency KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_commitment) KV_SERIALIZE_POD_AS_HEX_STRING(ao_amount_blinding_mask) KV_SERIALIZE_POD_AS_HEX_STRING(ao_commitment_in_outputs) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_key) + KV_SERIALIZE_POD_AS_HEX_STRING(tx_pub_key_p) END_KV_SERIALIZE_MAP() // solely for consolidated txs, asset opration fields are not serialized @@ -325,10 +337,14 @@ namespace currency //ao_amount_commitment //ao_amount_blinding_mask //ao_commitment_in_outputs + + FIELD(tx_key.pub) // TODO: change to sane serialization FIELD(tx_key) + FIELD(tx_key.sec) + FIELD(tx_pub_key_p) END_SERIALIZE() }; // struct tx_generation_context - bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context, const crypto::secret_key& onet_time_key); + bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context); std::string transform_tx_to_str(const transaction& tx); transaction transform_str_to_tx(const std::string& tx_str); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d4b19270..3226efaa 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -671,9 +671,12 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t out_get_mixin_attr(out_v) != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX) { std::stringstream ss; - ss << "output #" << o << " from tx " << ptc.tx_hash() << " with amount " << print_money_brief(outs[i_in_outs].amount) + ss << "output #" << o << " from tx " << ptc.tx_hash(); + if (!out.is_native_coin()) + ss << " asset_id: " << out.asset_id; + ss << " with amount " << print_money_brief(out.amount) << " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)out_get_mixin_attr(out_v) << ". Output is IGNORED."; - WLT_LOG_RED(ss.str(), LOG_LEVEL_0); + WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0); if (m_wcallback) m_wcallback->on_message(i_wallet2_callback::ms_red, ss.str()); //if (out.is_native_coin()) @@ -5548,7 +5551,7 @@ bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal proposal.tx_template = finalize_result.tx; wallet_public::ionic_swap_proposal_context ispc = AUTO_VAL_INIT(ispc); ispc.gen_context = finalize_result.ftp.gen_context; - ispc.one_time_skey = finalize_result.one_time_key; + //ispc.one_time_skey = finalize_result.one_time_key; std::string proposal_context_blob = t_serializable_object_to_blob(ispc); proposal.encrypted_context = crypto::chacha_crypt(static_cast(proposal_context_blob), finalize_result.derivation); return true; @@ -5586,7 +5589,7 @@ bool wallet2::get_ionic_swap_proposal_info(const wallet_public::ionic_swap_propo r = t_unserializable_object_from_blob(ionic_context, decrypted_raw_context); THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "Failed to unserialize decrypted ionic_context"); - r = validate_tx_output_details_againt_tx_generation_context(tx, ionic_context.gen_context, ionic_context.one_time_skey); + r = validate_tx_output_details_againt_tx_generation_context(tx, ionic_context.gen_context); THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "Failed to validate decrypted ionic_context"); std::unordered_map amounts_provided_by_a; @@ -5754,7 +5757,7 @@ bool wallet2::accept_ionic_swap_proposal(const wallet_public::ionic_swap_proposa construct_tx_param construct_param = get_default_construct_tx_param(); construct_param.fee = additional_fee; - crypto::secret_key one_time_key = ionic_context.one_time_skey; + crypto::secret_key one_time_key = ionic_context.gen_context.tx_key.sec; // TODO: figure out this mess with tx sec key -- sowle construct_param.crypt_address = m_account.get_public_address(); construct_param.flags = TX_FLAG_SIGNATURE_MODE_SEPARATE; construct_param.mark_tx_as_complete = true; diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 0b4b1970..11c11a68 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -1407,12 +1407,10 @@ namespace wallet_public struct ionic_swap_proposal_context { currency::tx_generation_context gen_context; - crypto::secret_key one_time_skey; BEGIN_SERIALIZE_OBJECT() VERSION(0) //use VERSION_TO_MEMBER if it's more then 0 FIELD(gen_context) - FIELD(one_time_skey) END_SERIALIZE() };