1
0
Fork 0
forked from lthn/blockchain

knowing a DL for transaction pub key is now being checked along with the balance proof (double Schnorr proof, share Fiat-Shamir challenge)

This commit is contained in:
sowle 2024-02-08 17:43:01 +01:00
parent ddcfa36a90
commit 5f53c8fbe1
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
8 changed files with 65 additions and 50 deletions

View file

@ -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");
}

View file

@ -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()
};

View file

@ -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<crypto::gt_G>(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<crypto::gt_G, crypto::gt_G>(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<crypto::gt_X>(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<crypto::gt_X, crypto::gt_G>(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<uint16_t> 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<asset_operation_proof>(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<const asset_operation_proof>(context.tx.proofs);
@ -608,6 +605,8 @@ namespace currency
bool r = get_type_in_variant_container<zc_balance_proof>(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<crypto::gt_X>(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<crypto::gt_X, crypto::gt_G>(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<crypto::gt_G>(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<crypto::gt_G, crypto::gt_G>(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_v> extra_local = extra;
std::vector<attachment_v> 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<tx_destination_entry> 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<asset_descriptor_operation>(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;

View file

@ -264,7 +264,7 @@ namespace currency
const std::vector<tx_out_v>& 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,

View file

@ -398,7 +398,7 @@ namespace currency
CATCH_ENTRY2(std::vector<tx_source_entry::output_entry>{});
}
//---------------------------------------------------------------
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

View file

@ -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<crypto::point_t> asset_ids;
std::vector<crypto::point_t> 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);

View file

@ -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<const std::string&>(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<crypto::public_key, uint64_t> 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;

View file

@ -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()
};