Merge pull request #105 from wowario/upstream

Upstream
pull/106/head
jw 6 years ago committed by GitHub
commit 8386c6df2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -122,6 +122,7 @@
if(!ps.load_from_json(query_info.m_body)) \
{ \
boost::value_initialized<epee::json_rpc::error_response> rsp; \
static_cast<epee::json_rpc::error_response&>(rsp).jsonrpc = "2.0"; \
static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \
static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \

@ -35,6 +35,11 @@
namespace epee
{
namespace
{
template<class C> void hint_resize(C &container, size_t size) {}
template<class C> void hint_resize(std::vector<C> &container, size_t size) { container.reserve(size); }
}
namespace serialization
{
@ -158,6 +163,7 @@ namespace epee
false,
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type));
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
hint_resize(container, count);
for(size_t i = 0; i < count; i++)
container.insert(container.end(), *(pelem++));
}

@ -28,6 +28,8 @@
#pragma once
#include <algorithm>
namespace epee
{
namespace misc_utils
@ -36,8 +38,12 @@ namespace misc_utils
{
inline std::string transform_to_escape_sequence(const std::string& src)
{
//std::stringstream res;
static const char escaped[] = "\b\f\n\r\t\v\"\\/";
if (std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)) == src.end())
return src;
std::string res;
res.reserve(2 * src.size());
for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it)
{
switch(*it)
@ -84,6 +90,7 @@ namespace misc_utils
inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
{
val.clear();
val.reserve(std::distance(star_end_string, buf_end));
bool escape_mode = false;
std::string::const_iterator it = star_end_string;
++it;

@ -201,15 +201,25 @@ namespace cryptonote
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation));
}
std::vector<crypto::key_derivation> additional_recv_derivations;
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
{
crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation);
r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
additional_recv_derivations.push_back(additional_recv_derivation);
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
}
else
{
additional_recv_derivations.push_back(additional_recv_derivation);
}
}
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);

@ -746,7 +746,7 @@ namespace cryptonote
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
tx_info txi;
txi.id_hash = epee::string_tools::pod_to_hex(txid);
txi.tx_blob = *bd;
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(*bd);
transaction tx;
if (!parse_and_validate_tx_from_blob(*bd, tx))
{
@ -1095,7 +1095,7 @@ namespace cryptonote
LockedTXN lock(m_blockchain);
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
{
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
@ -1109,7 +1109,6 @@ namespace cryptonote
if (max_total_size < total_size + meta.blob_size)
{
LOG_PRINT_L2(" would exceed maximum block size");
sorted_it++;
continue;
}
@ -1122,14 +1121,12 @@ namespace cryptonote
if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version))
{
LOG_PRINT_L2(" would exceed maximum block size");
sorted_it++;
continue;
}
coinbase = block_reward + fee + meta.fee;
if (coinbase < template_accept_threshold(best_coinbase))
{
LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase));
sorted_it++;
continue;
}
}
@ -1173,13 +1170,11 @@ namespace cryptonote
if (!ready)
{
LOG_PRINT_L2(" not ready to go");
sorted_it++;
continue;
}
if (have_key_images(k_images, tx))
{
LOG_PRINT_L2(" key images already seen");
sorted_it++;
continue;
}
@ -1188,7 +1183,6 @@ namespace cryptonote
fee += meta.fee;
best_coinbase = coinbase;
append_key_images(k_images, tx);
sorted_it++;
LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase));
}
@ -1284,6 +1278,7 @@ namespace cryptonote
{
MWARNING("Failed to parse tx from txpool, removing");
remove.push_back(txid);
return true;
}
if (!insert_key_images(tx, meta.kept_by_block))
{

@ -1167,8 +1167,20 @@ skip:
+ " blocks/sec), " + std::to_string(m_block_queue.get_data_size() / 1048576.f) + " MB queued";
if (ELPP->vRegistry()->allowed(el::Level::Debug, "sync-info"))
timing_message += std::string(": ") + m_block_queue.get_overview();
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
if(m_core.get_target_blockchain_height() == 0){
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
} else {
const int completition_percent = (m_core.get_current_blockchain_height() * 100 / m_core.get_target_blockchain_height());
if(completition_percent < 99) {//printing completion percent only if % is < of 99 cause for 99 >= this is useless
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< " (" << completition_percent << "% " << (m_core.get_target_blockchain_height() - m_core.get_current_blockchain_height())
<< " blocks remaining)" << timing_message);
} else {
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
}
}
}
}
}
@ -1752,3 +1764,4 @@ skip:
m_core.stop();
}
} // namespace

