forked from lthn/blockchain
Merge branch 'zarcanum_wallet' into zarcanum
This commit is contained in:
commit
74f6ce33ac
10 changed files with 445 additions and 228 deletions
|
|
@ -1651,7 +1651,7 @@ bool blockchain_storage::purge_altblock_keyimages_from_big_heap(const block& b,
|
|||
// TODO @#@# consider refactoring
|
||||
const txin_zarcanum_inputs& zins = boost::get<txin_zarcanum_inputs>(tx.vin[n]);
|
||||
for(const auto& el : zins.elements)
|
||||
purge_keyimage_from_big_heap(el.key_image, block_id);
|
||||
purge_keyimage_from_big_heap(el.k_image, block_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3952,7 +3952,7 @@ namespace currency
|
|||
// TODO: @#@# should check for hardfork here?
|
||||
for(auto& el : in.elements)
|
||||
{
|
||||
if (!visit(0, el.key_image, el.key_offsets))
|
||||
if (!visit(0, el.k_image, el.key_offsets))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -4311,7 +4311,7 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const
|
|||
const auto& zins = boost::get<txin_zarcanum_inputs>(in);
|
||||
for(auto& el: zins.elements)
|
||||
{
|
||||
if (have_tx_keyimg_as_spent(el.key_image))
|
||||
if (have_tx_keyimg_as_spent(el.k_image))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -4927,7 +4927,7 @@ std::shared_ptr<const transaction_chain_entry> blockchain_storage::find_key_imag
|
|||
const auto& zins = boost::get<txin_zarcanum_inputs>(in);
|
||||
for(auto& el: zins.elements)
|
||||
{
|
||||
if (el.key_image == ki)
|
||||
if (el.k_image == ki)
|
||||
{
|
||||
id_result = tx_id;
|
||||
return tx_chain_entry;
|
||||
|
|
|
|||
|
|
@ -310,15 +310,15 @@ namespace currency
|
|||
zarcanum_input(const zarcanum_input&) = default;
|
||||
zarcanum_input& operator=(const zarcanum_input&)= default;
|
||||
|
||||
crypto::key_image key_image;
|
||||
crypto::key_image k_image;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(key_image)
|
||||
FIELD(k_image)
|
||||
FIELD(key_offsets) // referring_input
|
||||
END_SERIALIZE()
|
||||
|
||||
BEGIN_BOOST_SERIALIZATION()
|
||||
BOOST_SERIALIZE(key_image)
|
||||
BOOST_SERIALIZE(k_image)
|
||||
BOOST_SERIALIZE(key_offsets) // referring_input
|
||||
END_BOOST_SERIALIZATION()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1313,6 +1313,106 @@ namespace currency
|
|||
return r;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
// prepare inputs
|
||||
struct input_generation_context_data
|
||||
{
|
||||
keypair in_ephemeral;
|
||||
//std::vector<keypair> participants_derived_keys;
|
||||
};
|
||||
//--------------------------------------------------------------------------------
|
||||
bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector<const tx_source_entry*>& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------
|
||||
bool generate_zc_sig(const std::vector<const tx_source_entry*>& 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<txin_zarcanum_inputs>(tx.vin.back()), boost::get<zarcanum_sig>(tx.signatures.back()));
|
||||
}
|
||||
//--------------------------------------------------------------------------------
|
||||
bool generate_NLSAG_sig(const std::vector<const tx_source_entry*>& sources, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector<input_generation_context_data>& 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<crypto::signature>& sigs = boost::get<NLSAG_sig>(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<const crypto::public_key*> 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<tx_out_v>& 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<const crypto::public_key*> 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<tx_out_zarcanum>(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)
|
||||
{
|
||||
const std::vector<tx_source_entry>& sources = ftp.sources;
|
||||
|
|
@ -1395,19 +1495,20 @@ namespace currency
|
|||
tx.extra.insert(tx.extra.end(), extra_local.begin(), extra_local.end());
|
||||
}
|
||||
|
||||
// //first: separate zarcanum inputs and regular one
|
||||
std::vector<const tx_source_entry*> zc_sources;
|
||||
std::vector<const tx_source_entry*> NLSAG_sources;
|
||||
|
||||
|
||||
// prepare inputs
|
||||
struct input_generation_context_data
|
||||
{
|
||||
keypair in_ephemeral;
|
||||
//std::vector<keypair> participants_derived_keys;
|
||||
};
|
||||
std::vector<input_generation_context_data> in_contexts;
|
||||
|
||||
std::vector<input_generation_context_data> in_contexts;
|
||||
|
||||
//we'll aggregate Zarcanum outs into one txin_zarcanum_inputs
|
||||
txin_zarcanum_inputs ins_zc = AUTO_VAL_INIT(ins_zc);
|
||||
|
||||
size_t input_starter_index = tx.vin.size();
|
||||
uint64_t summary_inputs_money = 0;
|
||||
//fill inputs
|
||||
//fill inputs NLSAG and Zarcanum
|
||||
for (const tx_source_entry& src_entr : sources)
|
||||
{
|
||||
in_contexts.push_back(input_generation_context_data());
|
||||
|
|
@ -1513,10 +1614,30 @@ namespace currency
|
|||
input_to_key.key_offsets.push_back(out_entry.first);
|
||||
|
||||
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
|
||||
tx.vin.push_back(in_v);
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ins_zc.elements.size())
|
||||
{
|
||||
tx.vin.push_back(ins_zc);
|
||||
}
|
||||
|
||||
// "Shuffle" outs
|
||||
std::vector<tx_destination_entry> shuffled_dsts(destinations);
|
||||
if (shuffle)
|
||||
|
|
@ -1525,15 +1646,16 @@ namespace currency
|
|||
uint64_t summary_outs_money = 0;
|
||||
//fill outputs
|
||||
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<uint16_t> 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)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check
|
||||
bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, blinding_masks[output_index], result, tx_outs_attr);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out");
|
||||
amounts[output_index] = dst_entr.amount;
|
||||
amounts[range_proof_start_index - output_index] = dst_entr.amount;
|
||||
output_index++;
|
||||
summary_outs_money += dst_entr.amount;
|
||||
}
|
||||
|
|
@ -1549,7 +1671,6 @@ namespace currency
|
|||
}
|
||||
|
||||
|
||||
|
||||
//process offers and put there offers derived keys
|
||||
uint64_t att_count = 0;
|
||||
for (auto& o : tx.attachment)
|
||||
|
|
@ -1567,13 +1688,16 @@ namespace currency
|
|||
att_count++;
|
||||
}
|
||||
}
|
||||
if (!(flags & TX_FLAG_SIGNATURE_MODE_SEPARATE))
|
||||
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
|
||||
{
|
||||
//take hash from attachment and put into extra
|
||||
if (tx.attachment.size())
|
||||
add_attachments_info_to_extra(tx.extra, tx.attachment);
|
||||
//add range proofs
|
||||
currency::zarcanum_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs);
|
||||
bool r = generate_zarcanum_outs_range_proof(range_proof_start_index, amounts.size(), amounts, blinding_masks, tx.vout, range_proofs);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate zarcanum_outs_range_proof()");
|
||||
tx.attachment.push_back(range_proofs);
|
||||
}
|
||||
else
|
||||
|
||||
if (flags & TX_FLAG_SIGNATURE_MODE_SEPARATE)
|
||||
{
|
||||
// for separately signed tx each input has to contain information about corresponding outputs, extra entries and attachments
|
||||
for (size_t in_index = input_starter_index; in_index != tx.vin.size(); in_index++)
|
||||
|
|
@ -1583,69 +1707,43 @@ namespace currency
|
|||
so.n_outs = tx.vout.size();
|
||||
so.n_extras = tx.extra.size();
|
||||
get_txin_etc_options(tx.vin[in_index]).push_back(so);
|
||||
|
||||
|
||||
// put attachment extra info to each input's details (in case there are attachments)
|
||||
add_attachments_info_to_extra(get_txin_etc_options(tx.vin[in_index]), tx.attachment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//take hash from attachment and put into extra
|
||||
if (tx.attachment.size())
|
||||
add_attachments_info_to_extra(tx.extra, tx.attachment);
|
||||
}
|
||||
|
||||
|
||||
//generate ring signatures
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
||||
|
||||
std::stringstream ss_ring_s;
|
||||
size_t input_index = input_starter_index;
|
||||
size_t in_context_index = 0;
|
||||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
if (NLSAG_sources.size())
|
||||
{
|
||||
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<crypto::signature>& sigs = boost::get<NLSAG_sig>(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<const crypto::public_key*> 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++;
|
||||
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()");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd)
|
||||
{
|
||||
|
|
@ -3455,28 +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<tx_out_v>& vouts, zarcanum_outs_range_proof& result)
|
||||
{
|
||||
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<const crypto::public_key*> 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<tx_out_zarcanum>(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
|
||||
{
|
||||
|
|
@ -3501,11 +3578,6 @@ namespace currency
|
|||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------
|
||||
bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector<tx_source_entry>& 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,
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ namespace currency
|
|||
bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx)
|
||||
{
|
||||
CHECK_CORE_READY();
|
||||
res.status = "Failed";
|
||||
res.status = API_RETURN_CODE_FAIL;
|
||||
if(!m_core.get_random_outs_for_amounts(req, res))
|
||||
{
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -323,84 +323,143 @@ void wallet2::fetch_tx_global_indixes(const std::list<std::reference_wrapper<con
|
|||
}
|
||||
}
|
||||
|
||||
bool out_is_to_key(const currency::tx_out_v& out_t)
|
||||
{
|
||||
if (out_t.type() == typeid(currency::tx_out_bare))
|
||||
{
|
||||
return boost::get<currency::tx_out_bare>(out_t).target.type() == typeid(currency::txout_to_key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool out_is_multisig(const currency::tx_out_v& out_t)
|
||||
{
|
||||
if (out_t.type() == typeid(currency::tx_out_bare))
|
||||
{
|
||||
return boost::get<currency::tx_out_bare>(out_t).target.type() == typeid(currency::txout_multisig);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool out_is_to_htlc(const currency::tx_out_v& out_t)
|
||||
{
|
||||
if (out_t.type() == typeid(currency::tx_out_bare))
|
||||
{
|
||||
return boost::get<currency::tx_out_bare>(out_t).target.type() == typeid(currency::txout_htlc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const currency::txout_htlc& out_get_htlc(const currency::tx_out_v& out_t)
|
||||
{
|
||||
return boost::get<currency::txout_htlc>(boost::get<currency::tx_out_bare>(out_t).target);
|
||||
}
|
||||
|
||||
uint8_t wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t)
|
||||
{
|
||||
if (out_t.type() == typeid(currency::tx_out_bare))
|
||||
{
|
||||
if (boost::get<currency::tx_out_bare>(out_t).target.type() == typeid(currency::txout_to_key))
|
||||
{
|
||||
return boost::get<currency::txout_to_key>(boost::get<currency::tx_out_bare>(out_t).target).mix_attr;
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_WALLET_CMN_ERR_EX("Unexpected type in out_get_mixin_attr");
|
||||
}
|
||||
}
|
||||
else if (out_t.type() == typeid(currency::tx_out_zarcanum))
|
||||
{
|
||||
return boost::get<currency::tx_out_zarcanum>(out_t).mix_attr;
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_WALLET_CMN_ERR_EX("Unexpected type in out_get_mixin_attr");
|
||||
}
|
||||
THROW_WALLET_CMN_ERR_EX("Unexpected out type im wallet: " << out_t.type().name());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool out_is_to_zarcanum(const currency::tx_out_v& out_t)
|
||||
{
|
||||
return out_t.type() == typeid(currency::tx_out_zarcanum);
|
||||
}
|
||||
|
||||
const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out_t, std::list<currency::htlc_info>& htlc_info_list)
|
||||
{
|
||||
if (out_t.type() == typeid(tx_out_bare))
|
||||
{
|
||||
const currency::tx_out_bare& out = boost::get<currency::tx_out_bare>(out_t);
|
||||
if (out.target.type() == typeid(currency::txout_to_key))
|
||||
{
|
||||
return boost::get<currency::txout_to_key>(out.target).key;
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(out.target.type() == typeid(currency::txout_htlc), "Unexpected out type in target wallet: " << out.target.type().name());
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(htlc_info_list.size() > 0, "Found txout_htlc out but htlc_info_list is empty");
|
||||
bool hltc_our_out_is_before_expiration = htlc_info_list.front().hltc_our_out_is_before_expiration;
|
||||
htlc_info_list.pop_front();
|
||||
if (hltc_our_out_is_before_expiration)
|
||||
{
|
||||
return boost::get<currency::txout_htlc>(out.target).pkey_redeem;
|
||||
}
|
||||
else
|
||||
{
|
||||
return boost::get<currency::txout_htlc>(out.target).pkey_refund;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(out_t.type() == typeid(currency::tx_out_zarcanum), "Unexpected out type im wallet: " << out_t.type().name());
|
||||
return boost::get<currency::tx_out_zarcanum>(out_t).stealth_address;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t height, const currency::block& b, const std::vector<uint64_t>* pglobal_indexes)
|
||||
{
|
||||
std::vector<std::string> recipients, remote_aliases;
|
||||
process_unconfirmed(tx, recipients, remote_aliases);
|
||||
|
||||
std::vector<wallet_out_info> outs;
|
||||
uint64_t tx_money_got_in_outs = 0;
|
||||
crypto::public_key tx_pub_key = null_pkey;
|
||||
bool r = parse_and_validate_tx_extra(tx, tx_pub_key);
|
||||
THROW_IF_TRUE_WALLET_EX(!r, error::tx_extra_parse_error, tx);
|
||||
|
||||
//check for transaction spends
|
||||
uint64_t tx_money_spent_in_ins = 0;
|
||||
process_transaction_context ptc = AUTO_VAL_INIT(ptc);
|
||||
|
||||
// check all outputs for spending (compare key images)
|
||||
money_transfer2_details mtd;
|
||||
size_t i = 0;
|
||||
bool is_pos_coinbase = false;
|
||||
bool coin_base_tx = is_coinbase(tx, is_pos_coinbase);
|
||||
ptc.coin_base_tx = is_coinbase(tx, ptc.is_pos_coinbase);
|
||||
//PoW block don't have change, so all outs supposed to be marked as "mined"
|
||||
bool is_derived_from_coinbase = !is_pos_coinbase;
|
||||
ptc.is_derived_from_coinbase = !ptc.is_pos_coinbase;
|
||||
ptc.height = height;
|
||||
|
||||
for(auto& in : tx.vin)
|
||||
{
|
||||
if (in.type() == typeid(currency::txin_to_key))
|
||||
ptc.sub_i = 0;
|
||||
VARIANT_SWITCH_BEGIN(in);
|
||||
VARIANT_CASE_CONST(currency::txin_to_key, intk)
|
||||
{
|
||||
const currency::txin_to_key& intk = boost::get<currency::txin_to_key>(in);
|
||||
|
||||
// check if this input spends our output
|
||||
uint64_t tid = UINT64_MAX;
|
||||
|
||||
if (is_auditable() && is_watch_only())
|
||||
{
|
||||
// tracking wallet, assuming all outputs are spent directly because of mix_attr = 1
|
||||
tid = get_directly_spent_transfer_id_by_input_in_tracking_wallet(intk);
|
||||
}
|
||||
else
|
||||
{
|
||||
// wallet with spend secret key -- we can calculate own key images and then search by them
|
||||
auto it = m_key_images.find(intk.k_image);
|
||||
if (it != m_key_images.end())
|
||||
{
|
||||
tid = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (tid != UINT64_MAX)
|
||||
{
|
||||
tx_money_spent_in_ins += intk.amount;
|
||||
transfer_details& td = m_transfers[tid];
|
||||
uint32_t flags_before = td.m_flags;
|
||||
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT;
|
||||
td.m_spent_height = height;
|
||||
if (coin_base_tx && td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER)
|
||||
is_derived_from_coinbase = true;
|
||||
else
|
||||
is_derived_from_coinbase = false;
|
||||
WLT_LOG_L0("Spent key out, transfer #" << tid << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height <<
|
||||
"; flags: " << flags_before << " -> " << td.m_flags);
|
||||
mtd.spent_indices.push_back(i);
|
||||
remove_transfer_from_expiration_list(tid);
|
||||
}
|
||||
process_input_t(intk, ptc, tx);
|
||||
}
|
||||
else if (in.type() == typeid(currency::txin_multisig))
|
||||
VARIANT_CASE_CONST(currency::txin_zarcanum_inputs, tinz)
|
||||
{
|
||||
crypto::hash multisig_id = boost::get<currency::txin_multisig>(in).multisig_out_id;
|
||||
for (const auto& e : tinz.elements)
|
||||
{
|
||||
process_input_t(e, ptc, tx);
|
||||
ptc.sub_i++;
|
||||
}
|
||||
}
|
||||
VARIANT_CASE_CONST(currency::txin_multisig, inms)
|
||||
{
|
||||
crypto::hash multisig_id = inms.multisig_out_id;
|
||||
auto it = m_multisig_transfers.find(multisig_id);
|
||||
if (it != m_multisig_transfers.end())
|
||||
{
|
||||
it->second.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT;
|
||||
it->second.m_spent_height = height;
|
||||
WLT_LOG_L0("Spent multisig out: " << multisig_id << ", amount: " << print_money(currency::get_amount_from_variant(in)) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height);
|
||||
mtd.spent_indices.push_back(i);
|
||||
ptc.mtd.spent_indices.push_back(ptc.i);
|
||||
}
|
||||
}
|
||||
else if (in.type() == typeid(currency::txin_htlc))
|
||||
VARIANT_CASE_CONST(currency::txin_htlc, in_htlc)
|
||||
{
|
||||
const currency::txin_htlc& in_htlc = boost::get<currency::txin_htlc>(in);
|
||||
if (in_htlc.key_offsets.size() != 1)
|
||||
{
|
||||
LOG_ERROR("in_htlc.key_offsets.size() != 1, skip inout");
|
||||
|
|
@ -428,7 +487,8 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
tdeohi.redeem_tx_id = get_transaction_hash(tx);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
VARIANT_SWITCH_END();
|
||||
ptc.i++;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -438,6 +498,12 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
*/
|
||||
uint64_t max_out_unlock_time = 0;
|
||||
|
||||
std::vector<wallet_out_info> outs;
|
||||
uint64_t tx_money_got_in_outs = 0;
|
||||
crypto::public_key tx_pub_key = null_pkey;
|
||||
bool r = parse_and_validate_tx_extra(tx, tx_pub_key);
|
||||
THROW_IF_TRUE_WALLET_EX(!r, error::tx_extra_parse_error, tx);
|
||||
|
||||
//check for transaction income
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
std::list<htlc_info> htlc_info_list;
|
||||
|
|
@ -479,34 +545,12 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
{
|
||||
size_t o = outs[i_in_outs].index;
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(o < tx.vout.size(), "wrong out in transaction: internal index=" << o << ", total_outs=" << tx.vout.size());
|
||||
VARIANT_SWITCH_BEGIN(tx.vout[o]);
|
||||
VARIANT_CASE_CONST(tx_out_bare, out)
|
||||
{
|
||||
if (out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc))
|
||||
const currency::tx_out_v& out_v = tx.vout[o];
|
||||
|
||||
if (out_is_to_key(out_v) || out_is_to_htlc(out_v) || out_is_to_zarcanum(out_v)) // out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc))
|
||||
{
|
||||
bool hltc_our_out_is_before_expiration = false;
|
||||
crypto::public_key out_key = null_pkey;
|
||||
if (out.target.type() == typeid(txout_to_key))
|
||||
{
|
||||
out_key = boost::get<currency::txout_to_key>(out.target).key;
|
||||
}
|
||||
else if (out.target.type() == typeid(txout_htlc))
|
||||
{
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(htlc_info_list.size() > 0, "Found txout_htlc out but htlc_info_list is empty");
|
||||
if (htlc_info_list.front().hltc_our_out_is_before_expiration)
|
||||
{
|
||||
out_key = boost::get<currency::txout_htlc>(out.target).pkey_redeem;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_key = boost::get<currency::txout_htlc>(out.target).pkey_refund;
|
||||
}
|
||||
htlc_info_list.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IF_TRUE_WALLET_INT_ERR_EX(false, "Unexpected out type im wallet: " << out.target.type().name());
|
||||
}
|
||||
crypto::public_key out_key = out_get_pub_key(out_v, htlc_info_list);
|
||||
//const currency::txout_to_key& otk = boost::get<currency::txout_to_key>(out.target);
|
||||
|
||||
// obtain key image for this output
|
||||
|
|
@ -553,32 +597,33 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0);
|
||||
if (m_wcallback)
|
||||
m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str());
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tx_money_got_in_outs >= out.amount, "tx_money_got_in_outs: " << tx_money_got_in_outs << ", out.amount:" << out.amount);
|
||||
tx_money_got_in_outs -= out.amount;
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tx_money_got_in_outs >= outs[i_in_outs].amount, "tx_money_got_in_outs: " << tx_money_got_in_outs << ", out.amount:" << outs[i_in_outs].amount);
|
||||
tx_money_got_in_outs -= outs[i_in_outs].amount;
|
||||
continue; // skip the output
|
||||
}
|
||||
}
|
||||
|
||||
if (is_auditable() && out.target.type() == typeid(txout_to_key) &&
|
||||
boost::get<txout_to_key>(out.target).mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
|
||||
if (is_auditable() && (out_is_to_key(out_v) || out_is_to_zarcanum(out_v)) &&
|
||||
out_get_mixin_attr(out_v) != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "output #" << o << " from tx " << get_transaction_hash(tx) << " with amount " << print_money_brief(out.amount)
|
||||
<< " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)boost::get<txout_to_key>(out.target).mix_attr << ". Output IGNORED.";
|
||||
ss << "output #" << o << " from tx " << get_transaction_hash(tx) << " with amount " << print_money_brief(outs[i_in_outs].amount)
|
||||
<< " is targeted to this auditable wallet and has INCORRECT mix_attr = " << (uint64_t)out_get_mixin_attr(out_v) << ". Output IGNORED.";
|
||||
WLT_LOG_RED(ss.str(), LOG_LEVEL_0);
|
||||
if (m_wcallback)
|
||||
m_wcallback->on_message(i_wallet2_callback::ms_red, ss.str());
|
||||
tx_money_got_in_outs -= out.amount;
|
||||
tx_money_got_in_outs -= outs[i_in_outs].amount;
|
||||
continue; // skip the output
|
||||
}
|
||||
|
||||
mtd.receive_indices.push_back(o);
|
||||
ptc.mtd.receive_indices.push_back(o);
|
||||
|
||||
m_transfers.push_back(boost::value_initialized<transfer_details>());
|
||||
transfer_details& td = m_transfers.back();
|
||||
td.m_ptx_wallet_info = pwallet_info;
|
||||
td.m_internal_output_index = o;
|
||||
td.m_key_image = ki;
|
||||
td.m_amount = outs[i_in_outs].amount;
|
||||
if (m_use_deffered_global_outputs)
|
||||
{
|
||||
if (pglobal_indexes && pglobal_indexes->size() > o)
|
||||
|
|
@ -592,19 +637,18 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(pglobal_indexes->size() > o, "pglobal_indexes size()(" << pglobal_indexes->size() << ") <= o " << o);
|
||||
td.m_global_output_index = (*pglobal_indexes)[o];
|
||||
}
|
||||
if (coin_base_tx)
|
||||
if (ptc.coin_base_tx)
|
||||
{
|
||||
//last out in coinbase tx supposed to be change from coinstake
|
||||
if (!(o == tx.vout.size() - 1 && !is_derived_from_coinbase))
|
||||
if (!(o == tx.vout.size() - 1 && !ptc.is_derived_from_coinbase))
|
||||
{
|
||||
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER;
|
||||
}
|
||||
}
|
||||
uint64_t amount = out.amount;
|
||||
size_t transfer_index = m_transfers.size() - 1;
|
||||
if (out.target.type() == typeid(txout_htlc))
|
||||
if (out_is_to_htlc(out_v))
|
||||
{
|
||||
const txout_htlc& hltc = boost::get<currency::txout_htlc>(out.target);
|
||||
const currency::txout_htlc& hltc = out_get_htlc(out_v);
|
||||
//mark this as spent
|
||||
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT;
|
||||
//create entry for htlc input
|
||||
|
|
@ -620,7 +664,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
}
|
||||
|
||||
//active htlc
|
||||
auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index);
|
||||
auto amount_gindex_pair = std::make_pair(td.m_amount, td.m_global_output_index);
|
||||
m_active_htlcs[amount_gindex_pair] = transfer_index;
|
||||
m_active_htlcs_txid[get_transaction_hash(tx)] = transfer_index;
|
||||
//add payer to extra options
|
||||
|
|
@ -644,29 +688,29 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
if (td.m_key_image != currency::null_ki)
|
||||
m_key_images[td.m_key_image] = transfer_index;
|
||||
|
||||
add_transfer_to_transfers_cache(amount, transfer_index);
|
||||
add_transfer_to_transfers_cache(td.m_amount, transfer_index);
|
||||
|
||||
if (is_watch_only() && is_auditable())
|
||||
{
|
||||
WLT_CHECK_AND_ASSERT_MES_NO_RET(td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED, "td.m_global_output_index != WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED validation failed");
|
||||
auto amount_gindex_pair = std::make_pair(amount, td.m_global_output_index);
|
||||
WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << amount << ", gindex " << td.m_global_output_index << " already exists");
|
||||
auto amount_gindex_pair = std::make_pair(td.m_amount, td.m_global_output_index);
|
||||
WLT_CHECK_AND_ASSERT_MES_NO_RET(m_amount_gindex_to_transfer_id.count(amount_gindex_pair) == 0, "update m_amount_gindex_to_transfer_id: amount " << td.m_amount << ", gindex " << td.m_global_output_index << " already exists");
|
||||
m_amount_gindex_to_transfer_id[amount_gindex_pair] = transfer_index;
|
||||
}
|
||||
|
||||
if (max_out_unlock_time < get_tx_unlock_time(tx, o))
|
||||
max_out_unlock_time = get_tx_unlock_time(tx, o);
|
||||
|
||||
if (out.target.type() == typeid(txout_to_key))
|
||||
if (out_is_to_key(out_v) || out_is_to_zarcanum(out_v))
|
||||
{
|
||||
WLT_LOG_L0("Received money, transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height);
|
||||
}
|
||||
else if (out.target.type() == typeid(txout_htlc))
|
||||
else if (out_is_to_htlc(out_v))
|
||||
{
|
||||
WLT_LOG_L0("Detected HTLC[" << (td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM ? "REDEEM" : "REFUND") << "], transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height);
|
||||
}
|
||||
}
|
||||
else if (out.target.type() == typeid(txout_multisig))
|
||||
else if (out_is_multisig(out_v))
|
||||
{
|
||||
crypto::hash multisig_id = currency::get_multisig_out_id(tx, o);
|
||||
WLT_CHECK_AND_ASSERT_MES_NO_RET(m_multisig_transfers.count(multisig_id) == 0, "multisig_id = " << multisig_id << " already in multisig container");
|
||||
|
|
@ -676,16 +720,14 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
WLT_LOG_L0("Received multisig, multisig out id: " << multisig_id << ", amount: " << tdb.amount() << ", with tx: " << get_transaction_hash(tx));
|
||||
}
|
||||
}
|
||||
VARIANT_CASE_CONST(tx_out_zarcanum, o);
|
||||
//@#@
|
||||
VARIANT_SWITCH_END();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string payment_id;
|
||||
if (tx_money_got_in_outs && get_payment_id_from_tx(tx.attachment, payment_id))
|
||||
{
|
||||
uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0;
|
||||
uint64_t received = (ptc.tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - ptc.tx_money_spent_in_ins : 0;
|
||||
if (0 < received && payment_id.size())
|
||||
{
|
||||
payment_details payment;
|
||||
|
|
@ -699,34 +741,34 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
|
|||
}
|
||||
|
||||
|
||||
if (tx_money_spent_in_ins)
|
||||
if (ptc.tx_money_spent_in_ins)
|
||||
{//this actually is transfer transaction, notify about spend
|
||||
if (tx_money_spent_in_ins > tx_money_got_in_outs)
|
||||
if (ptc.tx_money_spent_in_ins > tx_money_got_in_outs)
|
||||
{//usual transfer
|
||||
handle_money_spent2(b, tx, tx_money_spent_in_ins - (tx_money_got_in_outs+get_tx_fee(tx)), mtd, recipients, remote_aliases);
|
||||
handle_money_spent2(b, tx, ptc.tx_money_spent_in_ins - (tx_money_got_in_outs+get_tx_fee(tx)), ptc.mtd, recipients, remote_aliases);
|
||||
}
|
||||
else
|
||||
{//strange transfer, seems that in one transaction have transfers from different wallets.
|
||||
if (!is_coinbase(tx))
|
||||
{
|
||||
WLT_LOG_RED("Unusual transaction " << currency::get_transaction_hash(tx) << ", tx_money_spent_in_ins: " << tx_money_spent_in_ins << ", tx_money_got_in_outs: " << tx_money_got_in_outs, LOG_LEVEL_0);
|
||||
WLT_LOG_RED("Unusual transaction " << currency::get_transaction_hash(tx) << ", tx_money_spent_in_ins: " << ptc.tx_money_spent_in_ins << ", tx_money_got_in_outs: " << tx_money_got_in_outs, LOG_LEVEL_0);
|
||||
}
|
||||
handle_money_received2(b, tx, (tx_money_got_in_outs - (tx_money_spent_in_ins - get_tx_fee(tx))), mtd);
|
||||
handle_money_received2(b, tx, (tx_money_got_in_outs - (ptc.tx_money_spent_in_ins - get_tx_fee(tx))), ptc.mtd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(tx_money_got_in_outs)
|
||||
handle_money_received2(b, tx, tx_money_got_in_outs, mtd);
|
||||
handle_money_received2(b, tx, tx_money_got_in_outs, ptc.mtd);
|
||||
else if (currency::is_derivation_used_to_encrypt(tx, derivation))
|
||||
{
|
||||
//transaction doesn't transfer actually money, bud bring some information
|
||||
handle_money_received2(b, tx, 0, mtd);
|
||||
handle_money_received2(b, tx, 0, ptc.mtd);
|
||||
}
|
||||
else if (mtd.spent_indices.size())
|
||||
else if (ptc.mtd.spent_indices.size())
|
||||
{
|
||||
// multisig spend detected
|
||||
handle_money_spent2(b, tx, 0, mtd, recipients, remote_aliases);
|
||||
handle_money_spent2(b, tx, 0, ptc.mtd, recipients, remote_aliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -833,6 +875,7 @@ void wallet2::accept_proposal(const crypto::hash& contract_id, uint64_t b_accept
|
|||
tdb.m_internal_output_index = n;
|
||||
tdb.m_flags &= ~(WALLET_TRANSFER_DETAIL_FLAG_SPENT);
|
||||
//---------------------------------
|
||||
//@#@ todo: proper handling with zarcanum_based stuff
|
||||
//figure out fee that was left for release contract
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(tx.vout[n].type() == typeid(tx_out_bare), "Unexpected output type in accept proposal");
|
||||
THROW_IF_FALSE_WALLET_INT_ERR_EX(boost::get<tx_out_bare>(tx.vout[n]).amount > (contr_it->second.private_detailes.amount_to_pay +
|
||||
|
|
@ -1404,7 +1447,7 @@ void wallet2::unprocess_htlc_triggers_on_block_removed(uint64_t height)
|
|||
}
|
||||
//re-add to active contracts
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type() == typeid(tx_out_bare), std::string("Unexprected type of out in unprocess_htlc_triggers_on_block_removed : ") + tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type().name());
|
||||
auto pair_key = std::make_pair(boost::get<tx_out_bare>(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index]).amount, tr.m_global_output_index);
|
||||
auto pair_key = std::make_pair(tr.m_amount, tr.m_global_output_index);
|
||||
auto it_active_htlc = m_active_htlcs.find(pair_key);
|
||||
if (it_active_htlc != m_active_htlcs.end())
|
||||
{
|
||||
|
|
@ -1467,7 +1510,7 @@ void wallet2::process_htlc_triggers_on_block_added(uint64_t height)
|
|||
|
||||
//remove it from active contracts
|
||||
CHECK_AND_ASSERT_MES(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type() == typeid(tx_out_bare), void(), "Unexpected type out in process_htlc_triggers_on_block_added: " << tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index].type().name());
|
||||
uint64_t amount = boost::get<tx_out_bare>(tr.m_ptx_wallet_info->m_tx.vout[tr.m_internal_output_index]).amount;
|
||||
uint64_t amount = tr.m_amount;
|
||||
|
||||
auto it_active_htlc = m_active_htlcs.find(std::make_pair(amount, tr.m_global_output_index));
|
||||
if (it_active_htlc == m_active_htlcs.end())
|
||||
|
|
@ -1832,32 +1875,42 @@ bool wallet2::has_related_alias_entry_unconfirmed(const currency::transaction& t
|
|||
return false;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_directly_spent_transfer_id_by_input_in_tracking_wallet(const currency::txin_to_key& intk)
|
||||
uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk)
|
||||
{
|
||||
return get_directly_spent_transfer_index_by_input_in_tracking_wallet(intk.amount, intk.key_offsets);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::zarcanum_input& inzk)
|
||||
{
|
||||
return get_directly_spent_transfer_index_by_input_in_tracking_wallet(0, inzk.key_offsets);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::get_directly_spent_transfer_index_by_input_in_tracking_wallet(uint64_t amount, const std::vector<currency::txout_ref_v> & key_offsets)
|
||||
{
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(is_auditable() && is_watch_only(), "this is not an auditable-watch-only (tracking) wallet");
|
||||
|
||||
uint64_t tid = UINT64_MAX;
|
||||
|
||||
// try to find a reference among own UTXOs
|
||||
std::vector<txout_ref_v> abs_key_offsets = relative_output_offsets_to_absolute(intk.key_offsets); // potential speed-up: don't convert to abs offsets as we interested only in direct spends for auditable wallets. Now it's kind a bit paranoid.
|
||||
std::vector<txout_ref_v> abs_key_offsets = relative_output_offsets_to_absolute(key_offsets); // potential speed-up: don't convert to abs offsets as we interested only in direct spends for auditable wallets. Now it's kind a bit paranoid.
|
||||
for (auto v : abs_key_offsets)
|
||||
{
|
||||
if (v.type() != typeid(uint64_t))
|
||||
continue;
|
||||
uint64_t gindex = boost::get<uint64_t>(v);
|
||||
auto it = m_amount_gindex_to_transfer_id.find(std::make_pair(intk.amount, gindex));
|
||||
auto it = m_amount_gindex_to_transfer_id.find(std::make_pair(amount, gindex));
|
||||
if (it != m_amount_gindex_to_transfer_id.end())
|
||||
{
|
||||
tid = it->second;
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tid < m_transfers.size(), "invalid tid: " << tid << ", ref from input with amount: " << intk.amount << ", gindex: " << gindex);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(tid < m_transfers.size(), "invalid tid: " << tid << ", ref from input with amount: " << amount << ", gindex: " << gindex);
|
||||
auto& td = m_transfers[it->second];
|
||||
if (intk.key_offsets.size() != 1)
|
||||
if (key_offsets.size() != 1)
|
||||
{
|
||||
// own output was used in non-direct transaction
|
||||
// the core should not allow this to happen, the only way it may happen - mixing in own output that was sent without mix_attr == 1
|
||||
// log strange situation
|
||||
std::stringstream ss;
|
||||
ss << "own transfer tid=" << tid << " tx=" << td.tx_hash() << " mix_attr=" << td.mix_attr() << ", is referenced by a transaction with mixins, ref from input with amount: " << intk.amount << ", gindex: " << gindex;
|
||||
ss << "own transfer tid=" << tid << " tx=" << td.tx_hash() << " mix_attr=" << td.mix_attr() << ", is referenced by a transaction with mixins, ref from input with amount: " << amount << ", gindex: " << gindex;
|
||||
WLT_LOG_YELLOW(ss.str(), LOG_LEVEL_0);
|
||||
if (m_wcallback)
|
||||
m_wcallback->on_message(i_wallet2_callback::ms_yellow, ss.str());
|
||||
|
|
@ -1867,7 +1920,7 @@ uint64_t wallet2::get_directly_spent_transfer_id_by_input_in_tracking_wallet(con
|
|||
tid = UINT64_MAX;
|
||||
continue;
|
||||
}
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(td.m_spent_height == 0, "transfer is spent in blockchain, tid: " << tid << ", ref from input with amount: " << intk.amount << ", gindex: " << gindex);
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(td.m_spent_height == 0, "transfer is spent in blockchain, tid: " << tid << ", ref from input with amount: " << amount << ", gindex: " << gindex);
|
||||
// okay, own output is being spent, return it
|
||||
break;
|
||||
}
|
||||
|
|
@ -1961,7 +2014,7 @@ void wallet2::scan_tx_pool(bool& has_related_alias_in_unconfirmed)
|
|||
if (is_auditable() && is_watch_only())
|
||||
{
|
||||
// tracking wallet, assuming all outputs are spent directly because of mix_attr = 1
|
||||
tid = get_directly_spent_transfer_id_by_input_in_tracking_wallet(intk);
|
||||
tid = get_directly_spent_transfer_index_by_input_in_tracking_wallet(intk.amount, intk.key_offsets);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -5115,7 +5168,7 @@ bool wallet2::prepare_free_transfers_cache(uint64_t fake_outputs_count)
|
|||
if (is_transfer_able_to_go(td, fake_outputs_count))
|
||||
{
|
||||
//@#@
|
||||
m_found_free_amounts[boost::get<tx_out_bare>(td.m_ptx_wallet_info->m_tx.vout[td.m_internal_output_index]).amount].insert(i);
|
||||
m_found_free_amounts[td.amount()].insert(i);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
|
@ -5166,6 +5219,18 @@ bool wallet2::read_money_transfer2_details_from_tx(const transaction& tx, const
|
|||
wtd.spn.push_back(in_to_key.amount);
|
||||
}
|
||||
}
|
||||
else if (i.type() == typeid(currency::txin_zarcanum_inputs))
|
||||
{
|
||||
const currency::txin_zarcanum_inputs& in_to_zc = boost::get<currency::txin_zarcanum_inputs>(i);
|
||||
for (auto& e : in_to_zc.elements)
|
||||
{
|
||||
auto it = m_key_images.find(e.k_image);
|
||||
//should we panic if image not found?
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_key_images.end(), "[read_money_transfer2_details_from_tx]Unknown key image in tx: " << get_transaction_hash(tx));
|
||||
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second < m_transfers.size(), "[read_money_transfer2_details_from_tx]Index out of range for key image in tx: " << get_transaction_hash(tx));
|
||||
wtd.spn.push_back(m_transfers[it->second].amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ namespace tools
|
|||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
typedef tools::pod_array_file_container<out_key_to_ki> pending_ki_file_container_t;
|
||||
|
||||
namespace detail
|
||||
|
|
@ -378,10 +379,11 @@ namespace tools
|
|||
uint64_t m_internal_output_index;
|
||||
uint64_t m_spent_height;
|
||||
uint32_t m_flags;
|
||||
uint64_t m_amount;
|
||||
|
||||
// @#@ will throw if type is not tx_out_bare, TODO: change according to new model,
|
||||
// need to replace all get_tx_out_bare_from_out_v() to proper code
|
||||
uint64_t amount() const { return currency::get_tx_out_bare_from_out_v(m_ptx_wallet_info->m_tx.vout[m_internal_output_index]).amount; }
|
||||
uint64_t amount() const { return m_amount; }
|
||||
const currency::tx_out_bare& output() const { return currency::get_tx_out_bare_from_out_v(m_ptx_wallet_info->m_tx.vout[m_internal_output_index]); }
|
||||
uint8_t mix_attr() const { return output().target.type() == typeid(currency::txout_to_key) ? boost::get<const currency::txout_to_key&>(output().target).mix_attr : UINT8_MAX; }
|
||||
crypto::hash tx_hash() const { return get_transaction_hash(m_ptx_wallet_info->m_tx); }
|
||||
|
|
@ -394,6 +396,7 @@ namespace tools
|
|||
KV_SERIALIZE(m_internal_output_index)
|
||||
KV_SERIALIZE(m_spent_height)
|
||||
KV_SERIALIZE(m_flags)
|
||||
KV_SERIALIZE(m_amount)
|
||||
KV_SERIALIZE_EPHEMERAL_N(uint64_t, tools::wallet2::transfer_details_base_to_amount, "amount")
|
||||
KV_SERIALIZE_EPHEMERAL_N(std::string, tools::wallet2::transfer_details_base_to_tx_hash, "tx_id")
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
|
@ -416,6 +419,7 @@ namespace tools
|
|||
crypto::key_image m_key_image; //TODO: key_image stored twice :(
|
||||
std::vector<transfer_details_extra_options_v> varian_options;
|
||||
|
||||
//v2
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(m_global_output_index)
|
||||
KV_SERIALIZE_POD_AS_HEX_STRING(m_key_image)
|
||||
|
|
@ -499,6 +503,21 @@ namespace tools
|
|||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct process_transaction_context
|
||||
{
|
||||
uint64_t tx_money_spent_in_ins;
|
||||
// check all outputs for spending (compare key images)
|
||||
money_transfer2_details mtd;
|
||||
bool is_pos_coinbase;
|
||||
bool coin_base_tx;
|
||||
//PoW block don't have change, so all outs supposed to be marked as "mined"
|
||||
bool is_derived_from_coinbase;
|
||||
size_t i;
|
||||
size_t sub_i;
|
||||
uint64_t height;
|
||||
};
|
||||
|
||||
|
||||
void assign_account(const currency::account_base& acc);
|
||||
void generate(const std::wstring& path, const std::string& password, bool auditable_wallet);
|
||||
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password);
|
||||
|
|
@ -964,6 +983,8 @@ private:
|
|||
void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const wallet_public::wallet_transfer_info& wti) const;
|
||||
void change_contract_state(wallet_public::escrow_contract_details_basic& contract, uint32_t new_state, const crypto::hash& contract_id, const std::string& reason = "internal intention") const;
|
||||
|
||||
template<typename input_t>
|
||||
bool process_input_t(const input_t& in_t, wallet2::process_transaction_context& ptc, const currency::transaction& tx);
|
||||
|
||||
const construct_tx_param& get_default_construct_tx_param();
|
||||
|
||||
|
|
@ -1015,8 +1036,13 @@ private:
|
|||
//void check_if_block_matched(uint64_t i, const crypto::hash& id, bool& block_found, bool& block_matched, bool& full_reset_needed);
|
||||
uint64_t detach_from_block_ids(uint64_t height);
|
||||
uint64_t get_wallet_minimum_height();
|
||||
uint64_t get_directly_spent_transfer_id_by_input_in_tracking_wallet(const currency::txin_to_key& intk);
|
||||
uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(uint64_t amount, const std::vector<currency::txout_ref_v> & key_offsets);
|
||||
uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::txin_to_key& intk);
|
||||
uint64_t get_directly_spent_transfer_index_by_input_in_tracking_wallet(const currency::zarcanum_input& inzk);
|
||||
bool is_in_hardfork_zone(uint64_t hardfork_index);
|
||||
uint8_t out_get_mixin_attr(const currency::tx_out_v& out_t);
|
||||
const crypto::public_key& out_get_pub_key(const currency::tx_out_v& out_t, std::list<currency::htlc_info>& htlc_info_list);
|
||||
|
||||
|
||||
void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector<currency::extra_v>& extra);
|
||||
void remove_transfer_from_amount_gindex_map(uint64_t tid);
|
||||
|
|
@ -1086,6 +1112,7 @@ private:
|
|||
BOOST_CLASS_VERSION(tools::wallet2, WALLET_FILE_SERIALIZATION_VERSION)
|
||||
BOOST_CLASS_VERSION(tools::wallet_public::wallet_transfer_info, 10)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 3)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details_base, 2)
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
|
@ -1106,6 +1133,12 @@ namespace boost
|
|||
a & x.m_internal_output_index;
|
||||
a & x.m_flags;
|
||||
a & x.m_spent_height;
|
||||
if (ver < 2)
|
||||
{
|
||||
x.m_amount = currency::get_tx_out_bare_from_out_v(x.m_ptx_wallet_info->m_tx.vout[x.m_internal_output_index]).amount;
|
||||
return;
|
||||
}
|
||||
a & x.m_amount;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1217,6 +1250,46 @@ namespace boost
|
|||
|
||||
namespace tools
|
||||
{
|
||||
template<typename input_t>
|
||||
bool wallet2::process_input_t(const input_t& in_t, wallet2::process_transaction_context& ptc, const currency::transaction& tx)
|
||||
{
|
||||
// check if this input spends our output
|
||||
uint64_t tr_index = UINT64_MAX;
|
||||
|
||||
if (this->is_auditable() && this->is_watch_only())
|
||||
{
|
||||
// tracking wallet, assuming all outputs are spent directly because of mix_attr = 1
|
||||
tr_index = this->get_directly_spent_transfer_index_by_input_in_tracking_wallet(in_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
// wallet with spend secret key -- we can calculate own key images and then search by them
|
||||
auto it = m_key_images.find(in_t.k_image);
|
||||
if (it != m_key_images.end())
|
||||
{
|
||||
tr_index = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (tr_index != UINT64_MAX)
|
||||
{
|
||||
transfer_details& td = m_transfers[tr_index];
|
||||
ptc.tx_money_spent_in_ins += td.amount();
|
||||
uint32_t flags_before = td.m_flags;
|
||||
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT;
|
||||
td.m_spent_height = ptc.height;
|
||||
if (ptc.coin_base_tx && td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER)
|
||||
ptc.is_derived_from_coinbase = true;
|
||||
else
|
||||
ptc.is_derived_from_coinbase = false;
|
||||
WLT_LOG_L0("Spent key out, transfer #" << tr_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << ptc.height <<
|
||||
"; flags: " << flags_before << " -> " << td.m_flags);
|
||||
ptc.mtd.spent_indices.push_back(ptc.i);
|
||||
remove_transfer_from_expiration_list(tr_index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename idle_condition_cb_t> //do refresh as external callback
|
||||
bool wallet2::scan_pos(mining_context& cxt,
|
||||
std::atomic<bool>& stop,
|
||||
|
|
|
|||
|
|
@ -751,3 +751,10 @@ if (cond)
|
|||
LOG_ERROR(" (" << #cond << ") is FALSE. THROW EXCEPTION: wallet_common_error"); \
|
||||
tools::error::throw_wallet_ex<tools::error::wallet_common_error>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ss.str()); \
|
||||
}
|
||||
#define THROW_WALLET_CMN_ERR_EX(mess) \
|
||||
{ \
|
||||
std::stringstream ss; \
|
||||
ss << mess; \
|
||||
LOG_ERROR("THROW EXCEPTION: wallet_common_error"); \
|
||||
tools::error::throw_wallet_ex<tools::error::wallet_common_error>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ss.str()); \
|
||||
}
|
||||
|
|
@ -160,7 +160,7 @@ bool gen_chain_switch_1::check_split_not_switched(currency::core& c, size_t ev_i
|
|||
CHECK_TEST_CONDITION(r);
|
||||
CHECK_EQ(1, tx_pool.size());
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
std::vector<wallet_out_info> tx_outs;
|
||||
uint64_t transfered;
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
lookup_acc_outs(m_recipient_account_4.get_keys(), tx_pool.front(), get_tx_pub_key_from_extra(tx_pool.front()), tx_outs, transfered, derivation);
|
||||
|
|
@ -223,7 +223,7 @@ bool gen_chain_switch_1::check_split_switched(currency::core& c, size_t ev_index
|
|||
CHECK_EQ(1, tx_pool.size());
|
||||
CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front()));
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
std::vector<wallet_out_info> tx_outs;
|
||||
uint64_t transfered;
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
lookup_acc_outs(m_recipient_account_2.get_keys(), tx_pool.front(), tx_outs, transfered, derivation);
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ bool gen_ring_signature_big::check_balances_2(currency::core& c, size_t ev_index
|
|||
CHECK_EQ(balance, get_balance(an_account, chain, mtx));
|
||||
}
|
||||
|
||||
std::vector<size_t> tx_outs;
|
||||
std::vector<wallet_out_info> tx_outs;
|
||||
uint64_t transfered;
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
lookup_acc_outs(m_alice_account.get_keys(), boost::get<transaction>(events[events.size() - 3]), get_tx_pub_key_from_extra(boost::get<transaction>(events[events.size() - 3])), tx_outs, transfered, derivation);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ bool test_transaction_generation_and_ring_signature()
|
|||
r = crypto::check_ring_signature(pref_hash, boost::get<txin_to_key>(tx_rc1.vin[0]).k_image, output_keys, &boost::get<currency::NLSAG_sig>(tx_rc1.signatures[0]).s[0]);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to check ring signature");
|
||||
|
||||
std::vector<size_t> outs;
|
||||
std::vector<wallet_out_info> outs;
|
||||
uint64_t money = 0;
|
||||
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
|
||||
r = lookup_acc_outs(rv_acc.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money, derivation);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue