aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/dns_utils.cpp8
-rw-r--r--src/common/dns_utils.h4
-rw-r--r--src/common/scoped_message_writer.h2
-rw-r--r--src/cryptonote_basic/miner.cpp112
-rw-r--r--src/cryptonote_core/blockchain.cpp14
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp22
-rw-r--r--src/cryptonote_core/cryptonote_core.h9
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl2
-rw-r--r--src/daemonizer/posix_daemonizer.inl12
-rw-r--r--src/daemonizer/posix_fork.cpp42
-rw-r--r--src/daemonizer/posix_fork.h15
-rw-r--r--src/rpc/core_rpc_server.cpp22
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h21
-rw-r--r--src/simplewallet/simplewallet.cpp65
-rw-r--r--src/wallet/wallet2.cpp72
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--src/wallet/wallet_rpc_server.cpp45
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h24
19 files changed, 399 insertions, 96 deletions
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index ab38cbbae..979f34b3e 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -307,12 +307,8 @@ DNSResolver& DNSResolver::instance()
{
boost::lock_guard<boost::mutex> lock(instance_lock);
- static DNSResolver* staticInstance = NULL;
- if (staticInstance == NULL)
- {
- staticInstance = new DNSResolver();
- }
- return *staticInstance;
+ static DNSResolver staticInstance;
+ return staticInstance;
}
DNSResolver DNSResolver::create()
diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h
index 53c0c1c7b..7a093db51 100644
--- a/src/common/dns_utils.h
+++ b/src/common/dns_utils.h
@@ -101,7 +101,7 @@ public:
*
* @return A vector of strings containing a TXT record; or an empty vector
*/
- // TODO: modify this to accomodate DNSSEC
+ // TODO: modify this to accommodate DNSSEC
std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid);
/**
@@ -142,7 +142,7 @@ private:
*
* @return A vector of strings containing the requested record; or an empty vector
*/
- // TODO: modify this to accomodate DNSSEC
+ // TODO: modify this to accommodate DNSSEC
std::vector<std::string> get_record(const std::string& url, int record_type, std::string (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid);
/**
diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h
index 7ee4f1379..e31f8f0b2 100644
--- a/src/common/scoped_message_writer.h
+++ b/src/common/scoped_message_writer.h
@@ -91,7 +91,7 @@ public:
{
m_flush = false;
- MCLOG(m_log_level, "msgwriter", m_oss.str());
+ MCLOG_FILE(m_log_level, "msgwriter", m_oss.str());
if (epee::console_color_default == m_color)
{
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index cd0943618..9248e2e1d 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -846,40 +846,100 @@ namespace cryptonote
#elif defined(__linux__)
- // i've only tested on UBUNTU, these paths might be different on other systems
- // need to figure out a way to make this more flexible
- std::string power_supply_path = "";
- const std::string POWER_SUPPLY_STATUS_PATHS[] =
- {
- "/sys/class/power_supply/ACAD/online",
- "/sys/class/power_supply/AC/online",
- "/sys/class/power_supply/AC0/online",
- "/sys/class/power_supply/ADP0/online"
- };
+ // Use the power_supply class http://lxr.linux.no/#linux+v4.10.1/Documentation/power/power_supply_class.txt
+ std::string power_supply_class_path = "/sys/class/power_supply";
- for(const std::string& path : POWER_SUPPLY_STATUS_PATHS)
+ boost::tribool on_battery = boost::logic::tribool(boost::logic::indeterminate);
+ if (boost::filesystem::is_directory(power_supply_class_path))
{
- if( epee::file_io_utils::is_file_exist(path) )
+ const boost::filesystem::directory_iterator end_itr;
+ for (boost::filesystem::directory_iterator iter(power_supply_class_path); iter != end_itr; ++iter)
{
- power_supply_path = path;
- break;
+ const boost::filesystem::path& power_supply_path = iter->path();
+ if (boost::filesystem::is_directory(power_supply_path))
+ {
+ std::ifstream power_supply_present_stream((power_supply_path / "present").string());
+ if (power_supply_present_stream.fail())
+ {
+ LOG_PRINT_L0("Unable to read from " << power_supply_path << " to check if power supply present");
+ continue;
+ }
+
+ if (power_supply_present_stream.get() != '1')
+ {
+ LOG_PRINT_L4("Power supply not present at " << power_supply_path);
+ continue;
+ }
+
+ boost::filesystem::path power_supply_type_path = power_supply_path / "type";
+ if (boost::filesystem::is_regular_file(power_supply_type_path))
+ {
+ std::ifstream power_supply_type_stream(power_supply_type_path.string());
+ if (power_supply_type_stream.fail())
+ {
+ LOG_PRINT_L0("Unable to read from " << power_supply_type_path << " to check power supply type");
+ continue;
+ }
+
+ std::string power_supply_type;
+ std::getline(power_supply_type_stream, power_supply_type);
+
+ // If there is an AC adapter that's present and online we can break early
+ if (boost::starts_with(power_supply_type, "Mains"))
+ {
+ boost::filesystem::path power_supply_online_path = power_supply_path / "online";
+ if (boost::filesystem::is_regular_file(power_supply_online_path))
+ {
+ std::ifstream power_supply_online_stream(power_supply_online_path.string());
+ if (power_supply_online_stream.fail())
+ {
+ LOG_PRINT_L0("Unable to read from " << power_supply_online_path << " to check ac power supply status");
+ continue;
+ }
+
+ if (power_supply_online_stream.get() == '1')
+ {
+ return boost::logic::tribool(false);
+ }
+ }
+ }
+ else if (boost::starts_with(power_supply_type, "Battery") && boost::logic::indeterminate(on_battery))
+ {
+ boost::filesystem::path power_supply_status_path = power_supply_path / "status";
+ if (boost::filesystem::is_regular_file(power_supply_status_path))
+ {
+ std::ifstream power_supply_status_stream(power_supply_status_path.string());
+ if (power_supply_status_stream.fail())
+ {
+ LOG_PRINT_L0("Unable to read from " << power_supply_status_path << " to check battery power supply status");
+ continue;
+ }
+
+ // Possible status are Charging, Full, Discharging, Not Charging, and Unknown
+ // We are only need to handle negative states right now
+ std::string power_supply_status;
+ std::getline(power_supply_status_stream, power_supply_status);
+ if (boost::starts_with(power_supply_status, "Charging") || boost::starts_with(power_supply_status, "Full"))
+ {
+ on_battery = boost::logic::tribool(false);
+ }
+
+ if (boost::starts_with(power_supply_status, "Discharging"))
+ {
+ on_battery = boost::logic::tribool(true);
+ }
+ }
+ }
+ }
+ }
}
}
- if( power_supply_path.empty() )
+ if (boost::logic::indeterminate(on_battery))
{
- LOG_ERROR("Couldn't find battery/power status file, can't determine if plugged in!");
- return boost::logic::tribool(boost::logic::indeterminate);;
+ LOG_ERROR("couldn't query power status from " << power_supply_class_path);
}
-
- std::ifstream power_stream(power_supply_path);
- if( power_stream.fail() )
- {
- LOG_ERROR("failed to open '" << power_supply_path << "'");
- return boost::logic::tribool(boost::logic::indeterminate);;
- }
-
- return boost::logic::tribool( (power_stream.get() != '1') );
+ return on_battery;
#endif
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 745608b9f..1602cd06d 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -3097,6 +3097,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
CRITICAL_REGION_LOCAL(m_blockchain_lock);
TIME_MEASURE_START(t1);
+ static bool seen_future_version = false;
+
m_db->block_txn_start(true);
if(bl.prev_id != get_tail_id())
{
@@ -3106,6 +3108,18 @@ leave:
return false;
}
+ // warn users if they're running an old version
+ if (!seen_future_version && bl.major_version > m_hardfork->get_ideal_version())
+ {
+ seen_future_version = true;
+ const el::Level level = el::Level::Warning;
+ MCLOG_RED(level, "global", "**********************************************************************");
+ MCLOG_RED(level, "global", "A block was seen on the network with a version higher than the last");
+ MCLOG_RED(level, "global", "known one. This may be an old version of the daemon, and a software");
+ MCLOG_RED(level, "global", "update may be required to sync further. Try running: update check");
+ MCLOG_RED(level, "global", "**********************************************************************");
+ }
+
// this is a cheap test
if (!m_hardfork->check(bl))
{
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 4cfa52441..da8ec68dd 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -660,6 +660,12 @@ namespace cryptonote
return false;
}
+ if (!check_tx_inputs_ring_members_diff(tx))
+ {
+ MERROR_VER("tx uses duplicate ring members");
+ return false;
+ }
+
if (!check_tx_inputs_keyimages_domain(tx))
{
MERROR_VER("tx uses key image not in the valid domain");
@@ -752,6 +758,22 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const
+ {
+ const uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
+ if (version >= 6)
+ {
+ for(const auto& in: tx.vin)
+ {
+ CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
+ for (size_t n = 1; n < tokey_in.key_offsets.size(); ++n)
+ if (tokey_in.key_offsets[n] == 0)
+ return false;
+ }
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const
{
std::unordered_set<crypto::key_image> ki;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index e5fbf7f91..4ced91e23 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -781,6 +781,15 @@ namespace cryptonote
bool check_tx_inputs_keyimages_diff(const transaction& tx) const;
/**
+ * @brief verify that each ring uses distinct members
+ *
+ * @param tx the transaction to check
+ *
+ * @return false if any ring uses duplicate members, true otherwise
+ */
+ bool check_tx_inputs_ring_members_diff(const transaction& tx) const;
+
+ /**
* @brief verify that each input key image in a transaction is in
* the valid domain
*
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index c5bc834ad..3dfe86fe1 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -208,7 +208,7 @@ namespace cryptonote
cnx.recv_idle_time = timestamp - cntxt.m_last_recv;
cnx.send_count = cntxt.m_send_cnt;
- cnx.send_idle_time = timestamp;
+ cnx.send_idle_time = timestamp - cntxt.m_last_send;
cnx.state = get_protocol_state_string(cntxt.m_state);
diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl
index f8be15dda..506c7766f 100644
--- a/src/daemonizer/posix_daemonizer.inl
+++ b/src/daemonizer/posix_daemonizer.inl
@@ -43,6 +43,10 @@ namespace daemonizer
"detach"
, "Run as daemon"
};
+ const command_line::arg_descriptor<std::string> arg_pidfile = {
+ "pidfile"
+ , "File path to write the daemon's PID to (optional, requires --detach)"
+ };
const command_line::arg_descriptor<bool> arg_non_interactive = {
"non-interactive"
, "Run non-interactive"
@@ -55,6 +59,7 @@ namespace daemonizer
)
{
command_line::add_arg(normal_options, arg_detach);
+ command_line::add_arg(normal_options, arg_pidfile);
command_line::add_arg(normal_options, arg_non_interactive);
}
@@ -80,7 +85,12 @@ namespace daemonizer
if (command_line::has_arg(vm, arg_detach))
{
tools::success_msg_writer() << "Forking to background...";
- posix::fork();
+ std::string pidfile;
+ if (command_line::has_arg(vm, arg_pidfile))
+ {
+ pidfile = command_line::get_arg(vm, arg_pidfile);
+ }
+ posix::fork(pidfile);
auto daemon = executor.create_daemon(vm);
return daemon.run();
}
diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp
index d9b4a6d0e..4dff04f3f 100644
--- a/src/daemonizer/posix_fork.cpp
+++ b/src/daemonizer/posix_fork.cpp
@@ -21,15 +21,43 @@
namespace posix {
namespace {
- void quit(std::string const & message)
+ void quit(const std::string & message)
{
LOG_ERROR(message);
throw std::runtime_error(message);
}
}
-void fork()
+void fork(const std::string & pidfile)
{
+ // If a PID file is specified, we open the file here, because
+ // we can't report errors after the fork operation.
+ // When we fork, we close thise file in each of the parent
+ // processes.
+ // Only in the final child process do we write the PID to the
+ // file (and close it).
+ std::ofstream pidofs;
+ if (! pidfile.empty ())
+ {
+ int oldpid;
+ std::ifstream pidrifs;
+ pidrifs.open(pidfile, std::fstream::in);
+ if (! pidrifs.fail())
+ {
+ // Read the PID and send signal 0 to see if the process exists.
+ if (pidrifs >> oldpid && oldpid > 1 && kill(oldpid, 0) == 0)
+ {
+ quit("PID file " + pidfile + " already exists and the PID therein is valid");
+ }
+ pidrifs.close();
+ }
+
+ pidofs.open(pidfile, std::fstream::out | std::fstream::trunc);
+ if (pidofs.fail())
+ {
+ quit("Failed to open specified PID file for writing");
+ }
+ }
// Fork the process and have the parent exit. If the process was started
// from a shell, this returns control to the user. Forking a new process is
// also a prerequisite for the subsequent call to setsid().
@@ -38,7 +66,7 @@ void fork()
if (pid > 0)
{
// We're in the parent process and need to exit.
- //
+ pidofs.close();
// When the exit() function is used, the program terminates without
// invoking local variables' destructors. Only global variables are
// destroyed.
@@ -59,6 +87,7 @@ void fork()
{
if (pid > 0)
{
+ pidofs.close();
exit(0);
}
else
@@ -67,6 +96,13 @@ void fork()
}
}
+ if (! pidofs.fail())
+ {
+ int pid = ::getpid();
+ pidofs << pid << std::endl;
+ pidofs.close();
+ }
+
// Close the standard streams. This decouples the daemon from the terminal
// that started it.
close(0);
diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h
index 459417d25..77ef4cb19 100644
--- a/src/daemonizer/posix_fork.h
+++ b/src/daemonizer/posix_fork.h
@@ -1,21 +1,21 @@
// Copyright (c) 2014-2017, The Monero Project
-//
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -27,12 +27,13 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
+#include <string>
#ifndef WIN32
namespace posix {
-void fork();
+void fork(const std::string & pidfile);
}
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 97fe18696..097958d24 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -227,6 +227,28 @@ namespace cryptonote
res.status = CORE_RPC_STATUS_OK;
return true;
}
+ bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res)
+ {
+ CHECK_CORE_BUSY();
+ std::list<block> blks;
+
+ if(!m_core.get_alternative_blocks(blks))
+ {
+ res.status = "Failed";
+ return false;
+ }
+
+ res.blks_hashes.reserve(blks.size());
+
+ for (auto const& blk: blks)
+ {
+ res.blks_hashes.push_back(epee::string_tools::pod_to_hex(get_block_hash(blk)));
+ }
+
+ MDEBUG("on_get_alt_blocks_hashes: " << blks.size() << " blocks " );
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res)
{
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 44ac6f07a..cf95b0edf 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -81,6 +81,7 @@ namespace cryptonote
MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN)
MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS)
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
+ MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES)
MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT)
MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX)
MAP_URI_AUTO_JON2_IF("/start_mining", on_start_mining, COMMAND_RPC_START_MINING, !m_restricted)
@@ -128,6 +129,7 @@ namespace cryptonote
bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res);
bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res);
+ bool on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res);
bool on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res);
bool on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res);
bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 7a1f5a963..a38952bff 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -49,7 +49,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1
-#define CORE_RPC_VERSION_MINOR 12
+#define CORE_RPC_VERSION_MINOR 13
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -146,6 +146,25 @@ namespace cryptonote
};
};
+ struct COMMAND_RPC_GET_ALT_BLOCKS_HASHES
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::vector<std::string> blks_hashes;
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(blks_hashes)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
struct COMMAND_RPC_GET_HASHES_FAST
{
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index adf2fde80..a1edc7a50 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -2375,7 +2375,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
{
auto & ptx = ptx_vector.back();
m_wallet->commit_tx(ptx);
- success_msg_writer(true) << tr("Money successfully sent, transaction ") << get_transaction_hash(ptx.tx);
+ success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << get_transaction_hash(ptx.tx) << ENDL
+ << tr("You can check its status by using the `show_transfers` command.");
// if no exception, remove element from vector
ptx_vector.pop_back();
@@ -2978,12 +2979,39 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
// gather info to ask the user
uint64_t amount = 0, amount_to_dests = 0, change = 0;
size_t min_mixin = ~0;
- std::unordered_map<std::string, uint64_t> dests;
+ std::unordered_map<std::string, std::pair<std::string, uint64_t>> dests;
const std::string wallet_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
int first_known_non_zero_change_index = -1;
+ std::string payment_id_string = "";
for (size_t n = 0; n < get_num_txes(); ++n)
{
const tools::wallet2::tx_construction_data &cd = get_tx(n);
+
+ std::vector<tx_extra_field> tx_extra_fields;
+ bool has_encrypted_payment_id = false;
+ crypto::hash8 payment_id8 = cryptonote::null_hash8;
+ if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
+ {
+ tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash payment_id;
+ if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ if (!payment_id_string.empty())
+ payment_id_string += ", ";
+ payment_id_string = std::string("encrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id8);
+ has_encrypted_payment_id = true;
+ }
+ else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ if (!payment_id_string.empty())
+ payment_id_string += ", ";
+ payment_id_string = std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id);
+ }
+ }
+ }
+
for (size_t s = 0; s < cd.sources.size(); ++s)
{
amount += cd.sources[s].amount;
@@ -2994,23 +3022,30 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
{
const tx_destination_entry &entry = cd.splitted_dsts[d];
- std::string address = get_account_address_as_str(m_wallet->testnet(), entry.addr);
- std::unordered_map<std::string,uint64_t>::iterator i = dests.find(address);
+ std::string address, standard_address = get_account_address_as_str(m_wallet->testnet(), entry.addr);
+ if (has_encrypted_payment_id)
+ {
+ address = get_account_integrated_address_as_str(m_wallet->testnet(), entry.addr, payment_id8);
+ address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")");
+ }
+ else
+ address = standard_address;
+ std::unordered_map<std::string,std::pair<std::string,uint64_t>>::iterator i = dests.find(standard_address);
if (i == dests.end())
- dests.insert(std::make_pair(address, entry.amount));
+ dests.insert(std::make_pair(standard_address, std::make_pair(address, entry.amount)));
else
- i->second += entry.amount;
+ i->second.second += entry.amount;
amount_to_dests += entry.amount;
}
if (cd.change_dts.amount > 0)
{
- std::unordered_map<std::string, uint64_t>::iterator it = dests.find(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr));
+ std::unordered_map<std::string, std::pair<std::string, uint64_t>>::iterator it = dests.find(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr));
if (it == dests.end())
{
fail_msg_writer() << tr("Claimed change does not go to a paid address");
return false;
}
- if (it->second < cd.change_dts.amount)
+ if (it->second.second < cd.change_dts.amount)
{
fail_msg_writer() << tr("Claimed change is larger than payment to the change address");
return false;
@@ -3026,15 +3061,19 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
}
}
change += cd.change_dts.amount;
- it->second -= cd.change_dts.amount;
- if (it->second == 0)
+ it->second.second -= cd.change_dts.amount;
+ if (it->second.second == 0)
dests.erase(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr));
}
}
+
+ if (payment_id_string.empty())
+ payment_id_string = "no payment ID";
+
std::string dest_string;
- for (std::unordered_map<std::string, uint64_t>::const_iterator i = dests.begin(); i != dests.end(); )
+ for (std::unordered_map<std::string, std::pair<std::string, uint64_t>>::const_iterator i = dests.begin(); i != dests.end(); )
{
- dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second) % i->first).str();
+ dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second.second) % i->second.first).str();
++i;
if (i != dests.end())
dest_string += ", ";
@@ -3052,7 +3091,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
change_string += tr("no change");
uint64_t fee = amount - amount_to_dests;
- std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % extra_message).str();
+ std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu, %s. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % payment_id_string % extra_message).str();
return command_line::is_yes(command_line::input_line(prompt_str));
}
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 6b1026a55..034b0a02a 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1388,7 +1388,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::update_pool_state()
+void wallet2::update_pool_state(bool refreshed)
{
MDEBUG("update_pool_state start");
@@ -1424,13 +1424,14 @@ void wallet2::update_pool_state()
// a tx is removed from the pool due to being found in a new block, but
// just before the block is visible by refresh. So we keep a boolean, so
// that the first time we don't see the tx, we set that boolean, and only
- // delete it the second time it is checked
+ // delete it the second time it is checked (but only when refreshed, so
+ // we're sure we've seen the blockchain state first)
if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending)
{
LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool");
pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool;
}
- else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool)
+ else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed)
{
LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed");
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
@@ -1459,24 +1460,30 @@ void wallet2::update_pool_state()
MDEBUG("update_pool_state done first loop");
// remove pool txes to us that aren't in the pool anymore
- std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
- while (uit != m_unconfirmed_payments.end())
+ // but only if we just refreshed, so that the tx can go in
+ // the in transfers list instead (or nowhere if it just
+ // disappeared without being mined)
+ if (refreshed)
{
- const crypto::hash &txid = uit->second.m_tx_hash;
- bool found = false;
- for (const auto &it2: res.tx_hashes)
+ std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
+ while (uit != m_unconfirmed_payments.end())
{
- if (it2 == txid)
+ const crypto::hash &txid = uit->second.m_tx_hash;
+ bool found = false;
+ for (const auto &it2: res.tx_hashes)
{
- found = true;
- break;
+ if (it2 == txid)
+ {
+ found = true;
+ break;
+ }
+ }
+ auto pit = uit++;
+ if (!found)
+ {
+ MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
+ m_unconfirmed_payments.erase(pit);
}
- }
- auto pit = uit++;
- if (!found)
- {
- MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
- m_unconfirmed_payments.erase(pit);
}
}
MDEBUG("update_pool_state done second loop");
@@ -1490,7 +1497,16 @@ void wallet2::update_pool_state()
LOG_PRINT_L2("Already seen " << txid << ", skipped");
continue;
}
- if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end())
+ bool txid_found_in_up = false;
+ for (const auto &up: m_unconfirmed_payments)
+ {
+ if (up.second.m_tx_hash == txid)
+ {
+ txid_found_in_up = true;
+ break;
+ }
+ }
+ if (!txid_found_in_up)
{
LOG_PRINT_L1("Found new pool tx: " << txid);
bool found = false;
@@ -1685,6 +1701,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
+ bool refreshed = false;
// pull the first set of blocks
get_short_chain_history(short_chain_history);
@@ -1726,6 +1743,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
if(blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
+ refreshed = true;
break;
}
@@ -1764,7 +1782,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
{
// If stop() is called we don't need to check pending transactions
if(m_run.load(std::memory_order_relaxed))
- update_pool_state();
+ update_pool_state(refreshed);
}
catch (...)
{
@@ -4359,7 +4377,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// try to pick outputs not from the same block. We will get two outputs, one for
// the destination, and one for change.
LOG_PRINT_L2("checking preferred");
- std::vector<size_t> prefered_inputs;
+ std::vector<size_t> preferred_inputs;
uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
if (use_rct && get_num_rct_outputs() >= rct_outs_needed)
@@ -4367,12 +4385,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count + 1, 2), fee_multiplier);
- prefered_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee);
- if (!prefered_inputs.empty())
+ preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee);
+ if (!preferred_inputs.empty())
{
string s;
- for (auto i: prefered_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
- LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s);
+ for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
+ LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s);
}
}
LOG_PRINT_L2("done checking preferred");
@@ -4427,8 +4445,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
pop_if_present(unused_transfers_indices, idx);
pop_if_present(unused_dust_indices, idx);
- } else if (!prefered_inputs.empty()) {
- idx = pop_back(prefered_inputs);
+ } else if (!preferred_inputs.empty()) {
+ idx = pop_back(preferred_inputs);
pop_if_present(unused_transfers_indices, idx);
pop_if_present(unused_dust_indices, idx);
} else
@@ -4521,7 +4539,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_testnet, i->addr) << " from " <<
- print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accomodate " <<
+ print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accommodate " <<
print_money(needed_fee) << " fee");
dsts[0].amount += i->amount - new_paid_amount;
i->amount = new_paid_amount;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index e7692badb..b853c5f3c 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -585,7 +585,7 @@ namespace tools
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
- void update_pool_state();
+ void update_pool_state(bool refreshed = false);
std::string encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const;
std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated = true) const;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index e7b9b5a71..0dc1fe07e 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -454,7 +454,8 @@ namespace tools
return false;
}
- m_wallet->commit_tx(ptx_vector);
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hash
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
@@ -463,6 +464,13 @@ namespace tools
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
}
res.fee = ptx_vector.back().fee;
+
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx_vector.back().tx, blob);
+ res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
+ }
return true;
}
catch (const tools::error::daemon_busy& e)
@@ -519,9 +527,12 @@ namespace tools
ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
- LOG_PRINT_L2("on_transfer_split calling commit_tx");
- m_wallet->commit_tx(ptx_vector);
- LOG_PRINT_L2("on_transfer_split called commit_tx");
+ if (!req.do_not_relay)
+ {
+ LOG_PRINT_L2("on_transfer_split calling commit_tx");
+ m_wallet->commit_tx(ptx_vector);
+ LOG_PRINT_L2("on_transfer_split called commit_tx");
+ }
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -538,6 +549,13 @@ namespace tools
res.amount_list.push_back(ptx_amount);
res.fee_list.push_back(ptx.fee);
+
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
+ }
}
return true;
@@ -577,7 +595,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- m_wallet->commit_tx(ptx_vector);
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -588,6 +607,12 @@ namespace tools
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
res.fee_list.push_back(ptx.fee);
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
+ }
}
return true;
@@ -640,7 +665,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
- m_wallet->commit_tx(ptx_vector);
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -650,7 +676,12 @@ namespace tools
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
- res.fee_list.push_back(ptx.fee);
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
+ }
}
return true;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 12ac281e4..5832bb032 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -119,6 +119,8 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_key;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -127,6 +129,8 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_key)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -136,12 +140,14 @@ namespace wallet_rpc
std::string tx_key;
std::list<std::string> amount_keys;
uint64_t fee;
+ std::string tx_blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount_keys)
KV_SERIALIZE(fee)
+ KV_SERIALIZE(tx_blob)
END_KV_SERIALIZE_MAP()
};
};
@@ -156,6 +162,8 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_keys;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -164,6 +172,8 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -182,12 +192,14 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
+ std::list<std::string> tx_blob_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(tx_blob_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -197,9 +209,13 @@ namespace wallet_rpc
struct request
{
bool get_tx_keys;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(get_tx_keys)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -217,11 +233,13 @@ namespace wallet_rpc
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
+ std::list<std::string> tx_blob_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(tx_blob_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -237,6 +255,8 @@ namespace wallet_rpc
std::string payment_id;
bool get_tx_keys;
uint64_t below_amount;
+ bool do_not_relay;
+ bool get_tx_hex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
@@ -246,6 +266,8 @@ namespace wallet_rpc
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE(below_amount)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
END_KV_SERIALIZE_MAP()
};
@@ -263,11 +285,13 @@ namespace wallet_rpc
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
+ std::list<std::string> tx_blob_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(tx_blob_list)
END_KV_SERIALIZE_MAP()
};
};