1
0
Fork 0
forked from lthn/blockchain

Merge branch 'zarcanum_wallet' into zarcanum

This commit is contained in:
sowle 2022-07-11 01:29:44 +02:00
commit 74f6ce33ac
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
10 changed files with 445 additions and 228 deletions

View file

@ -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;

View file

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

View file

@ -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,

View file

@ -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;

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View file

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