@ -438,7 +438,8 @@ bool t_rpc_command_executor::show_status() {
}
}
tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
std::stringstream str;
str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections")
% (unsigned long long)ires.height
% (unsigned long long)net_height
% get_sync_percentage(ires)
@ -451,12 +452,21 @@ bool t_rpc_command_executor::show_status() {
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
% (unsigned)ires.outgoing_connections_count
% (unsigned)ires.incoming_connections_count
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
% (unsigned int)fmod(uptime, 60.0)
;
// restricted RPC does not disclose start time
if (ires.start_time)
{
str << boost::format(", uptime %ud %uh %um %us")
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
% (unsigned int)fmod(uptime, 60.0)
;
}
tools::success_msg_writer() << str.str();
return true;
}
@ -554,7 +564,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
if (!first)
std::cout << std::endl;
std::cout
<< "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty
<< "height: " << header.height << ", timestamp: " << header.timestamp
<< ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl

@ -863,7 +863,13 @@ namespace cryptonote
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
cryptonote::miner &miner= m_core.get_miner();
if (miner.is_mining())
{
res.status = "Already mining";
return true;
}
if(!miner.start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);

@ -93,13 +93,8 @@ typedef cryptonote::simple_wallet sw;
m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
/* stop any background refresh, and take over */ \
m_wallet->stop(); \
m_idle_mutex.lock(); \
while (m_auto_refresh_refreshing) \
m_idle_cond.notify_one(); \
m_idle_mutex.unlock(); \
/* if (auto_refresh_run)*/ \
/*m_auto_refresh_thread.join();*/ \
boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
m_idle_cond.notify_all(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
})
@ -525,6 +520,18 @@ bool parse_priority(const std::string& arg, uint32_t& priority)
return false;
}
std::string join_priority_strings(const char *delimiter)
{
std::string s;
for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
{
if (!s.empty())
s += delimiter;
s += allowed_priority_strings[n];
}
return s;
}
std::string simple_wallet::get_commands_str()
{
std::stringstream ss;
@ -1611,12 +1618,12 @@ bool simple_wallet::set_store_tx_info(const std::vector<std::string> &args/* = s
bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
int priority = 0;
uint32_t priority = 0;
try
{
if (strchr(args[1].c_str(), '-'))
{
fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 ");
fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
return true;
}
if (args[1] == "0")
@ -1625,11 +1632,23 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/*
}
else
{
priority = boost::lexical_cast<int>(args[1]);
if (priority < 1 || priority > 4)
bool found = false;
for (size_t n = 0; n < allowed_priority_strings.size(); ++n)
{
fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4");
return true;
if (allowed_priority_strings[n] == args[1])
{
found = true;
priority = n;
}
}
if (!found)
{
priority = boost::lexical_cast<int>(args[1]);
if (priority < 1 || priority > 4)
{
fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
return true;
}
}
}
@ -1643,7 +1662,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/*
}
catch(const boost::bad_lexical_cast &)
{
fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4");
fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", ");
return true;
}
catch(...)
@ -2178,7 +2197,7 @@ simple_wallet::simple_wallet()
tr("Show the unspent outputs of a specified address within an optional amount range."));
m_cmd_binder.set_handler("rescan_bc",
boost::bind(&simple_wallet::rescan_blockchain, this, _1),
tr("Rescan the blockchain from scratch."));
tr("Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself."));
m_cmd_binder.set_handler("set_tx_note",
boost::bind(&simple_wallet::set_tx_note, this, _1),
tr("set_tx_note <txid> [free text note]"),
@ -2308,6 +2327,10 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
std::string seed_language = m_wallet->get_seed_language();
if (m_use_english_language_names)
seed_language = crypto::ElectrumWords::get_english_name_for(seed_language);
std::string priority_string = "invalid";
uint32_t priority = m_wallet->get_default_priority();
if (priority < allowed_priority_strings.size())
priority_string = allowed_priority_strings[priority];
success_msg_writer() << "seed = " << seed_language;
success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers();
success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members();
@ -2627,6 +2650,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
}
}
epee::wipeable_string password;
if (!m_generate_from_view_key.empty())
{
m_wallet_file = m_generate_from_view_key;
@ -2679,8 +2703,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
bool r = new_wallet(vm, info.address, boost::none, viewkey);
auto r = new_wallet(vm, info.address, boost::none, viewkey);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
}
else if (!m_generate_from_spend_key.empty())
{
@ -2698,8 +2723,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("failed to parse spend key secret key");
return false;
}
bool r = new_wallet(vm, m_recovery_key, true, false, "");
auto r = new_wallet(vm, m_recovery_key, true, false, "");
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
}
else if (!m_generate_from_keys.empty())
{
@ -2776,8 +2802,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("view key does not match standard address");
return false;
}
bool r = new_wallet(vm, info.address, spendkey, viewkey);
auto r = new_wallet(vm, info.address, spendkey, viewkey);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
}
// Asks user for all the data required to merge secret keys from multisig wallets into one master wallet, which then gets full control of the multisig wallet. The resulting wallet will be the same as any other regular wallet.
@ -2910,8 +2937,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// create wallet
bool r = new_wallet(vm, info.address, spendkey, viewkey);
auto r = new_wallet(vm, info.address, spendkey, viewkey);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
}
else if (!m_generate_from_json.empty())
@ -2933,8 +2961,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_device;
// create wallet
bool r = new_wallet(vm, "Ledger");
auto r = new_wallet(vm, "Ledger");
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
// if no block_height is specified, assume its a new account and start it "now"
if(m_wallet->get_refresh_from_block_height() == 0) {
{
@ -2959,12 +2988,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
m_wallet_file = m_generate_new;
bool r;
boost::optional<epee::wipeable_string> r;
if (m_restore_multisig_wallet)
r = new_wallet(vm, multisig_keys, old_language);
else
r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
}
if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
@ -3046,6 +3076,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
m_wallet->set_refresh_from_block_height(m_restore_height);
}
m_wallet->rewrite(m_wallet_file, password);
}
else
{
@ -3213,15 +3244,16 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
return pwd_container;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
{
auto rc = tools::wallet2::make_new(vm, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
return false;
return {};
}
epee::wipeable_string password = rc.second.password();
if (!m_subaddress_lookahead.empty())
{
@ -3257,7 +3289,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
}
mnemonic_language = get_mnemonic_language();
if (mnemonic_language.empty())
return false;
return {};
}
m_wallet->set_seed_language(mnemonic_language);
@ -3275,7 +3307,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
catch (const std::exception& e)
{
fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
return false;
return {};
}
// convert rng value to electrum-style word list
@ -3301,10 +3333,10 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
}
success_msg_writer() << "**********************************************************************";
return true;
return std::move(password);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
{
@ -3312,8 +3344,9 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
m_wallet = std::move(rc.first);
if (!m_wallet)
{
return false;
return {};
}
epee::wipeable_string password = rc.second.password();
if (!m_subaddress_lookahead.empty())
{
@ -3343,22 +3376,23 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
catch (const std::exception& e)
{
fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
return false;
return {};
}
return true;
return std::move(password);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const std::string &device_name) {
auto rc = tools::wallet2::make_new(vm, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
return false;
return {};
}
epee::wipeable_string password = rc.second.password();
if (!m_subaddress_lookahead.empty())
{
@ -3379,21 +3413,22 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
catch (const std::exception& e)
{
fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
return false;
return {};
}
return true;
return std::move(password);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const std::string &multisig_keys, const std::string &old_language)
{
auto rc = tools::wallet2::make_new(vm, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
return false;
return {};
}
epee::wipeable_string password = rc.second.password();
if (!m_subaddress_lookahead.empty())
{
@ -3423,7 +3458,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
if (!m_wallet->multisig(&ready, &threshold, &total) || !ready)
{
fail_msg_writer() << tr("failed to generate new mutlisig wallet");
return false;
return {};
}
message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
@ -3431,10 +3466,10 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
catch (const std::exception& e)
{
fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
return false;
return {};
}
return true;
return std::move(password);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
@ -5972,7 +6007,7 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds)
if (ts < 3600 * 24 * 30.5)
return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
if (ts < 3600 * 24 * 365.25)
return std::to_string((uint64_t)(ts / (3600 * 24 * 365.25))) + tr(" months");
return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
return tr("a long time");
}
//----------------------------------------------------------------------------------------------------
@ -6168,7 +6203,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
auto local_args = args_;
std::set<uint32_t> subaddr_indices;
if (local_args.size() > 0 && local_args[0].substr(0, 6) != "index=")
if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
{
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return true;
@ -6298,6 +6333,14 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
{
message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
if(!std::cin.eof())
{
if (!command_line::is_yes(confirm))
return true;
}
return refresh_main(0, true);
}
//----------------------------------------------------------------------------------------------------

@ -91,13 +91,13 @@ namespace cryptonote
//! \return Prompts user for password and verifies against local file. Logs on error and returns `none`
boost::optional<tools::password_container> get_and_verify_password() const;
bool new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
bool recover, bool two_random, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
bool new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const std::string &multisig_keys, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();

@ -146,7 +146,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status");
m_earliest_height[version] = resp_t.enabled ? resp_t.earliest_height : std::numeric_limits<uint64_t>::max();
m_earliest_height[version] = resp_t.earliest_height;
}
earliest_height = m_earliest_height[version];

@ -887,6 +887,14 @@ cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::sub
return hwdev.get_subaddress(m_account.get_keys(), index);
}
//----------------------------------------------------------------------------------------------------
boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
{
auto index = m_subaddresses.find(address.m_spend_public_key);
if (index == m_subaddresses.end())
return boost::none;
return index->second;
}
//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const
{
hw::device &hwdev = m_account.get_device();
@ -1100,7 +1108,6 @@ 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::vector<size_t> outs;
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
@ -1118,6 +1125,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t total_received_1 = 0;
while (!tx.vout.empty())
{
std::vector<size_t> outs;
// if tx.vout is not empty, we loop through all tx pubkeys
tx_extra_pub_key pub_key_field;
@ -2385,7 +2393,7 @@ bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
return ok;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
{
uint32_t rpc_version;
boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
@ -5909,22 +5917,42 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
bool is_after_segregation_fork = height >= segregation_fork_height;
// if we have at least one rct out, get the distribution, or fall back to the previous system
uint64_t rct_start_height;
std::vector<uint64_t> rct_offsets;
bool has_rct = false;
for (size_t idx: selected_transfers)
if (m_transfers[idx].is_rct())
{ has_rct = true; break; }
const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
if (has_rct_distribution)
{
// check we're clear enough of rct start, to avoid corner cases below
THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE,
error::get_output_distribution, "Not enough rct outputs");
}
// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
// request histogram for all outputs, except 0 if we have the rct distribution
for(size_t idx: selected_transfers)
req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
std::sort(req_t.amounts.begin(), req_t.amounts.end());
auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
req_t.unlocked = true;
req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
if (!m_transfers[idx].is_rct() || !has_rct_distribution)
req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
if (!req_t.amounts.empty())
{
std::sort(req_t.amounts.begin(), req_t.amounts.end());
auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
req_t.unlocked = true;
req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
}
// if we want to segregate fake outs pre or post fork, get distribution
std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
@ -5980,6 +6008,36 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
struct gamma_engine
{
typedef uint64_t result_type;
static constexpr result_type min() { return 0; }
static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
result_type operator()() { return crypto::rand<result_type>(); }
} engine;
static const double shape = 19.28/*16.94*/;
//static const double shape = m_testnet ? 17.02 : 17.28;
static const double scale = 1/1.61;
std::gamma_distribution<double> gamma(shape, scale);
auto pick_gamma = [&]()
{
double x = gamma(engine);
x = exp(x);
uint64_t block_offset = x / DIFFICULTY_TARGET_V2; // this assumes constant target over the whole rct range
if (block_offset >= rct_offsets.size() - 1)
return std::numeric_limits<uint64_t>::max(); // bad pick
block_offset = rct_offsets.size() - 2 - block_offset;
THROW_WALLET_EXCEPTION_IF(block_offset >= rct_offsets.size() - 1, error::wallet_internal_error, "Bad offset calculation");
THROW_WALLET_EXCEPTION_IF(rct_offsets[block_offset + 1] < rct_offsets[block_offset],
error::get_output_distribution, "Decreasing offsets in rct distribution: " +
std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " +
std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1]));
uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset];
if (n_rct == 0)
return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
return rct_offsets[block_offset] + crypto::rand<uint64_t>() % n_rct;
};
size_t num_selected_transfers = 0;
for(size_t idx: selected_transfers)
{
@ -5990,6 +6048,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
size_t start = req.outputs.size();
bool use_histogram = amount != 0 || !has_rct_distribution;
const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
uint64_t num_outs = 0, num_recent_outs = 0;
@ -6045,26 +6104,41 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
num_post_fork_outs = num_outs - segregation_limit[amount].first;
}
LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
"histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
"histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
if (use_histogram)
{
LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
"histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
"histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
}
else
{
// the base offset of the first rct output in the first unlocked block (or the one to be if there's none)
num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE];
LOG_PRINT_L1("" << num_outs << " unlocked rct outputs");
THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
"histogram reports no unlocked rct outputs, not even ours");
}
// how many fake outs to draw on a pre-fork triangular distribution
// how many fake outs to draw on a pre-fork distribution
size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
// how many fake outs to draw otherwise
size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
// X% of those outs are to be taken from recent outputs
size_t recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
if (recent_outputs_count == 0)
recent_outputs_count = 1; // ensure we have at least one, if possible
if (recent_outputs_count > num_recent_outs)
recent_outputs_count = num_recent_outs;
if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
--recent_outputs_count; // if the real out is recent, pick one less recent fake out
size_t recent_outputs_count = 0;
if (use_histogram)
{
// X% of those outs are to be taken from recent outputs
recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
if (recent_outputs_count == 0)
recent_outputs_count = 1; // ensure we have at least one, if possible
if (recent_outputs_count > num_recent_outs)
recent_outputs_count = num_recent_outs;
if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
--recent_outputs_count; // if the real out is recent, pick one less recent fake out
}
LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
(requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
@ -6144,7 +6218,26 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t i;
const char *type = "";
if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
if (amount == 0 && has_rct_distribution)
{
// gamma distribution
if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
{
do i = pick_gamma(); while (i >= segregation_limit[amount].first);
type = "pre-fork gamma";
}
else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
{
do i = pick_gamma(); while (i < segregation_limit[amount].first || i >= num_outs);
type = "post-fork gamma";
}
else
{
do i = pick_gamma(); while (i >= num_outs);
type = "gamma";
}
}
else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
{
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
@ -6209,7 +6302,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// get the keys for those
m_daemon_rpc_mutex.lock();
r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
@ -8119,7 +8212,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
bool close_enough = height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand
if (close_enough)
LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
@ -8902,6 +8995,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
if (account_minreserve)
{
THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
// minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
{ return m_transfers[a].amount() > m_transfers[b].amount(); });
@ -9567,6 +9661,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
// was created by sweep_all, so we can't know the spent height and other detailed info.
std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
for (const transfer_details &td: m_transfers)
{
for (const cryptonote::txin_v& in : td.m_tx.vin)
{
if (in.type() == typeid(cryptonote::txin_to_key))
spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
}
}
for(size_t i = 0; i < signed_key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
@ -9580,28 +9685,11 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
{
bool is_spent_tx_found = false;
for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
{
bool is_spent_tx = false;
for(const cryptonote::txin_v& in : it->m_tx.vin)
{
if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
{
is_spent_tx = true;
break;
}
}
if (is_spent_tx)
{
is_spent_tx_found = true;
spent_txids.insert(it->m_txid);
break;
}
}
if (!is_spent_tx_found)
const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
if (skii == spent_key_images.end())
swept_transfers.push_back(i);
else
spent_txids.insert(skii->second);
}
}
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
@ -9724,11 +9812,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
{
const transfer_details& td = m_transfers[n];
confirmed_transfer_details pd;
pd.m_change = (uint64_t)-1; // cahnge is unknown
pd.m_change = (uint64_t)-1; // change is unknown
pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
std::string err;
pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
pd.m_block_height = 0; // spent block height is unknown
const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
}
}
@ -9823,18 +9910,20 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key),
error::wallet_internal_error, "Unsupported output type");
const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true;
td.m_key_image_partial = false;
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key,
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i));
m_key_images[td.m_key_image] = m_transfers.size();
m_pub_keys[td.get_public_key()] = m_transfers.size();
m_transfers.push_back(td);
m_transfers.push_back(std::move(td));
}
return m_transfers.size();

