aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/util.cpp8
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl7
-rw-r--r--src/p2p/net_node.inl21
-rw-r--r--src/p2p/net_peerlist.cpp12
-rw-r--r--src/p2p/net_peerlist.h26
-rw-r--r--src/rpc/CMakeLists.txt2
-rw-r--r--src/serialization/json_object.cpp18
-rw-r--r--src/wallet/api/wallet.cpp5
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/api/wallet2_api.h1
-rw-r--r--src/wallet/wallet2.cpp90
-rw-r--r--src/wallet/wallet_rpc_server.cpp41
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h23
13 files changed, 196 insertions, 59 deletions
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 445a11a75..af9843b95 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1355,8 +1355,12 @@ std::string get_nix_version_display_string()
100743, 92152, 57565, 22533, 37564, 21823, 19980, 18277, 18402, 14344,
12142, 15842, 13677, 17631, 18294, 22270, 41422, 39296, 36688, 33512,
33831, 27582, 22276, 27516, 27317, 25505, 24426, 20566, 23045, 26766,
- 28185, 26169, 27011,
- 28642 // Blocks 1,990,000 to 1,999,999 in December 2019
+ 28185, 26169, 27011, 28642, 34994, 34442, 30682, 34357, 31640, 41167,
+ 41301, 48616, 51075, 55061, 49909, 44606, 47091, 53828, 42520, 39023,
+ 55245, 56145, 51119, 60398, 71821, 48142, 60310, 56041, 54176, 66220,
+ 56336, 55248, 56656, 63305, 54029, 77136, 71902, 71618, 83587, 81068,
+ 69062, 54848, 53681, 53555,
+ 50616 // Blocks 2,400,000 to 2,409,999 in July 2021
};
const uint64_t block_range_size = 10000;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 685968c08..106253082 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -2081,6 +2081,8 @@ skip:
}
MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done");
context.m_state = cryptonote_connection_context::state_normal;
+ if (m_core.get_current_blockchain_height() >= m_core.get_target_blockchain_height())
+ on_connection_synchronized();
return true;
}
uint64_t next_needed_height = m_block_queue.get_next_needed_height(bc_height);
@@ -2228,6 +2230,8 @@ skip:
}
MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done");
context.m_state = cryptonote_connection_context::state_normal;
+ if (m_core.get_current_blockchain_height() >= m_core.get_target_blockchain_height())
+ on_connection_synchronized();
return true;
}
@@ -2419,10 +2423,7 @@ skip:
if (context.m_remote_blockchain_height >= m_core.get_target_blockchain_height())
{
if (m_core.get_current_blockchain_height() >= m_core.get_target_blockchain_height())
- {
- MGINFO_GREEN("SYNCHRONIZED OK");
on_connection_synchronized();
- }
}
else
{
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 8dd4b4476..cfeac3d37 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -270,9 +270,17 @@ namespace nodetool
peerlist_entry pe{};
pe.adr = addr;
- zone.second.m_peerlist.remove_from_peer_white(pe);
- zone.second.m_peerlist.remove_from_peer_gray(pe);
- zone.second.m_peerlist.remove_from_peer_anchor(addr);
+ if (addr.port() == 0)
+ {
+ zone.second.m_peerlist.evict_host_from_peerlist(true, pe);
+ zone.second.m_peerlist.evict_host_from_peerlist(false, pe);
+ }
+ else
+ {
+ zone.second.m_peerlist.remove_from_peer_white(pe);
+ zone.second.m_peerlist.remove_from_peer_gray(pe);
+ zone.second.m_peerlist.remove_from_peer_anchor(addr);
+ }
for (const auto &c: conns)
zone.second.m_net_server.get_config_object().close(c);
@@ -332,6 +340,13 @@ namespace nodetool
for (const auto &c: conns)
zone.second.m_net_server.get_config_object().close(c);
+ for (int i = 0; i < 2; ++i)
+ zone.second.m_peerlist.filter(i == 0, [&subnet](const peerlist_entry &pe){
+ if (pe.adr.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id())
+ return false;
+ return subnet.matches(pe.adr.as<const epee::net_utils::ipv4_network_address>());
+ });
+
conns.clear();
}
diff --git a/src/p2p/net_peerlist.cpp b/src/p2p/net_peerlist.cpp
index 42ab9727d..50dc6da77 100644
--- a/src/p2p/net_peerlist.cpp
+++ b/src/p2p/net_peerlist.cpp
@@ -289,17 +289,9 @@ namespace nodetool
copy_peers(peers.anchor, m_peers_anchor.get<by_addr>());
}
- void peerlist_manager::evict_host_from_white_peerlist(const peerlist_entry& pr)
+ void peerlist_manager::evict_host_from_peerlist(bool use_white, const peerlist_entry& pr)
{
- peers_indexed::index<by_time>::type& sorted_index=m_peers_white.get<by_time>();
- auto i = sorted_index.begin();
- while (i != sorted_index.end())
- {
- if (i->adr.is_same_host(pr.adr))
- i = sorted_index.erase(i);
- else
- ++i;
- }
+ filter(use_white, [&pr](const peerlist_entry& pe){ return pe.adr.is_same_host(pr.adr); });
}
}
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index d8de6abe9..0662789b9 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -109,7 +109,7 @@ namespace nodetool
bool get_white_peer_by_index(peerlist_entry& p, size_t i);
bool get_gray_peer_by_index(peerlist_entry& p, size_t i);
template<typename F> bool foreach(bool white, const F &f);
- void evict_host_from_white_peerlist(const peerlist_entry& pr);
+ void evict_host_from_peerlist(bool white, const peerlist_entry& pr);
bool append_with_peer_white(const peerlist_entry& pr, bool trust_last_seen = false);
bool append_with_peer_gray(const peerlist_entry& pr);
bool append_with_peer_anchor(const anchor_peerlist_entry& ple);
@@ -120,6 +120,7 @@ namespace nodetool
bool get_and_empty_anchor_peerlist(std::vector<anchor_peerlist_entry>& apl);
bool remove_from_peer_anchor(const epee::net_utils::network_address& addr);
bool remove_from_peer_white(const peerlist_entry& pe);
+ template<typename F> size_t filter(bool white, const F &f); // f returns true: drop, false: keep
private:
struct by_time{};
@@ -346,7 +347,7 @@ namespace nodetool
if(by_addr_it_wt == m_peers_white.get<by_addr>().end())
{
//put new record into white list
- evict_host_from_white_peerlist(ple);
+ evict_host_from_peerlist(true, ple);
m_peers_white.insert(ple);
trim_white_peerlist();
}else
@@ -520,5 +521,26 @@ namespace nodetool
CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_anchor()", false);
}
//--------------------------------------------------------------------------------------------------
+ template<typename F> size_t peerlist_manager::filter(bool white, const F &f)
+ {
+ size_t filtered = 0;
+ TRY_ENTRY();
+ CRITICAL_REGION_LOCAL(m_peerlist_lock);
+ peers_indexed::index<by_addr>::type& sorted_index = white ? m_peers_gray.get<by_addr>() : m_peers_white.get<by_addr>();
+ auto i = sorted_index.begin();
+ while (i != sorted_index.end())
+ {
+ if (f(*i))
+ {
+ i = sorted_index.erase(i);
+ ++filtered;
+ }
+ else
+ ++i;
+ }
+ CATCH_ENTRY_L0("peerlist_manager::filter()", filtered);
+ return filtered;
+ }
+ //--------------------------------------------------------------------------------------------------
}
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index aa4102481..15e433e10 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -39,7 +39,7 @@ set(rpc_sources
core_rpc_server.cpp
rpc_payment.cpp
rpc_version_str.cpp
- instanciations)
+ instanciations.cpp)
set(daemon_messages_sources
message.cpp
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index 67f042c2e..28e207ff2 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1091,9 +1091,12 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig&
};
INSERT_INTO_JSON_OBJECT(dest, type, sig.type);
- INSERT_INTO_JSON_OBJECT(dest, encrypted, sig.ecdhInfo);
- INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask));
- INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee);
+ if (sig.type != rct::RCTTypeNull)
+ {
+ INSERT_INTO_JSON_OBJECT(dest, encrypted, sig.ecdhInfo);
+ INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask));
+ INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee);
+ }
// prunable
if (!sig.p.bulletproofs.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty())
@@ -1122,9 +1125,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
}
GET_FROM_JSON_OBJECT(val, sig.type, type);
- GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted);
- GET_FROM_JSON_OBJECT(val, sig.outPk, commitments);
- GET_FROM_JSON_OBJECT(val, sig.txnFee, fee);
+ if (sig.type != rct::RCTTypeNull)
+ {
+ GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted);
+ GET_FROM_JSON_OBJECT(val, sig.outPk, commitments);
+ GET_FROM_JSON_OBJECT(val, sig.txnFee, fee);
+ }
// prunable
const auto prunable = val.FindMember("prunable");
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index b4fa1ea8c..3a2074cc4 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -2392,6 +2392,11 @@ void WalletImpl::setOffline(bool offline)
m_wallet->set_offline(offline);
}
+bool WalletImpl::isOffline() const
+{
+ return m_wallet->is_offline();
+}
+
void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const
{
m_wallet->get_hard_fork_info(version, earliest_height);
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 217f16e48..011a94ec4 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -186,6 +186,7 @@ public:
virtual std::string getCacheAttribute(const std::string &key) const override;
virtual void setOffline(bool offline) override;
+ virtual bool isOffline() const override;
virtual bool setUserNote(const std::string &txid, const std::string &note) override;
virtual std::string getUserNote(const std::string &txid) const override;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index a08033033..ed8c55d3b 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -1034,6 +1034,7 @@ struct Wallet
* \param offline - true/false
*/
virtual void setOffline(bool offline) = 0;
+ virtual bool isOffline() const = 0;
//! blackballs a set of outputs
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) = 0;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 634d9b312..7f23ed4d6 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -144,6 +144,9 @@ using namespace cryptonote;
#define IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION 12
+#define DEFAULT_UNLOCK_TIME (CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE * DIFFICULTY_TARGET_V2)
+#define RECENT_SPEND_WINDOW (50 * DIFFICULTY_TARGET_V2)
+
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
@@ -1034,6 +1037,34 @@ uint64_t gamma_picker::pick()
{
double x = gamma(engine);
x = exp(x);
+
+ if (x > DEFAULT_UNLOCK_TIME)
+ {
+ // We are trying to select an output from the chain that appeared 'x' seconds before the
+ // current chain tip, where 'x' is selected from the gamma distribution recommended in Miller et al.
+ // (https://arxiv.org/pdf/1704.04299/).
+ // Our method is to get the average time delta between outputs in the recent past, estimate the number of
+ // outputs 'n' that would have appeared between 'chain_tip - x' and 'chain_tip', select the real output at
+ // 'current_num_outputs - n', then randomly select an output from the block where that output appears.
+ // Source code to paper: https://github.com/maltemoeser/moneropaper
+ //
+ // Due to the 'default spendable age' mechanic in Monero, 'current_num_outputs' only contains
+ // currently *unlocked* outputs, which means the earliest output that can be selected is not at the chain tip!
+ // Therefore, we must offset 'x' so it matches up with the timing of the outputs being considered. We do
+ // this by saying if 'x` equals the expected age of the first unlocked output (compared to the current
+ // chain tip - i.e. DEFAULT_UNLOCK_TIME), then select the first unlocked output.
+ x -= DEFAULT_UNLOCK_TIME;
+ }
+ else
+ {
+ // If the spent time suggested by the gamma is less than the unlock time, that means the gamma is suggesting an output
+ // that is no longer feasible to be spent (possible since the gamma was constructed when consensus rules did not enforce the
+ // lock time). The assumption made in this code is that an output expected spent quicker than the unlock time would likely
+ // be spent within RECENT_SPEND_WINDOW after allowed. So it returns an output that falls between 0 and the RECENT_SPEND_WINDOW.
+ // The RECENT_SPEND_WINDOW was determined with empirical analysis of observed data.
+ x = crypto::rand_idx(static_cast<uint64_t>(RECENT_SPEND_WINDOW));
+ }
+
uint64_t output_index = x / average_output_time;
if (output_index >= num_rct_outputs)
return std::numeric_limits<uint64_t>::max(); // bad pick
@@ -1219,6 +1250,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
wallet2::~wallet2()
{
+ deinit();
}
bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm)
@@ -2782,9 +2814,8 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
{
if (tx_cache_data[i].empty())
continue;
- tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() {
+ tpool.submit(&waiter, [&gender, &tx_cache_data, i]() {
auto &slot = tx_cache_data[i];
- boost::unique_lock<hw::device> hwdev_lock(hwdev);
for (auto &iod: slot.primary)
gender(iod);
for (auto &iod: slot.additional)
@@ -3500,14 +3531,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
blocks_fetched += added_blocks;
}
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
- if(!first && blocks_start_height == next_blocks_start_height)
- {
- m_node_rpc_proxy.set_height(m_blockchain.size());
- refreshed = true;
- break;
- }
-
- first = false;
// handle error from async fetching thread
if (error)
@@ -3518,6 +3541,15 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
throw std::runtime_error("proxy exception in refresh thread");
}
+ if(!first && blocks_start_height == next_blocks_start_height)
+ {
+ m_node_rpc_proxy.set_height(m_blockchain.size());
+ refreshed = true;
+ break;
+ }
+
+ first = false;
+
if (!next_blocks.empty())
{
const uint64_t expected_start_height = std::max(static_cast<uint64_t>(m_blockchain.size()), uint64_t(1)) - 1;
@@ -3754,9 +3786,11 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui
//----------------------------------------------------------------------------------------------------
bool wallet2::deinit()
{
- m_is_initialized=false;
- unlock_keys_file();
- m_account.deinit();
+ if(m_is_initialized) {
+ m_is_initialized = false;
+ unlock_keys_file();
+ m_account.deinit();
+ }
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -4431,7 +4465,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
account_public_address device_account_public_address;
THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
- THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. "
+ THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. If the device uses the passphrase feature, please check whether the passphrase was entered correctly (it may have been misspelled - different passphrases generate different wallets, passphrase is case-sensitive). "
"Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
", wallet address: " + m_account.get_public_address_str(m_nettype));
LOG_PRINT_L0("Device inited...");
@@ -8596,18 +8630,30 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
// get the keys for those
- req.get_txid = false;
-
+ // the response can get large and end up rejected by the anti DoS limits, so chunk it if needed
+ size_t offset = 0;
+ while (offset < req.outputs.size())
{
+ static const size_t chunk_size = 1000;
+ COMMAND_RPC_GET_OUTPUTS_BIN::request chunk_req = AUTO_VAL_INIT(chunk_req);
+ COMMAND_RPC_GET_OUTPUTS_BIN::response chunk_daemon_resp = AUTO_VAL_INIT(chunk_daemon_resp);
+ chunk_req.get_txid = false;
+ for (size_t i = 0; i < std::min<size_t>(req.outputs.size() - offset, chunk_size); ++i)
+ chunk_req.outputs.push_back(req.outputs[offset + i]);
+
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits;
- req.client = get_client_signature();
- bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, *m_http_client, rpc_timeout);
- THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(daemon_resp.status));
- THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
+ chunk_req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", chunk_req, chunk_daemon_resp, *m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, chunk_daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(chunk_daemon_resp.status));
+ THROW_WALLET_EXCEPTION_IF(chunk_daemon_resp.outs.size() != chunk_req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " +
- std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
- check_rpc_cost("/get_outs.bin", daemon_resp.credits, pre_call_credits, daemon_resp.outs.size() * COST_PER_OUT);
+ std::to_string(chunk_daemon_resp.outs.size()) + ", expected " + std::to_string(chunk_req.outputs.size()));
+ check_rpc_cost("/get_outs.bin", chunk_daemon_resp.credits, pre_call_credits, chunk_daemon_resp.outs.size() * COST_PER_OUT);
+
+ offset += chunk_size;
+ for (size_t i = 0; i < chunk_daemon_resp.outs.size(); ++i)
+ daemon_resp.outs.push_back(std::move(chunk_daemon_resp.outs[i]));
}
std::unordered_map<uint64_t, uint64_t> scanty_outs;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index b72817ba0..e1a06886b 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1292,13 +1292,20 @@ namespace tools
try
{
// gather info to ask the user
- std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
+ std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> tx_dests;
+ std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> all_dests;
int first_known_non_zero_change_index = -1;
+ res.summary.amount_in = 0;
+ res.summary.amount_out = 0;
+ res.summary.change_amount = 0;
+ res.summary.fee = 0;
for (size_t n = 0; n < tx_constructions.size(); ++n)
{
const tools::wallet2::tx_construction_data &cd = tx_constructions[n];
res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
+ // Clear the recipients collection ready for this loop iteration
+ tx_dests.clear();
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
bool has_encrypted_payment_id = false;
@@ -1337,17 +1344,17 @@ namespace tools
std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
if (has_encrypted_payment_id && !entry.is_subaddress && address != entry.original)
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
- auto i = dests.find(entry.addr);
- if (i == dests.end())
- dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
+ auto i = tx_dests.find(entry.addr);
+ if (i == tx_dests.end())
+ tx_dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
else
i->second.second += entry.amount;
desc.amount_out += entry.amount;
}
if (cd.change_dts.amount > 0)
{
- auto it = dests.find(cd.change_dts.addr);
- if (it == dests.end())
+ auto it = tx_dests.find(cd.change_dts.addr);
+ if (it == tx_dests.end())
{
er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
er.message = "Claimed change does not go to a paid address";
@@ -1374,29 +1381,45 @@ namespace tools
desc.change_amount += cd.change_dts.amount;
it->second.second -= cd.change_dts.amount;
if (it->second.second == 0)
- dests.erase(cd.change_dts.addr);
+ tx_dests.erase(cd.change_dts.addr);
}
- for (auto i = dests.begin(); i != dests.end(); )
+ for (auto i = tx_dests.begin(); i != tx_dests.end(); ++i)
{
if (i->second.second > 0)
{
desc.recipients.push_back({i->second.first, i->second.second});
+ auto it_in_all = all_dests.find(i->first);
+ if (it_in_all == all_dests.end())
+ all_dests.insert(std::make_pair(i->first, i->second));
+ else
+ it_in_all->second.second += i->second.second;
}
else
++desc.dummy_outputs;
- ++i;
}
if (desc.change_amount > 0)
{
const tools::wallet2::tx_construction_data &cd0 = tx_constructions[0];
desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
+ res.summary.change_address = desc.change_address;
}
desc.fee = desc.amount_in - desc.amount_out;
desc.unlock_time = cd.unlock_time;
desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
+
+ // Update summary items
+ res.summary.amount_in += desc.amount_in;
+ res.summary.amount_out += desc.amount_out;
+ res.summary.change_amount += desc.change_amount;
+ res.summary.fee += desc.fee;
+ }
+ // Populate the summary recipients list
+ for (auto i = all_dests.begin(); i != all_dests.end(); ++i)
+ {
+ res.summary.recipients.push_back({i->second.first, i->second.second});
}
}
catch (const std::exception &e)
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 6640441ed..248d31aa4 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 22
+#define WALLET_RPC_VERSION_MINOR 23
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -701,6 +701,25 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
+ struct txset_summary
+ {
+ uint64_t amount_in;
+ uint64_t amount_out;
+ std::list<recipient> recipients;
+ uint64_t change_amount;
+ std::string change_address;
+ uint64_t fee;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_in)
+ KV_SERIALIZE(amount_out)
+ KV_SERIALIZE(recipients)
+ KV_SERIALIZE(change_amount)
+ KV_SERIALIZE(change_address)
+ KV_SERIALIZE(fee)
+ END_KV_SERIALIZE_MAP()
+ };
+
struct request_t
{
std::string unsigned_txset;
@@ -716,8 +735,10 @@ namespace wallet_rpc
struct response_t
{
std::list<transfer_description> desc;
+ struct txset_summary summary;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(summary)
KV_SERIALIZE(desc)
END_KV_SERIALIZE_MAP()
};