diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 51fe6937..8f5bc4f4 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1319,6 +1319,99 @@ namespace currency keypair in_ephemeral; //std::vector participants_derived_keys; }; + //-------------------------------------------------------------------------------- + bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) + { + return true; + } + //-------------------------------------------------------------------------------- + bool generate_zc_sig(const std::vector& sources, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys) + { + //TODO: sender_account_keys is not used? + tx.signatures.push_back(zarcanum_sig()); + CHECK_AND_ASSERT_THROW_MES(tx.vin.back().type() == typeid(txin_zarcanum_inputs), "Unexpected input type in generate_zc_sig"); + crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, tx.vin.size() - 1, tx_prefix_hash); + CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign"); + + return generate_zarcanum_signature(tx_hash_for_signature, sources, boost::get(tx.vin.back()), boost::get(tx.signatures.back())); + } + //-------------------------------------------------------------------------------- + bool generate_NLSAG_sig(const std::vector& sources, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) + { + bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; + size_t input_index = input_starter_index; + size_t in_context_index = 0; + BOOST_FOREACH(const tx_source_entry* src_entr_ptr, sources) + { + const tx_source_entry& src_entr = *src_entr_ptr; + crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, input_index, tx_prefix_hash); + CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign"); + + tx.signatures.push_back(NLSAG_sig()); + std::vector& sigs = boost::get(tx.signatures.back()).s; + + if (src_entr.is_multisig()) + { + // txin_multisig -- don't sign anything here (see also sign_multisig_input_in_tx()) + sigs.resize(src_entr.ms_keys_count, null_sig); // just reserve keys.size() null signatures (NOTE: not minimum_sigs!) + } + else + { + // regular txin_to_key or htlc + ss_ring_s << "input #" << input_index << ", pub_keys:" << ENDL; + std::vector keys_ptrs; + BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) + { + keys_ptrs.push_back(&o.second); + ss_ring_s << o.second << ENDL; + } + sigs.resize(src_entr.outputs.size()); + + if (!watch_only_mode) + crypto::generate_ring_signature(tx_hash_for_signature, get_to_key_input_from_txin_v(tx.vin[input_index]).k_image, keys_ptrs, in_contexts[in_context_index].in_ephemeral.sec, src_entr.real_output, sigs.data()); + + ss_ring_s << "signatures:" << ENDL; + std::for_each(sigs.begin(), sigs.end(), [&ss_ring_s](const crypto::signature& s) { ss_ring_s << s << ENDL; }); + ss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[in_context_index].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; + } + if (src_entr.separately_signed_tx_complete) + { + // if separately signed tx is complete, put one more signature to the last bunch using tx secret key, which confirms that transaction has been generated by authorized subject + CHECK_AND_ASSERT_MES(input_index == tx.vin.size() - 1, false, "separately_signed_tx_complete flag is set for source entry #" << input_index << ", allowed only for the last one"); + CHECK_AND_ASSERT_MES(get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE, false, "sorce entry separately_signed_tx_complete flag is set for tx with no TX_FLAG_SIGNATURE_MODE_SEPARATE flag"); + CHECK_AND_ASSERT_MES(tx_hash_for_signature == tx_prefix_hash, false, "internal error: hash_for_sign for the last input of separately signed complete tx expected to be the same as tx prefix hash"); + sigs.resize(sigs.size() + 1); + crypto::generate_signature(tx_prefix_hash, txkey.pub, txkey.sec, sigs.back()); + } + + input_index++; + in_context_index++; + } + return true; + } + //-------------------------------------------------------------------------------- + bool generate_zarcanum_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks, + const std::vector& vouts, zarcanum_outs_range_proof& result) + { + //TODO: review for Andre + CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, ""); + CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, ""); + CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, ""); + + std::vector commitments_1div8; + for (size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i) + { + const tx_out_zarcanum& toz = boost::get(vouts[out_index]); // may throw an exception, only zarcanum outputs are exprected + const crypto::public_key* p = &toz.amount_commitment; + commitments_1div8.push_back(p); + } + + uint8_t err = 0; + bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err); + CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << err); + + return true; + } bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& ftp, finalized_tx& result) { @@ -1403,20 +1496,9 @@ namespace currency } // //first: separate zarcanum inputs and regular one -// const std::vector zc_sources; -// const std::vector NLSAG_sources; -// -// BOOST_FOREACH(const tx_source_entry& src_entr, sources) -// { -// if (src_entr.is_zarcanum()) -// { -// zc_sources.push_back(src_entr); -// } -// else -// { -// NLSAG_sources.push_back(src_entr); -// } -// } + std::vector zc_sources; + std::vector NLSAG_sources; + std::vector in_contexts; @@ -1427,7 +1509,7 @@ namespace currency size_t input_starter_index = tx.vin.size(); uint64_t summary_inputs_money = 0; //fill inputs NLSAG and Zarcanum - for (const tx_source_entry& src_entr : NLSAG_sources) + for (const tx_source_entry& src_entr : sources) { in_contexts.push_back(input_generation_context_data()); if(src_entr.is_multisig()) @@ -1534,15 +1616,18 @@ namespace currency input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); //TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything) + //potentially this approach might help to support htlc and multisig without making to complicated code if (src_entr.is_zarcanum()) { zarcanum_input zc_in = AUTO_VAL_INIT(zc_in); zc_in.k_image = img; zc_in.key_offsets = input_to_key.key_offsets; ins_zc.elements.push_back(zc_in); + zc_sources.push_back(&src_entr); }else { tx.vin.push_back(in_v); + NLSAG_sources.push_back(&src_entr); } } @@ -1563,7 +1648,7 @@ namespace currency size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1 uint64_t range_proof_start_index = output_index; std::set deriv_cache; - crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret blinging masks for each output. For range proof generation + crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret binging masks for each output. For range proof generation crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For rnage proof generation for(const tx_destination_entry& dst_entr : shuffled_dsts) { @@ -1586,7 +1671,6 @@ namespace currency } - //process offers and put there offers derived keys uint64_t att_count = 0; for (auto& o : tx.attachment) @@ -1641,86 +1725,25 @@ namespace currency get_transaction_prefix_hash(tx, tx_prefix_hash); - std::stringstream ss_ring_s; - if (tx.version <= TRANSACTION_VERSION_PRE_HF4) + if (NLSAG_sources.size()) { - bool r = generate_NLSAG_sig(ftp, input_starter_index, tx, tx_prefix_hash, sender_account_keys, in_contexts, txkey, ss_ring_s); + bool r = generate_NLSAG_sig(NLSAG_sources, input_starter_index, tx, tx_prefix_hash, sender_account_keys, in_contexts, txkey, ss_ring_s); CHECK_AND_ASSERT_MES(r, false, "Failed to generate_NLSAG_sig()"); - }else - { - bool r = generate_hybrid_sig(); } + if (zc_sources.size()) + { + generate_zc_sig(zc_sources, tx, tx_prefix_hash, sender_account_keys); + } + + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3); return true; } - bool generate_hybrid_sig(const finalize_tx_param& ftp, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) - { - const std::vector& sources = ftp.sources; - bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; - size_t input_index = input_starter_index; - size_t in_context_index = 0; - - bool r = generate_zarcanum_signature(tx_prefix_hash, zc_sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) - - } - bool generate_NLSAG_sig(const finalize_tx_param& ftp, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s) - { - const std::vector& sources = ftp.sources; - bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey; - size_t input_index = input_starter_index; - size_t in_context_index = 0; - BOOST_FOREACH(const tx_source_entry& src_entr, sources) - { - crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, input_index, tx_prefix_hash); - CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign"); - - tx.signatures.push_back(NLSAG_sig()); - std::vector& sigs = boost::get(tx.signatures.back()).s; - - if (src_entr.is_multisig()) - { - // txin_multisig -- don't sign anything here (see also sign_multisig_input_in_tx()) - sigs.resize(src_entr.ms_keys_count, null_sig); // just reserve keys.size() null signatures (NOTE: not minimum_sigs!) - } - else - { - // regular txin_to_key or htlc - ss_ring_s << "input #" << input_index << ", pub_keys:" << ENDL; - std::vector keys_ptrs; - BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) - { - keys_ptrs.push_back(&o.second); - ss_ring_s << o.second << ENDL; - } - sigs.resize(src_entr.outputs.size()); - - if (!watch_only_mode) - crypto::generate_ring_signature(tx_hash_for_signature, get_to_key_input_from_txin_v(tx.vin[input_index]).k_image, keys_ptrs, in_contexts[in_context_index].in_ephemeral.sec, src_entr.real_output, sigs.data()); - - ss_ring_s << "signatures:" << ENDL; - std::for_each(sigs.begin(), sigs.end(), [&ss_ring_s](const crypto::signature& s) { ss_ring_s << s << ENDL; }); - ss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[in_context_index].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; - } - if (src_entr.separately_signed_tx_complete) - { - // if separately signed tx is complete, put one more signature to the last bunch using tx secret key, which confirms that transaction has been generated by authorized subject - CHECK_AND_ASSERT_MES(input_index == tx.vin.size() - 1, false, "separately_signed_tx_complete flag is set for source entry #" << input_index << ", allowed only for the last one"); - CHECK_AND_ASSERT_MES(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE, false, "sorce entry separately_signed_tx_complete flag is set for tx with no TX_FLAG_SIGNATURE_MODE_SEPARATE flag"); - CHECK_AND_ASSERT_MES(tx_hash_for_signature == tx_prefix_hash, false, "internal error: hash_for_sign for the last input of separately signed complete tx expected to be the same as tx prefix hash"); - sigs.resize(sigs.size() + 1); - crypto::generate_signature(tx_prefix_hash, txkey.pub, txkey.sec, sigs.back()); - } - - input_index++; - in_context_index++; - } - return true; - } //--------------------------------------------------------------- uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd) { @@ -3530,29 +3553,7 @@ namespace currency //@#@ TODO return false; } - //-------------------------------------------------------------------------------- - bool generate_zarcanum_outs_range_proof(size_t out_index_start, size_t outs_count, const crypto::scalar_vec_t& amounts, const crypto::scalar_vec_t& blinding_masks, - const std::vector& vouts, zarcanum_outs_range_proof& result) - { - //TODO: review for Andre - CHECK_AND_ASSERT_MES(amounts.size() == outs_count, false, ""); - CHECK_AND_ASSERT_MES(blinding_masks.size() == outs_count, false, ""); - CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, ""); - std::vector commitments_1div8; - for(size_t out_index = out_index_start, i = 0; i < outs_count; ++out_index, ++i) - { - const tx_out_zarcanum& toz = boost::get(vouts[out_index]); // may throw an exception, only zarcanum outputs are exprected - const crypto::public_key* p = &toz.amount_commitment; - commitments_1div8.push_back(p); - } - - uint8_t err = 0; - bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err); - CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << err); - - return true; - } //-------------------------------------------------------------------------------- struct zarcanum_outs_range_proof_commit_ref_t { @@ -3577,11 +3578,6 @@ namespace currency return true; } //-------------------------------------------------------------------------------- - bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result) - { - return true; - } - //-------------------------------------------------------------------------------- boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point,