@ -624,6 +624,7 @@ namespace tools
// Subaddress scheme
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const;
cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); }
boost::optional<cryptonote::subaddress_index> get_subaddress_index(const cryptonote::account_public_address& address) const;
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
@ -1137,7 +1138,7 @@ namespace tools
bool remove_rings(const cryptonote::transaction_prefix &tx);
bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;

@ -336,14 +336,20 @@ namespace tools
std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index);
std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
std::set<uint32_t> address_indices = req.address_indices;
if (address_indices.empty())
{
for (const auto& i : balance_per_subaddress)
address_indices.insert(i.first);
}
for (uint32_t i : address_indices)
{
wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info;
info.address_index = i.first;
info.address_index = i;
cryptonote::subaddress_index index = {req.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.balance = i.second;
info.unlocked_balance = unlocked_balance_per_subaddress[i.first];
info.balance = balance_per_subaddress[i];
info.unlocked_balance = unlocked_balance_per_subaddress[i];
info.label = m_wallet->get_subaddress_label(index);
info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; });
res.per_subaddress.push_back(info);
@ -397,6 +403,27 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Invalid address";
return false;
}
auto index = m_wallet->get_subaddress_index(info.address);
if (!index)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Address doesn't belong to the wallet";
return false;
}
res.index = *index;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@ -1875,8 +1902,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
}
}
@ -1885,8 +1912,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@ -1895,8 +1922,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@ -1907,11 +1934,17 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
if (i->second.m_pd.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
if (!res.transfers.empty())
{
res.transfer = res.transfers.front(); // backward compat
return true;
}
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction not found.";
return false;

@ -69,6 +69,7 @@ namespace tools
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX)
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
@ -139,6 +140,7 @@ namespace tools
//json_rpc
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er);
bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);

@ -39,6 +39,17 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
// When making *any* change here, bump minor
// If the change is incompatible, then bump major and set minor to 0
// This ensures WALLET_RPC_VERSION always increases, that every change
// has its own version, and that clients can just test major to see
// whether they can talk to a given wallet without having to know in
// 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 4
#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
{
namespace wallet_rpc
@ -51,8 +62,10 @@ namespace wallet_rpc
struct request
{
uint32_t account_index;
std::set<uint32_t> address_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(address_indices)
END_KV_SERIALIZE_MAP()
};
@ -130,6 +143,25 @@ namespace wallet_rpc
};
};
struct COMMAND_RPC_GET_ADDRESS_INDEX
{
struct request
{
std::string address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};
struct response
{
cryptonote::subaddress_index index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(index)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_CREATE_ADDRESS
{
struct request
@ -1264,9 +1296,11 @@ namespace wallet_rpc
struct response
{
transfer_entry transfer;
std::list<transfer_entry> transfers;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer);
KV_SERIALIZE(transfers);
END_KV_SERIALIZE_MAP()
};
};

Loading…
Cancel
Save