Skip to content
Permalink
Browse files

Wallet: Distingush amounts for a single subaddress

Adding a new `amounts` field ot the output of `get_transfers` RPC
method. This field specifies individual payments made to a single
subaddress in a single transaction, e.g., made by this command:

    transfer <addr1> <amount1> <addr1> <amount2>
  • Loading branch information
Tadeas Moravec
Tadeas Moravec committed Nov 27, 2019
1 parent 411f1b0 commit 3f53688dcdeeddb5982cbf5d0499827b0f800484
Showing with 46 additions and 14 deletions.
  1. +38 −13 src/wallet/wallet2.cpp
  2. +3 −1 src/wallet/wallet2.h
  3. +2 −0 src/wallet/wallet_rpc_server.cpp
  4. +3 −0 src/wallet/wallet_rpc_server_commands_defs.h
@@ -1729,7 +1729,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool)
void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, amounts_container> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool)
{
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");

@@ -1774,9 +1774,12 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
return;
}
outs.push_back(i);
THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered,

amounts_container& tx_money_got_in_out_this_output = tx_money_got_in_outs[tx_scan_info.received->index]; // Only for readability on the following lines.
uint64_t in_outs_sum = std::accumulate(tx_money_got_in_out_this_output.begin(), tx_money_got_in_out_this_output.end(), (uint64_t)0);
THROW_WALLET_EXCEPTION_IF(in_outs_sum >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered,
error::wallet_internal_error, "Overflow in received amounts");
tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered;
tx_money_got_in_out_this_output.push_back(tx_scan_info.money_transfered);
tx_scan_info.amount = tx_scan_info.money_transfered;
++num_vouts_received;
}
@@ -1824,7 +1827,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
std::unordered_map<cryptonote::subaddress_index, amounts_container> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
bool notify = false;

@@ -2097,21 +2100,36 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
<< " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
<< (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
<< print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored");
THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,

amounts_container& tx_money_got_in_out_this_output = tx_money_got_in_outs[tx_scan_info[o].received->index]; // Only for readability on the following lines.
uint64_t in_outs_sum = std::accumulate(tx_money_got_in_out_this_output.begin(), tx_money_got_in_out_this_output.end(), (uint64_t)0);
THROW_WALLET_EXCEPTION_IF(in_outs_sum < tx_scan_info[o].amount,
error::wallet_internal_error, "Unexpected values of new and old outputs");

auto amount_iterator = std::find(tx_money_got_in_out_this_output.begin(), tx_money_got_in_out_this_output.end(), tx_scan_info[o].amount);
THROW_WALLET_EXCEPTION_IF(amount_iterator == tx_money_got_in_out_this_output.end(),
error::wallet_internal_error, "Unexpected values of new and old outputs");
tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
tx_money_got_in_out_this_output.erase(amount_iterator);
}
else
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
<< " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
<< print_money(m_transfers[kit->second].amount()) << ", replacing with new output");

// The new larger output replaced a previous smaller one
THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,

amounts_container& tx_money_got_in_out_this_output = tx_money_got_in_outs[tx_scan_info[o].received->index]; // Only for readability on the following lines.
uint64_t in_outs_sum = std::accumulate(tx_money_got_in_out_this_output.begin(), tx_money_got_in_out_this_output.end(), (uint64_t)0);
THROW_WALLET_EXCEPTION_IF(in_outs_sum < tx_scan_info[o].amount,
error::wallet_internal_error, "Unexpected values of new and old outputs");
THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount,
error::wallet_internal_error, "Unexpected values of new and old outputs");
tx_money_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();

auto amount_iterator = std::find(tx_money_got_in_out_this_output.begin(), tx_money_got_in_out_this_output.end(), tx_scan_info[o].amount);
THROW_WALLET_EXCEPTION_IF(amount_iterator == tx_money_got_in_out_this_output.end(),
error::wallet_internal_error, "Unexpected values of new and old outputs");
tx_money_got_in_out_this_output.erase(amount_iterator);

uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
uint64_t extra_amount = amount - m_transfers[kit->second].amount();
@@ -2246,10 +2264,16 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
if (tx_money_spent_in_ins > 0 && !pool)
{
uint64_t self_received = std::accumulate<decltype(tx_money_got_in_outs.begin()), uint64_t>(tx_money_got_in_outs.begin(), tx_money_got_in_outs.end(), 0,
[&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
[&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, amounts_container>& p)
{
return acc + (p.first.major == *subaddr_account ? p.second : 0);
uint64_t subaddr_sum(0);
if (p.first.major == *subaddr_account)
{
subaddr_sum = std::accumulate(p.second.begin(), p.second.end(), (uint64_t)0);
}
return acc + subaddr_sum;
});

process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
// if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
if (tx_money_spent_in_ins == self_received + fee)
@@ -2267,7 +2291,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
if (subaddr_account && i->first.major == *subaddr_account)
{
sub_change += i->second;
sub_change += std::accumulate(i->second.begin(), i->second.end(), (uint64_t)0);
i = tx_money_got_in_outs.erase(i);
}
else
@@ -2326,7 +2350,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote

uint64_t total_received_2 = sub_change;
for (const auto& i : tx_money_got_in_outs)
total_received_2 += i.second;
total_received_2 += std::accumulate(i.second.begin(), i.second.end(), (uint64_t)0);
if (total_received_1 != total_received_2)
{
const el::Level level = el::Level::Warning;
@@ -2344,7 +2368,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
payment_details payment;
payment.m_tx_hash = txid;
payment.m_fee = fee;
payment.m_amount = i.second;
payment.m_amount = std::accumulate(i.second.begin(), i.second.end(), (uint64_t)0);
payment.m_amounts = i.second;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
payment.m_timestamp = ts;
@@ -358,10 +358,12 @@ namespace tools
END_SERIALIZE()
};

typedef std::vector<uint64_t> amounts_container;
struct payment_details
{
crypto::hash m_tx_hash;
uint64_t m_amount;
amounts_container m_amounts;
uint64_t m_fee;
uint64_t m_block_height;
uint64_t m_unlock_time;
@@ -1436,7 +1438,7 @@ namespace tools
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
void scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool);
void scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, amounts_container> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool);
void trim_hashchain();
crypto::key_image get_multisig_composite_key_image(size_t n) const;
rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const;
@@ -326,6 +326,7 @@ namespace tools
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.amounts = pd.m_amounts;
entry.unlock_time = pd.m_unlock_time;
entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
entry.fee = pd.m_fee;
@@ -408,6 +409,7 @@ namespace tools
entry.height = 0;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.amounts = pd.m_amounts;
entry.unlock_time = pd.m_unlock_time;
entry.locked = true;
entry.fee = pd.m_fee;
@@ -1354,13 +1354,15 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};

typedef std::vector<uint64_t> amounts_container;
struct transfer_entry
{
std::string txid;
std::string payment_id;
uint64_t height;
uint64_t timestamp;
uint64_t amount;
amounts_container amounts;
uint64_t fee;
std::string note;
std::list<transfer_destination> destinations;
@@ -1380,6 +1382,7 @@ namespace wallet_rpc
KV_SERIALIZE(height);
KV_SERIALIZE(timestamp);
KV_SERIALIZE(amount);
KV_SERIALIZE(amounts);
KV_SERIALIZE(fee);
KV_SERIALIZE(note);
KV_SERIALIZE(destinations);

0 comments on commit 3f53688

Please sign in to comment.
You can’t perform that action at this time.