From 5ab468be42e2cc08fb1adcd6ff011309744acf2c Mon Sep 17 00:00:00 2001 From: sowle Date: Tue, 25 Jun 2024 15:46:53 +0200 Subject: [PATCH] validate_tx_details_against_tx_generation_context implemented --- .../currency_format_utils_transactions.cpp | 112 ++++++++++++++++-- .../currency_format_utils_transactions.h | 3 +- src/wallet/wallet2.cpp | 4 +- 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 291010a6..6c4a5a2e 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -398,14 +398,112 @@ namespace currency CATCH_ENTRY2(std::vector{}); } //--------------------------------------------------------------- - bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context) + bool validate_tx_details_against_tx_generation_context(const transaction& tx, const tx_generation_context& tgc) { - //TODO: Implement this function before mainnet -#ifdef TESTNET + CHECK_AND_ASSERT_MES(tx.version >= TRANSACTION_VERSION_POST_HF4, false, "validate_tx_details_against_tx_generation_context should never be called for pre-HF4 txs"); + + // + // outputs + // (all outputs are ZC outputs, because we require tx.version to be post-HF4) + size_t tx_outs_count = tx.vout.size(); + CHECK_AND_ASSERT_EQ(tgc.asset_ids.size(), tx_outs_count); + CHECK_AND_ASSERT_EQ(tgc.blinded_asset_ids.size(), tx_outs_count); + CHECK_AND_ASSERT_EQ(tgc.amount_commitments.size(), tx_outs_count); + CHECK_AND_ASSERT_EQ(tgc.asset_id_blinding_masks.size(), tx_outs_count); + CHECK_AND_ASSERT_EQ(tgc.amounts.size(), tx_outs_count); + CHECK_AND_ASSERT_EQ(tgc.amount_blinding_masks.size(), tx_outs_count); + + crypto::point_t amount_commitments_sum = crypto::c_point_0; + crypto::scalar_t amount_blinding_masks_sum{}; + crypto::scalar_t asset_id_blinding_mask_x_amount_sum{}; + for(size_t i = 0; i < tx_outs_count; ++i) + { + crypto::point_t calculated_T = tgc.asset_ids[i] + tgc.asset_id_blinding_masks[i] * crypto::c_point_X; + CHECK_AND_ASSERT_MES(calculated_T.is_in_main_subgroup(), false, "calculated_T isn't in the main subgroup"); + CHECK_AND_ASSERT_EQ(tgc.blinded_asset_ids[i], calculated_T); + + crypto::point_t calculated_A = tgc.amounts[i] * calculated_T + tgc.amount_blinding_masks[i] * crypto::c_point_G; + CHECK_AND_ASSERT_MES(calculated_A.is_in_main_subgroup(), false, "calculated_A isn't in the main subgroup"); + CHECK_AND_ASSERT_EQ(tgc.amount_commitments[i], calculated_A); + + const tx_out_zarcanum& tx_out_zc = boost::get(tx.vout[i]); + crypto::point_t tx_T = crypto::point_t(tx_out_zc.blinded_asset_id).modify_mul8(); + CHECK_AND_ASSERT_EQ(tgc.blinded_asset_ids[i], tx_T); + crypto::point_t tx_A = crypto::point_t(tx_out_zc.amount_commitment).modify_mul8(); + CHECK_AND_ASSERT_EQ(tgc.amount_commitments[i], tx_A); + amount_commitments_sum += calculated_A; + amount_blinding_masks_sum += tgc.amount_blinding_masks[i]; + asset_id_blinding_mask_x_amount_sum += tgc.asset_id_blinding_masks[i] * tgc.amounts[i]; + } + CHECK_AND_ASSERT_EQ(tgc.amount_commitments_sum, amount_commitments_sum); + CHECK_AND_ASSERT_EQ(tgc.amount_blinding_masks_sum, amount_blinding_masks_sum); + CHECK_AND_ASSERT_EQ(tgc.asset_id_blinding_mask_x_amount_sum, asset_id_blinding_mask_x_amount_sum); + + // + // inputs + // + + size_t tx_inputs_count = tx.vin.size(); + size_t tx_zc_inputs_count = count_type_in_variant_container(tx.vin); + size_t tx_bare_inputs_count = count_type_in_variant_container(tx.vin); + CHECK_AND_ASSERT_EQ(tx_inputs_count, tx_zc_inputs_count + tx_bare_inputs_count); + CHECK_AND_ASSERT_EQ(tx.signatures.size(), tx.vin.size()); // can do this because we shouldn't face additional signature so far + + CHECK_AND_ASSERT_EQ(tgc.pseudo_outs_blinded_asset_ids.size(), tx_zc_inputs_count); + CHECK_AND_ASSERT_EQ(tgc.pseudo_outs_plus_real_out_blinding_masks.size(), tx_zc_inputs_count); + CHECK_AND_ASSERT_EQ(tgc.real_zc_ins_asset_ids.size(), tx_zc_inputs_count); + CHECK_AND_ASSERT_EQ(tgc.zc_input_amounts.size(), tx_zc_inputs_count); + + crypto::point_t pseudo_out_amount_commitments_sum = crypto::c_point_0; + + for(size_t input_index = 0, zc_input_index = 0; input_index < tx.vin.size(); ++input_index) + { + const txin_v& in_v = tx.vin[input_index]; + if (in_v.type() == typeid(txin_zc_input)) + { + CHECK_AND_ASSERT_EQ(tx.signatures[input_index].type(), typeid(ZC_sig)); + const ZC_sig& sig = boost::get(tx.signatures[input_index]); + + crypto::point_t sig_pseudo_out_amount_commitment = crypto::point_t(sig.pseudo_out_amount_commitment).modify_mul8(); + crypto::point_t sig_pseudo_out_blinded_asset_id = crypto::point_t(sig.pseudo_out_blinded_asset_id).modify_mul8(); + + crypto::point_t pseudo_out_blinded_asset_id_calculated = tgc.real_zc_ins_asset_ids[zc_input_index] + tgc.pseudo_outs_plus_real_out_blinding_masks[zc_input_index] * crypto::c_point_X; // T^P = H_i + r_pi*X + r'_i*X + CHECK_AND_ASSERT_EQ(sig_pseudo_out_blinded_asset_id, pseudo_out_blinded_asset_id_calculated); + CHECK_AND_ASSERT_EQ(tgc.pseudo_outs_blinded_asset_ids[zc_input_index], pseudo_out_blinded_asset_id_calculated); + CHECK_AND_ASSERT_MES(pseudo_out_blinded_asset_id_calculated.is_in_main_subgroup(), false, "pseudo_out_blinded_asset_id_calculated isn't in the main subgroup"); + // cannot be verified: + // tgc.zc_input_amounts[zc_input_index]; // ZC only input amounts + + pseudo_out_amount_commitments_sum += sig_pseudo_out_amount_commitment; + ++zc_input_index; + } + else if (in_v.type() == typeid(txin_to_key)) + { + // do nothing + } + else + { + // should never get here + CHECK_AND_ASSERT_MES(false, false, "internal error, unexpected type: " << in_v.type().name()); + } + } + + // tx secret and public keys + CHECK_AND_ASSERT_MES(tgc.tx_pub_key_p.is_in_main_subgroup(), false, "tgc.tx_pub_key_p isn't in the main subgroup"); + CHECK_AND_ASSERT_EQ(tgc.tx_pub_key_p.to_public_key(), tgc.tx_key.pub); + CHECK_AND_ASSERT_EQ(crypto::scalar_t(tgc.tx_key.sec) * crypto::c_point_G, tgc.tx_pub_key_p); + + // no ongoing asset operation + CHECK_AND_ASSERT_EQ(tgc.ao_asset_id, currency::null_pkey); + CHECK_AND_ASSERT_EQ(tgc.ao_asset_id_pt, crypto::c_point_0); + CHECK_AND_ASSERT_EQ(tgc.ao_amount_commitment, crypto::c_point_0); + CHECK_AND_ASSERT_EQ(tgc.ao_amount_blinding_mask, crypto::c_scalar_0); + + // cannot be verified: + // tgc.pseudo_out_amount_blinding_masks_sum + // tgc.real_in_asset_id_blinding_mask_x_amount_sum + return true; -#else - return true; -#endif } //---------------------------------------------------------------------------------------------------- @@ -421,4 +519,4 @@ namespace currency } -} \ No newline at end of file +} diff --git a/src/currency_core/currency_format_utils_transactions.h b/src/currency_core/currency_format_utils_transactions.h index f608b4eb..0ce53e85 100644 --- a/src/currency_core/currency_format_utils_transactions.h +++ b/src/currency_core/currency_format_utils_transactions.h @@ -344,7 +344,8 @@ namespace currency END_SERIALIZE() }; // struct tx_generation_context - bool validate_tx_output_details_againt_tx_generation_context(const transaction& tx, const tx_generation_context& gen_context); + bool validate_tx_details_against_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 5effc4f1..6b746c8f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6060,8 +6060,8 @@ 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); - THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "Failed to validate decrypted ionic_context"); + r = validate_tx_details_against_tx_generation_context(tx, ionic_context.gen_context); + THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "validate_tx_details_against_tx_generation_context failed"); std::unordered_map amounts_provided_by_a;