From 5fbb7a7fbbaaf8435cfbfb2e57aa5c875c3f08eb Mon Sep 17 00:00:00 2001 From: sowle Date: Sun, 26 Feb 2023 21:48:09 +0100 Subject: [PATCH] bcs: range proofs collection, aggragation and balance checking refactored & improved --- src/currency_core/blockchain_storage.cpp | 49 ++++++++++------- src/currency_core/blockchain_storage.h | 2 +- src/currency_core/currency_format_utils.cpp | 52 +++++++++---------- .../currency_format_utils_transactions.cpp | 5 -- 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 21208afe..e591b308 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1324,19 +1324,20 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t { if (pos) { - CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 1, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2"); - CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)"); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 1, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 2"); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction has incorrect type of proof #0 (expected: zc_outs_range_proof)"); } else { - CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 2, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2"); - CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)"); - CHECK_AND_ASSERT_MES(b.miner_tx.attachment[1].type() == typeid(zc_balance_proof), false, "coinbase transaction wrong attachmenttype #1 (expected: zc_balance_proof)"); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 2, false, "coinbase transaction has incorrect number of proofs (" << b.miner_tx.proofs.size() << "), expected 2"); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction has incorrect type of proof #0 (expected: zc_outs_range_proof)"); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs[1].type() == typeid(zc_balance_proof), false, "coinbase transaction has incorrect type of proof #1 (expected: zc_balance_proof)"); } } else { CHECK_AND_ASSERT_MES(b.miner_tx.attachment.empty(), false, "coinbase transaction has attachments; attachments are not allowed for coinbase transactions."); + CHECK_AND_ASSERT_MES(b.miner_tx.proofs.size() == 0, false, "pre-HF4 coinbase shoudn't have non-empty proofs containter"); } return true; @@ -5706,21 +5707,32 @@ bool get_tx_from_cache(const crypto::hash& tx_id, transactions_map& tx_cache, tr return true; } //------------------------------------------------------------------ -bool blockchain_storage::collect_rangeproofs_data_from_tx(std::vector& agregated_proofs, const transaction& tx) +bool blockchain_storage::collect_rangeproofs_data_from_tx(const transaction& tx, const crypto::hash& tx_id, std::vector& agregated_proofs) { if (tx.version <= TRANSACTION_VERSION_PRE_HF4) - { return true; - } - // TODO @#@# Verify somewhere(maybe here) that all outputs are covered with associated rangeproofs - size_t proofs_count = 0; - size_t current_output_start = 0; //for Consolidated Transactions we'll have multiple zc_outs_range_proof entries + size_t range_proofs_count = 0; + size_t out_index_offset = 0; //Consolidated Transactions have multiple zc_outs_range_proof entries for (const auto& a : tx.proofs) { if (a.type() == typeid(zc_outs_range_proof)) { const zc_outs_range_proof& zcrp = boost::get(a); + + // validate aggregation proof + std::vector amount_commitment_ptrs_1div8, blinded_asset_id_ptrs_1div8; + for(size_t j = out_index_offset; j < tx.vout.size(); ++j) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vout[j], const tx_out_zarcanum, zcout, false); + amount_commitment_ptrs_1div8.push_back(&zcout.amount_commitment); + blinded_asset_id_ptrs_1div8.push_back(&zcout.blinded_asset_id); + } + uint8_t err = 0; + bool r = crypto::verify_vector_UG_aggregation_proof(tx_id, amount_commitment_ptrs_1div8, blinded_asset_id_ptrs_1div8, zcrp.aggregation_proof, &err); + CHECK_AND_ASSERT_MES(r, false, "verify_vector_UG_aggregation_proof failed with err code " << (int)err); + + agregated_proofs.emplace_back(zcrp); // convert amount commitments for aggregation from public_key to point_t form @@ -5728,13 +5740,14 @@ bool blockchain_storage::collect_rangeproofs_data_from_tx(std::vector 0, false, "transaction " << get_transaction_hash(tx) << " don't have range_proofs"); - CHECK_AND_ASSERT_MES(proofs_count == 1 || (get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE), false, "transaction " << get_transaction_hash(tx) - << " has TX_FLAG_SIGNATURE_MODE_SEPARATE but proofs_count = " << proofs_count); + CHECK_AND_ASSERT_MES(out_index_offset == tx.vout.size(), false, "range proof elements count doesn't match with outputs count: " << out_index_offset << " != " << tx.vout.size()); + CHECK_AND_ASSERT_MES(range_proofs_count > 0, false, "transaction " << get_transaction_hash(tx) << " doesn't have range proofs"); + CHECK_AND_ASSERT_MES(range_proofs_count == 1 || (get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE), false, "transaction " << get_transaction_hash(tx) + << " doesn't have TX_FLAG_SIGNATURE_MODE_SEPARATE but has range_proofs_count = " << range_proofs_count); return true; } @@ -5901,7 +5914,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt { auto cleanup = [&](){ purge_block_data_from_blockchain(bl, tx_processed_count); bvc.m_verification_failed = true; }; - CHECK_AND_ASSERT_MES_CUSTOM(collect_rangeproofs_data_from_tx(range_proofs_agregated, tx/*, tx_outs_commitments*/), false, cleanup(), + CHECK_AND_ASSERT_MES_CUSTOM(collect_rangeproofs_data_from_tx(tx, tx_id, range_proofs_agregated), false, cleanup(), "block " << id << ", tx " << tx_id << ": collect_rangeproofs_data_from_tx failed"); CHECK_AND_ASSERT_MES_CUSTOM(check_tx_balance(tx, tx_id), false, cleanup(), @@ -5986,7 +5999,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt return false; } - if (!collect_rangeproofs_data_from_tx(range_proofs_agregated, bl.miner_tx)) + if (!collect_rangeproofs_data_from_tx(bl.miner_tx, get_transaction_hash(bl.miner_tx), range_proofs_agregated)) { LOG_PRINT_L0("Block with id: " << id << " have wrong miner tx, failed to collect_rangeproofs_data_from_tx()"); diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index cc89303f..019ec7de 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -593,7 +593,7 @@ namespace currency wide_difficulty_type get_next_difficulty_for_alternative_chain(const alt_chain_type& alt_chain, block_extended_info& bei, bool pos) const; bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc); bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc); - bool collect_rangeproofs_data_from_tx(std::vector& agregated_proofs, const transaction& tx); + bool collect_rangeproofs_data_from_tx(const transaction& tx, const crypto::hash& tx_id, std::vector& agregated_proofs); std::string print_alt_chain(alt_chain_type alt_chain); bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); bool is_reorganize_required(const block_extended_info& main_chain_bei, const alt_chain_type& alt_chain, const crypto::hash& proof_alt); diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index fe6499e5..df1ee47d 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -154,8 +154,24 @@ namespace currency return true; } - //--------------------------------------------------------------- + bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs) + { + if (range_proofs.empty()) + return true; + + std::vector sigs; + sigs.reserve(range_proofs.size()); + for(auto& el : range_proofs) + sigs.emplace_back(el.range_proof.bpp, el.amount_commitments); + + uint8_t err = 0; + bool r = crypto::bpp_verify<>(sigs, &err); + CHECK_AND_ASSERT_MES(r, false, "bpp_verify failed with error " << (int)err); + + return true; + } + //-------------------------------------------------------------------------------- wide_difficulty_type correct_difficulty_with_sequence_factor(size_t sequence_factor, wide_difficulty_type diff) { //delta=delta*(0.75^n) @@ -171,6 +187,7 @@ namespace currency { CHECK_AND_ASSERT_MES(tx.version > TRANSACTION_VERSION_PRE_HF4, false, "unsupported tx.version: " << tx.version); CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.signatures) == 0, false, "ZC_sig is unexpected"); + CHECK_AND_ASSERT_MES(outs_gen_context.asset_id_blinding_masks_sum.is_zero(), false, "it's expected that all asset ids for this tx are non-blinded"); // because this tx has no ZC inputs => all outs clearly have native asset id uint64_t bare_inputs_sum = block_reward_for_miner_tx; // TODO: condider remove the followin cycle @@ -200,18 +217,18 @@ namespace currency uint64_t fee = 0; CHECK_AND_ASSERT_MES(get_tx_fee(tx, fee), false, "unable to get tx fee"); - // sum(bare inputs' amounts) * H + sum(pseudo outs commitments for ZC inputs) + residual * G = sum(outputs' commitments) + fee * H - // <=> - // (fee - sum(bare inputs' amounts)) * H - sum(pseudo outs commitments for ZC inputs) + sum(outputs' commitments) = residual * G + // (sum(bare inputs' amounts) - fee) * H + sum(pseudo outs commitments for ZC inputs) - sum(outputs' commitments) = lin(G) - // tx doesn't have any zc inputs --> add Schnorr proof for commitment to zero + // tx doesn't already have any zc inputs --> add Schnorr proof for commitment to zero CHECK_AND_ASSERT_MES(count_type_in_variant_container(tx.proofs) == 0, false, ""); zc_balance_proof balance_proof = AUTO_VAL_INIT(balance_proof); - crypto::point_t commitment_to_zero = outs_commitments_sum + (crypto::scalar_t(fee) - crypto::scalar_t(bare_inputs_sum)) * crypto::c_point_H; - //crypto::scalar_t witness = outputs_blinding_masks_sum; - - crypto::generate_signature(tx_id, commitment_to_zero.to_public_key(), outs_gen_context.amount_blinding_masks_sum.as_secret_key(), balance_proof.s); + crypto::point_t commitment_to_zero = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * crypto::c_point_H - outs_commitments_sum; + crypto::scalar_t secret_x = -outs_gen_context.amount_blinding_masks_sum; +#ifndef NDEBUG + CHECK_AND_ASSERT_MES(commitment_to_zero == secret_x * crypto::c_point_G, false, "internal error: commitment_to_zero is malformed"); +#endif + crypto::generate_signature(tx_id, commitment_to_zero.to_public_key(), secret_x.as_secret_key(), balance_proof.s); tx.proofs.emplace_back(std::move(balance_proof)); return true; @@ -4134,23 +4151,6 @@ namespace currency return a.n == b.n && a.tx_id == b.tx_id; } //-------------------------------------------------------------------------------- - bool verify_multiple_zc_outs_range_proofs(const std::vector& range_proofs) - { - if (range_proofs.empty()) - return true; - - std::vector sigs; - sigs.reserve(range_proofs.size()); - for(auto& el : range_proofs) - sigs.emplace_back(el.range_proof.bpp, el.amount_commitments); - - uint8_t err = 0; - bool r = crypto::bpp_verify<>(sigs, &err); - CHECK_AND_ASSERT_MES(r, false, "bpp_verify failed with error " << (int)err); - - return true; - } - //-------------------------------------------------------------------------------- boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point, diff --git a/src/currency_core/currency_format_utils_transactions.cpp b/src/currency_core/currency_format_utils_transactions.cpp index 6ab5de36..24ec80d5 100644 --- a/src/currency_core/currency_format_utils_transactions.cpp +++ b/src/currency_core/currency_format_utils_transactions.cpp @@ -33,11 +33,6 @@ namespace currency return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT; } //--------------------------------------------------------------- - - - - - uint64_t get_burned_amount(const transaction& tx) { uint64_t res = 0;