aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp4
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h4
-rw-r--r--src/cryptonote_basic/miner.cpp112
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp19
-rw-r--r--src/cryptonote_core/cryptonote_core.h15
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h14
-rw-r--r--src/daemon/main.cpp2
-rw-r--r--src/daemon/rpc_command_executor.cpp4
-rw-r--r--src/mnemonics/electrum-words.cpp77
-rw-r--r--src/mnemonics/electrum-words.h22
-rw-r--r--src/rpc/core_rpc_server.cpp2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h2
-rw-r--r--src/serialization/serialization.h3
-rw-r--r--src/serialization/set.h127
-rw-r--r--src/simplewallet/simplewallet.cpp511
-rw-r--r--src/simplewallet/simplewallet.h3
-rw-r--r--src/wallet/CMakeLists.txt33
-rw-r--r--src/wallet/api/CMakeLists.txt89
-rw-r--r--src/wallet/api/address_book.h2
-rw-r--r--src/wallet/api/pending_transaction.h2
-rw-r--r--src/wallet/api/subaddress.h2
-rw-r--r--src/wallet/api/subaddress_account.h2
-rw-r--r--src/wallet/api/transaction_history.h2
-rw-r--r--src/wallet/api/transaction_info.h2
-rw-r--r--src/wallet/api/unsigned_transaction.h2
-rw-r--r--src/wallet/api/wallet.cpp186
-rw-r--r--src/wallet/api/wallet.h7
-rw-r--r--src/wallet/api/wallet2_api.h (renamed from src/wallet/wallet2_api.h)18
-rw-r--r--src/wallet/api/wallet_manager.cpp149
-rw-r--r--src/wallet/api/wallet_manager.h3
-rw-r--r--src/wallet/wallet2.cpp807
-rw-r--r--src/wallet/wallet2.h33
-rw-r--r--src/wallet/wallet_args.cpp2
-rw-r--r--src/wallet/wallet_errors.h22
-rw-r--r--src/wallet/wallet_rpc_server.cpp344
-rw-r--r--src/wallet/wallet_rpc_server.h14
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h203
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h4
38 files changed, 2118 insertions, 731 deletions
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 1c28ca4d8..3c760493f 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -994,7 +994,7 @@ namespace cryptonote
block_hashes_cached = block_hashes_cached_count;
}
//---------------------------------------------------------------
- crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase)
+ crypto::secret_key encrypt_key(crypto::secret_key key, const std::string &passphrase)
{
crypto::hash hash;
crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash);
@@ -1002,7 +1002,7 @@ namespace cryptonote
return key;
}
//---------------------------------------------------------------
- crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase)
+ crypto::secret_key decrypt_key(crypto::secret_key key, const std::string &passphrase)
{
crypto::hash hash;
crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash);
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index f88310c4c..aebeaa6f4 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -226,8 +226,8 @@ namespace cryptonote
bool is_valid_decomposed_amount(uint64_t amount);
void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached);
- crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase);
- crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase);
+ crypto::secret_key encrypt_key(crypto::secret_key key, const std::string &passphrase);
+ crypto::secret_key decrypt_key(crypto::secret_key key, const std::string &passphrase);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \
specific_type& variable_name = boost::get<specific_type>(variant_var);
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 0aafe24e1..c90ab0f03 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -53,6 +53,19 @@
#include <TargetConditionals.h>
#endif
+#ifdef __FreeBSD__
+#include <devstat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <machine/apm_bios.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "miner"
@@ -735,8 +748,6 @@ namespace cryptonote
#elif defined(__linux__)
- const std::string STR_CPU("cpu");
- const std::size_t STR_CPU_LEN = STR_CPU.size();
const std::string STAT_FILE_PATH = "/proc/stat";
if( !epee::file_io_utils::is_file_exist(STAT_FILE_PATH) )
@@ -785,9 +796,36 @@ namespace cryptonote
return true;
+ #elif defined(__FreeBSD__)
+
+ struct statinfo s;
+ size_t n = sizeof(s.cp_time);
+ if( sysctlbyname("kern.cp_time", s.cp_time, &n, NULL, 0) == -1 )
+ {
+ LOG_ERROR("sysctlbyname(\"kern.cp_time\"): " << strerror(errno));
+ return false;
+ }
+ if( n != sizeof(s.cp_time) )
+ {
+ LOG_ERROR("sysctlbyname(\"kern.cp_time\") output is unexpectedly "
+ << n << " bytes instead of the expected " << sizeof(s.cp_time)
+ << " bytes.");
+ return false;
+ }
+
+ idle_time = s.cp_time[CP_IDLE];
+ total_time =
+ s.cp_time[CP_USER] +
+ s.cp_time[CP_NICE] +
+ s.cp_time[CP_SYS] +
+ s.cp_time[CP_INTR] +
+ s.cp_time[CP_IDLE];
+
+ return true;
+
#endif
- return false; // unsupported systemm..
+ return false; // unsupported system
}
//-----------------------------------------------------------------------------------------------------
bool miner::get_process_time(uint64_t& total_time)
@@ -807,7 +845,7 @@ namespace cryptonote
return true;
}
- #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__)
+ #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__)
struct tms tms;
if ( times(&tms) != (clock_t)-1 )
@@ -818,7 +856,7 @@ namespace cryptonote
#endif
- return false; // unsupported system..
+ return false; // unsupported system
}
//-----------------------------------------------------------------------------------------------------
uint8_t miner::get_percent_of_total(uint64_t other, uint64_t total)
@@ -929,6 +967,70 @@ namespace cryptonote
}
return on_battery;
+ #elif defined(__FreeBSD__)
+ int ac;
+ size_t n = sizeof(ac);
+ if( sysctlbyname("hw.acpi.acline", &ac, &n, NULL, 0) == -1 )
+ {
+ if( errno != ENOENT )
+ {
+ LOG_ERROR("Cannot query battery status: "
+ << "sysctlbyname(\"hw.acpi.acline\"): " << strerror(errno));
+ return boost::logic::tribool(boost::logic::indeterminate);
+ }
+
+ // If sysctl fails with ENOENT, then try querying /dev/apm.
+
+ static const char* dev_apm = "/dev/apm";
+ const int fd = open(dev_apm, O_RDONLY);
+ if( fd == -1 ) {
+ LOG_ERROR("Cannot query battery status: "
+ << "open(): " << dev_apm << ": " << strerror(errno));
+ return boost::logic::tribool(boost::logic::indeterminate);
+ }
+
+ apm_info info;
+ if( ioctl(fd, APMIO_GETINFO, &info) == -1 ) {
+ close(fd);
+ LOG_ERROR("Cannot query battery status: "
+ << "ioctl(" << dev_apm << ", APMIO_GETINFO): " << strerror(errno));
+ return boost::logic::tribool(boost::logic::indeterminate);
+ }
+
+ close(fd);
+
+ // See apm(8).
+ switch( info.ai_acline )
+ {
+ case 0: // off-line
+ case 2: // backup power
+ return boost::logic::tribool(true);
+ case 1: // on-line
+ return boost::logic::tribool(false);
+ }
+ switch( info.ai_batt_stat )
+ {
+ case 0: // high
+ case 1: // low
+ case 2: // critical
+ return boost::logic::tribool(true);
+ case 3: // charging
+ return boost::logic::tribool(false);
+ }
+
+ LOG_ERROR("Cannot query battery status: "
+ << "sysctl hw.acpi.acline is not available and /dev/apm returns "
+ << "unexpected ac-line status (" << info.ai_acline << ") and "
+ << "battery status (" << info.ai_batt_stat << ").");
+ return boost::logic::tribool(boost::logic::indeterminate);
+ }
+ if( n != sizeof(ac) )
+ {
+ LOG_ERROR("sysctlbyname(\"hw.acpi.acline\") output is unexpectedly "
+ << n << " bytes instead of the expected " << sizeof(ac) << " bytes.");
+ return boost::logic::tribool(boost::logic::indeterminate);
+ }
+ return boost::logic::tribool(ac == 0);
#endif
LOG_ERROR("couldn't query power status");
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index acc76a8d6..5c181208f 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1336,6 +1336,7 @@ namespace cryptonote
m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this));
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
+ m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
m_miner.on_idle();
m_mempool.on_idle();
return true;
@@ -1467,6 +1468,17 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ bool core::check_disk_space()
+ {
+ uint64_t free_space = get_free_space();
+ if (free_space < 1ull * 1024 * 1024 * 1024) // 1 GB
+ {
+ const el::Level level = el::Level::Warning;
+ MCLOG_RED(level, "global", "Free space is below 1 GB on " << m_config_folder);
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
void core::set_target_blockchain_height(uint64_t target_blockchain_height)
{
m_target_blockchain_height = target_blockchain_height;
@@ -1482,6 +1494,13 @@ namespace cryptonote
return get_blockchain_storage().prevalidate_block_hashes(height, hashes);
}
//-----------------------------------------------------------------------------------------------
+ uint64_t core::get_free_space() const
+ {
+ boost::filesystem::path path(m_config_folder);
+ boost::filesystem::space_info si = boost::filesystem::space(path);
+ return si.available;
+ }
+ //-----------------------------------------------------------------------------------------------
std::time_t core::get_start_time() const
{
return start_time;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index dc014206d..905e67f6d 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -766,6 +766,13 @@ namespace cryptonote
*/
uint64_t prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes);
+ /**
+ * @brief get free disk space on the blockchain partition
+ *
+ * @return free space in bytes
+ */
+ uint64_t get_free_space() const;
+
private:
/**
@@ -925,6 +932,13 @@ namespace cryptonote
*/
bool check_updates();
+ /**
+ * @brief checks free disk space
+ *
+ * @return true on success, false otherwise
+ */
+ bool check_disk_space();
+
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
@@ -948,6 +962,7 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
+ epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 9a3b2484d..8d9a1e332 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -53,6 +53,20 @@ namespace cryptonote
rct::key mask; //ringct amount mask
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(outputs)
+ FIELD(real_output)
+ FIELD(real_out_tx_key)
+ FIELD(real_out_additional_tx_keys)
+ FIELD(real_output_in_tx_index)
+ FIELD(amount)
+ FIELD(rct)
+ FIELD(mask)
+
+ if (real_output >= outputs.size())
+ return false;
+ END_SERIALIZE()
};
struct tx_destination_entry
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index ae83943b6..d038cc825 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -278,7 +278,7 @@ int main(int argc, char const * argv[])
tools::set_stack_trace_log(log_file_path.filename().string());
#endif // STACK_TRACE
- if (command_line::has_arg(vm, daemon_args::arg_max_concurrency))
+ if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency));
// logging is now set up
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 230b9f090..d7ee28baa 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -1304,6 +1304,7 @@ bool t_rpc_command_executor::start_save_graph()
}
}
+ tools::success_msg_writer() << "Saving graph is now on";
return true;
}
@@ -1329,6 +1330,7 @@ bool t_rpc_command_executor::stop_save_graph()
return true;
}
}
+ tools::success_msg_writer() << "Saving graph is now off";
return true;
}
@@ -1495,6 +1497,7 @@ bool t_rpc_command_executor::flush_txpool(const std::string &txid)
}
}
+ tools::success_msg_writer() << "Pool successfully flushed";
return true;
}
@@ -1797,6 +1800,7 @@ bool t_rpc_command_executor::relay_tx(const std::string &txid)
}
}
+ tools::success_msg_writer() << "Transaction successfully relayed";
return true;
}
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 6ae33a743..1b14905f6 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -236,11 +236,13 @@ namespace crypto
/*!
* \brief Converts seed words to bytes (secret key).
* \param words String containing the words separated by spaces.
- * \param dst To put the secret key restored from the words.
+ * \param dst To put the secret data restored from the words.
+ * \param len The number of bytes to expect, 0 if unknown
+ * \param duplicate If true and len is not zero, we accept half the data, and duplicate it
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
*/
- bool words_to_bytes(std::string words, crypto::secret_key& dst,
+ bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate,
std::string &language_name)
{
std::vector<std::string> seed;
@@ -248,15 +250,23 @@ namespace crypto
boost::algorithm::trim(words);
boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
- // error on non-compliant word list
- if (seed.size() != seed_length/2 && seed.size() != seed_length &&
- seed.size() != seed_length + 1)
- {
+ if (len % 4)
return false;
- }
- // If it is seed with a checksum.
- bool has_checksum = seed.size() == (seed_length + 1);
+ bool has_checksum = true;
+ if (len)
+ {
+ // error on non-compliant word list
+ const size_t expected = len * 8 * 3 / 32;
+ if (seed.size() != expected/2 && seed.size() != expected &&
+ seed.size() != expected + 1)
+ {
+ return false;
+ }
+
+ // If it is seed with a checksum.
+ has_checksum = seed.size() == (expected + 1);
+ }
std::vector<uint32_t> matched_indices;
Language::Base *language;
@@ -290,32 +300,55 @@ namespace crypto
if (!(val % word_list_length == w1)) return false;
- memcpy(dst.data + i * 4, &val, 4); // copy 4 bytes to position
+ dst.append((const char*)&val, 4); // copy 4 bytes to position
}
- std::string wlist_copy = words;
- if (seed.size() == seed_length/2)
+ if (len > 0 && duplicate)
{
- memcpy(dst.data+16, dst.data, 16); // if electrum 12-word seed, duplicate
- wlist_copy += ' ';
- wlist_copy += words;
+ const size_t expected = len * 3 / 32;
+ std::string wlist_copy = words;
+ if (seed.size() == expected/2)
+ {
+ dst.append(dst); // if electrum 12-word seed, duplicate
+ wlist_copy += ' ';
+ wlist_copy += words;
+ }
}
return true;
}
/*!
+ * \brief Converts seed words to bytes (secret key).
+ * \param words String containing the words separated by spaces.
+ * \param dst To put the secret key restored from the words.
+ * \param language_name Language of the seed as found gets written here.
+ * \return false if not a multiple of 3 words, or if word is not in the words list
+ */
+ bool words_to_bytes(std::string words, crypto::secret_key& dst,
+ std::string &language_name)
+ {
+ std::string s;
+ if (!words_to_bytes(words, s, sizeof(dst), true, language_name))
+ return false;
+ if (s.size() != sizeof(dst))
+ return false;
+ dst = *(const crypto::secret_key*)s.data();
+ return true;
+ }
+
+ /*!
* \brief Converts bytes (secret key) to seed words.
* \param src Secret key
* \param words Space delimited concatenated words get written here.
* \param language_name Seed language name
* \return true if successful false if not. Unsuccessful if wrong key size.
*/
- bool bytes_to_words(const crypto::secret_key& src, std::string& words,
+ bool bytes_to_words(const char *src, size_t len, std::string& words,
const std::string &language_name)
{
- if (sizeof(src.data) % 4 != 0 || sizeof(src.data) == 0) return false;
+ if (len % 4 != 0 || len == 0) return false;
Language::Base *language;
if (language_name == "English")
@@ -376,13 +409,13 @@ namespace crypto
uint32_t word_list_length = word_list.size();
// 8 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626
- for (unsigned int i=0; i < sizeof(src.data)/4; i++, words += ' ')
+ for (unsigned int i=0; i < len/4; i++, words += ' ')
{
uint32_t w1, w2, w3;
uint32_t val;
- memcpy(&val, (src.data) + (i * 4), 4);
+ memcpy(&val, src + (i * 4), 4);
w1 = val % word_list_length;
w2 = ((val / word_list_length) + w1) % word_list_length;
@@ -404,6 +437,12 @@ namespace crypto
return true;
}
+ bool bytes_to_words(const crypto::secret_key& src, std::string& words,
+ const std::string &language_name)
+ {
+ return bytes_to_words(src.data, sizeof(src), words, language_name);
+ }
+
/*!
* \brief Gets a list of seed languages that are supported.
* \param languages The vector is set to the list of languages.
diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h
index 94ce9c200..941768352 100644
--- a/src/mnemonics/electrum-words.h
+++ b/src/mnemonics/electrum-words.h
@@ -64,6 +64,17 @@ namespace crypto
/*!
* \brief Converts seed words to bytes (secret key).
* \param words String containing the words separated by spaces.
+ * \param dst To put the secret data restored from the words.
+ * \param len The number of bytes to expect, 0 if unknown
+ * \param duplicate If true and len is not zero, we accept half the data, and duplicate it
+ * \param language_name Language of the seed as found gets written here.
+ * \return false if not a multiple of 3 words, or if word is not in the words list
+ */
+ bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate,
+ std::string &language_name);
+ /*!
+ * \brief Converts seed words to bytes (secret key).
+ * \param words String containing the words separated by spaces.
* \param dst To put the secret key restored from the words.
* \param language_name Language of the seed as found gets written here.
* \return false if not a multiple of 3 words, or if word is not in the words list
@@ -72,6 +83,17 @@ namespace crypto
std::string &language_name);
/*!
+ * \brief Converts bytes to seed words.
+ * \param src Secret data
+ * \param len Secret data length in bytes (positive multiples of 4 only)
+ * \param words Space delimited concatenated words get written here.
+ * \param language_name Seed language name
+ * \return true if successful false if not. Unsuccessful if wrong key size.
+ */
+ bool bytes_to_words(const char *src, size_t len, std::string& words,
+ const std::string &language_name);
+
+ /*!
* \brief Converts bytes (secret key) to seed words.
* \param src Secret key
* \param words Space delimited concatenated words get written here.
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 6e7fb7e8e..e9a6a18aa 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -149,6 +149,7 @@ namespace cryptonote
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
res.status = CORE_RPC_STATUS_OK;
res.start_time = (uint64_t)m_core.get_start_time();
+ res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1328,6 +1329,7 @@ namespace cryptonote
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
res.status = CORE_RPC_STATUS_OK;
res.start_time = (uint64_t)m_core.get_start_time();
+ res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 15b4b503a..d27d5611e 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -927,6 +927,7 @@ namespace cryptonote
uint64_t cumulative_difficulty;
uint64_t block_size_limit;
uint64_t start_time;
+ uint64_t free_space;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
@@ -947,6 +948,7 @@ namespace cryptonote
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE(block_size_limit)
KV_SERIALIZE(start_time)
+ KV_SERIALIZE(free_space)
END_KV_SERIALIZE_MAP()
};
};
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 869f5d10e..9e23f0791 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -43,6 +43,8 @@
#include <vector>
#include <deque>
#include <list>
+#include <set>
+#include <unordered_set>
#include <string>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/integral_constant.hpp>
@@ -364,3 +366,4 @@ namespace serialization {
#include "vector.h"
#include "list.h"
#include "pair.h"
+#include "set.h"
diff --git a/src/serialization/set.h b/src/serialization/set.h
new file mode 100644
index 000000000..54b4eb3ab
--- /dev/null
+++ b/src/serialization/set.h
@@ -0,0 +1,127 @@
+// 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
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include "serialization.h"
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::set<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::set<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v);
+
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename Archive, class T>
+ bool serialize_set_element(Archive& ar, T& e)
+ {
+ return ::do_serialize(ar, e);
+ }
+
+ template <typename Archive>
+ bool serialize_set_element(Archive& ar, uint32_t& e)
+ {
+ ar.serialize_varint(e);
+ return true;
+ }
+
+ template <typename Archive>
+ bool serialize_set_element(Archive& ar, uint64_t& e)
+ {
+ ar.serialize_varint(e);
+ return true;
+ }
+ }
+}
+
+template <template <bool> class Archive, class T>
+bool do_serialize_set(Archive<false> &ar, T &v)
+{
+ size_t cnt;
+ ar.begin_array(cnt);
+ if (!ar.stream().good())
+ return false;
+ v.clear();
+
+ // very basic sanity check
+ if (ar.remaining_bytes() < cnt) {
+ ar.stream().setstate(std::ios::failbit);
+ return false;
+ }
+
+ for (size_t i = 0; i < cnt; i++) {
+ if (i > 0)
+ ar.delimit_array();
+ typename T::key_type k;
+ if (!::serialization::detail::serialize_set_element(ar, k))
+ return false;
+ v.insert(std::move(k));
+ if (!ar.stream().good())
+ return false;
+ }
+ ar.end_array();
+ return true;
+}
+
+template <template <bool> class Archive, class T>
+bool do_serialize_set(Archive<true> &ar, T &v)
+{
+ size_t cnt = v.size();
+ ar.begin_array(cnt);
+ bool first = true;
+ for (const typename T::key_type &k: v) {
+ if (!ar.stream().good())
+ return false;
+ if (!first)
+ ar.delimit_array();
+ if(!::serialization::detail::serialize_set_element(ar, const_cast<typename T::key_type&>(k)))
+ return false;
+ if (!ar.stream().good())
+ return false;
+ first = false;
+ }
+ ar.end_array();
+ return true;
+}
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::set<T> &v) { return do_serialize_set(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::set<T> &v) { return do_serialize_set(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v) { return do_serialize_set(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v) { return do_serialize_set(ar, v); }
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 8b1f0862a..17462bb81 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -80,6 +80,8 @@ typedef cryptonote::simple_wallet sw;
#define DEFAULT_MIX 4
+#define MIN_RING_SIZE 5 // Used to inform user about min ring size -- does not track actual protocol
+
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
#define LOCK_IDLE_SCOPE() \
@@ -619,27 +621,27 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/*
{
if (strchr(args[1].c_str(), '-'))
{
- fail_msg_writer() << tr("ring size must be an integer >= 3");
+ fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE;
return true;
}
uint32_t ring_size = boost::lexical_cast<uint32_t>(args[1]);
- if (ring_size < 3 && ring_size != 0)
+ if (ring_size < MIN_RING_SIZE && ring_size != 0)
{
- fail_msg_writer() << tr("ring size must be an integer >= 3");
+ fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE;
return true;
}
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->default_mixin(ring_size - 1);
+ m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
}
return true;
}
catch(const boost::bad_lexical_cast &)
{
- fail_msg_writer() << tr("ring size must be an integer >= 3");
+ fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE;
return true;
}
catch(...)
@@ -929,8 +931,10 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>"));
m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>"));
- m_cmd_binder.set_handler("get_tx_proof", boost::bind(&simple_wallet::get_tx_proof, this, _1), tr("Generate a signature to prove payment to <address> in <txid> using the transaction secret key (r) without revealing it"));
+ m_cmd_binder.set_handler("get_tx_proof", boost::bind(&simple_wallet::get_tx_proof, this, _1), tr("Generate a signature proving payment/receipt of money to/by <address> in <txid> using the transaction/view secret key"));
m_cmd_binder.set_handler("check_tx_proof", boost::bind(&simple_wallet::check_tx_proof, this, _1), tr("Check tx proof for payment going to <address> in <txid>"));
+ m_cmd_binder.set_handler("get_spend_proof", boost::bind(&simple_wallet::get_spend_proof, this, _1), tr("Generate a signature proving that you generated <txid> using the spend secret key"));
+ m_cmd_binder.set_handler("check_spend_proof", boost::bind(&simple_wallet::check_spend_proof, this, _1), tr("Check a signature proving that the signer generated <txid>"));
m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), tr("show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] - Show incoming/outgoing transfers within an optional height range"));
m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::unspent_outputs, this, _1), tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] - Show 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 blockchain from scratch"));
@@ -1009,7 +1013,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1"));
- CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= 3"));
+ CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE);
CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)"));
CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4"));
@@ -3911,22 +3915,24 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
}
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(local_args[0], txid))
{
fail_msg_writer() << tr("failed to parse txid");
return true;
}
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
LOCK_IDLE_SCOPE();
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- std::vector<crypto::secret_key> amount_keys;
if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
{
- success_msg_writer() << tr("Tx key: ") << epee::string_tools::pod_to_hex(tx_key);
+ ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ success_msg_writer() << tr("Tx key: ") << oss.str();
return true;
}
else
@@ -3938,19 +3944,18 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
{
- if(args.size() != 2) {
- fail_msg_writer() << tr("usage: get_tx_proof <txid> <dest_address>");
+ if (args.size() != 2 && args.size() != 3)
+ {
+ fail_msg_writer() << tr("usage: get_tx_proof_out <txid> <address> [<message>]");
return true;
}
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(args[0], txid_data) || txid_data.size() != sizeof(crypto::hash))
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(args[0], txid))
{
fail_msg_writer() << tr("failed to parse txid");
return true;
}
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
cryptonote::address_parse_info info;
if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), args[1], oa_prompter))
@@ -3959,140 +3964,21 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
return true;
}
- LOCK_IDLE_SCOPE();
-
- crypto::secret_key tx_key;
- std::vector<crypto::secret_key> additional_tx_keys;
- if (!m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
- {
- fail_msg_writer() << tr("Tx secret key wasn't found in the wallet file.");
- return true;
- }
-
- // fetch tx prefix either from the daemon or from the wallet cache if it's still pending
- cryptonote::transaction_prefix tx;
- // first, look up the wallet cache
- std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> unconfirmed_payments_out;
- m_wallet->get_unconfirmed_payments_out(unconfirmed_payments_out, m_current_subaddress_account);
- auto found = std::find_if(unconfirmed_payments_out.begin(), unconfirmed_payments_out.end(), [&](const std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>& p)
- {
- return p.first == txid;
- });
- // if not found, query the daemon
- if (found == unconfirmed_payments_out.end())
- {
- COMMAND_RPC_GET_TRANSACTIONS::request req;
- COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
- if (!net_utils::invoke_http_json("/gettransactions", req, res, m_http_client) ||
- (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
- {
- fail_msg_writer() << tr("failed to get transaction from daemon");
- return true;
- }
- cryptonote::blobdata tx_data;
- bool ok;
- if (res.txs.size() == 1)
- ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
- else
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- if (!ok)
- {
- fail_msg_writer() << tr("failed to parse transaction from daemon");
- return true;
- }
- crypto::hash tx_hash, tx_prefix_hash;
- cryptonote::transaction tx_full;
- if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx_full, tx_hash, tx_prefix_hash))
- {
- fail_msg_writer() << tr("failed to validate transaction from daemon");
- return true;
- }
- if (tx_hash != txid)
- {
- fail_msg_writer() << tr("failed to get the right transaction from daemon");
- return true;
- }
- tx = tx_full;
- }
- else
- {
- tx = found->second.m_tx;
- }
-
- // fetch tx pubkey
- crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
- if (tx_pub_key == crypto::null_pkey)
- {
- fail_msg_writer() << tr("Tx pubkey was not found");
- return true;
- }
- const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
- if (additional_tx_keys.size() != additional_tx_pub_keys.size())
- {
- fail_msg_writer() << tr("The set of additional tx secret/public keys doesn't match");
- return true;
- }
-
- // find the correct R:
- // R = r*G for standard addresses
- // R = r*B for subaddresses (where B is the recipient's spend pubkey)
- crypto::public_key R;
- crypto::secret_key r;
- if (info.is_subaddress)
- {
- auto i = additional_tx_keys.begin();
- auto j = additional_tx_pub_keys.begin();
- for (; ; ++i, ++j) {
- if (i == additional_tx_keys.end())
- {
- r = tx_key;
- R = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(info.address.m_spend_public_key), rct::sk2rct(r)));
- if (R == tx_pub_key)
- break;
- fail_msg_writer() << tr("The matching tx secret/pubkey pair wasn't found for this subaddress");
- return true;
- }
- else
- {
- r = *i;
- R = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(info.address.m_spend_public_key), rct::sk2rct(r)));
- if (R == *j)
- break;
- }
- }
- }
- else
- {
- r = tx_key;
- crypto::secret_key_to_public_key(r, R);
- if (R != tx_pub_key)
- {
- fail_msg_writer() << tr("The destinations of this tx don't include a standard address");
- return true;
- }
- }
+ if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
- crypto::public_key rA = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(info.address.m_view_public_key), rct::sk2rct(r)));
- crypto::signature sig;
try
{
- if (info.is_subaddress)
- crypto::generate_tx_proof(txid, R, info.address.m_view_public_key, info.address.m_spend_public_key, rA, r, sig);
+ std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : "");
+ const std::string filename = "monero_tx_proof";
+ if (epee::file_io_utils::save_string_to_file(filename, sig_str))
+ success_msg_writer() << tr("signature file saved to: ") << filename;
else
- crypto::generate_tx_proof(txid, R, info.address.m_view_public_key, boost::none, rA, r, sig);
+ fail_msg_writer() << tr("failed to save signature file");
}
- catch (const std::runtime_error &e)
+ catch (const std::exception &e)
{
- fail_msg_writer() << e.what();
- return true;
+ fail_msg_writer() << tr("error: ") << e.what();
}
-
- std::string sig_str = std::string("ProofV1") +
- tools::base58::encode(std::string((const char *)&rA, sizeof(crypto::public_key))) +
- tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature)));
-
- success_msg_writer() << tr("Signature: ") << sig_str;
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -4113,27 +3999,31 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
fail_msg_writer() << tr("wallet is null");
return true;
}
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data) || txid_data.size() != sizeof(crypto::hash))
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(local_args[0], txid))
{
fail_msg_writer() << tr("failed to parse txid");
return true;
}
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
- if (local_args[1].size() < 64 || local_args[1].size() % 64)
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ if(!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key))
{
fail_msg_writer() << tr("failed to parse tx key");
return true;
}
- crypto::secret_key tx_key;
- cryptonote::blobdata tx_key_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data) || tx_key_data.size() != sizeof(crypto::secret_key))
+ local_args[1] = local_args[1].substr(64);
+ while (!local_args[1].empty())
{
- fail_msg_writer() << tr("failed to parse tx key");
- return true;
+ additional_tx_keys.resize(additional_tx_keys.size() + 1);
+ if(!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back()))
+ {
+ fail_msg_writer() << tr("failed to parse tx key");
+ return true;
+ }
+ local_args[1] = local_args[1].substr(64);
}
- tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
cryptonote::address_parse_info info;
if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), local_args[2], oa_prompter))
@@ -4142,134 +4032,48 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
return true;
}
- crypto::key_derivation derivation;
- if (!crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation))
- {
- fail_msg_writer() << tr("failed to generate key derivation from supplied parameters");
- return true;
- }
-
- return check_tx_key_helper(txid, info.address, info.is_subaddress, derivation);
-}
-//----------------------------------------------------------------------------------------------------
-bool simple_wallet::check_tx_key_helper(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const crypto::key_derivation &derivation)
-{
- COMMAND_RPC_GET_TRANSACTIONS::request req;
- COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
- if (!net_utils::invoke_http_json("/gettransactions", req, res, m_http_client) ||
- (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
- {
- fail_msg_writer() << tr("failed to get transaction from daemon");
- return true;
- }
- cryptonote::blobdata tx_data;
- bool ok;
- if (res.txs.size() == 1)
- ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
- else
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- if (!ok)
- {
- fail_msg_writer() << tr("failed to parse transaction from daemon");
- return true;
- }
- crypto::hash tx_hash, tx_prefix_hash;
- cryptonote::transaction tx;
- if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash))
- {
- fail_msg_writer() << tr("failed to validate transaction from daemon");
- return true;
- }
- if (tx_hash != txid)
+ try
{
- fail_msg_writer() << tr("failed to get the right transaction from daemon");
- return true;
- }
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+ m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
- uint64_t received = 0;
- try {
- for (size_t n = 0; n < tx.vout.size(); ++n)
+ if (received > 0)
{
- if (typeid(txout_to_key) != tx.vout[n].target.type())
- continue;
- const txout_to_key tx_out_to_key = boost::get<txout_to_key>(tx.vout[n].target);
- crypto::public_key pubkey;
- derive_public_key(derivation, n, address.m_spend_public_key, pubkey);
- if (pubkey == tx_out_to_key.key)
+ success_msg_writer() << get_account_address_as_str(m_wallet->testnet(), info.is_subaddress, info.address) << " " << tr("received") << " " << print_money(received) << " " << tr("in txid") << " " << txid;
+ if (in_pool)
{
- uint64_t amount;
- if (tx.version == 1)
+ success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
+ }
+ else
+ {
+ if (confirmations != (uint64_t)-1)
{
- amount = tx.vout[n].amount;
+ success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
}
else
{
- try
- {
- rct::key Ctmp;
- //rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
- {
- crypto::secret_key scalar1;
- crypto::derivation_to_scalar(derivation, n, scalar1);
- rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
- rct::key C = tx.rct_signatures.outPk[n].mask;
- rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
- if (rct::equalKeys(C, Ctmp))
- amount = rct::h2d(ecdh_info.amount);
- else
- amount = 0;
- }
- }
- catch (...) { amount = 0; }
+ success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
}
- received += amount;
}
}
- }
- catch(const std::exception &e)
- {
- LOG_ERROR("error: " << e.what());
- fail_msg_writer() << tr("error: ") << e.what();
- return true;
- }
-
- std::string address_str = get_account_address_as_str(m_wallet->testnet(), is_subaddress, address);
- if (received > 0)
- {
- success_msg_writer() << address_str << " " << tr("received") << " " << print_money(received) << " " << tr("in txid") << " " << txid;
- }
- else
- {
- fail_msg_writer() << address_str << " " << tr("received nothing in txid") << " " << txid;
- }
- if (res.txs.front().in_pool)
- {
- success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
- }
- else
- {
- std::string err;
- uint64_t bc_height = get_daemon_blockchain_height(err);
- if (err.empty())
- {
- uint64_t confirmations = bc_height - (res.txs.front().block_height + 1);
- success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
- }
else
{
- success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
+ fail_msg_writer() << get_account_address_as_str(m_wallet->testnet(), info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid;
}
}
-
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("error: ") << e.what();
+ }
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
{
- if(args.size() != 3) {
- fail_msg_writer() << tr("usage: check_tx_proof <txid> <address> <signature>");
+ if(args.size() != 3 && args.size() != 4) {
+ fail_msg_writer() << tr("usage: check_tx_proof <txid> <address> <signature_file> [<message>]");
return true;
}
@@ -4277,13 +4081,12 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
return true;
// parse txid
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(args[0], txid_data) || txid_data.size() != sizeof(crypto::hash))
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(args[0], txid))
{
fail_msg_writer() << tr("failed to parse txid");
return true;
}
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
// parse address
cryptonote::address_parse_info info;
@@ -4293,117 +4096,141 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
return true;
}
- // parse pubkey r*A & signature
- std::string sig_str = args[2];
- const size_t header_len = strlen("ProofV1");
- if (sig_str.size() < header_len || sig_str.substr(0, header_len) != "ProofV1")
+ // read signature file
+ std::string sig_str;
+ if (!epee::file_io_utils::load_file_to_string(args[2], sig_str))
{
- fail_msg_writer() << tr("Signature header check error");
+ fail_msg_writer() << tr("failed to load signature file");
return true;
}
- crypto::public_key rA;
- crypto::signature sig;
- const size_t rA_len = tools::base58::encode(std::string((const char *)&rA, sizeof(crypto::public_key))).size();
- const size_t sig_len = tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature))).size();
- std::string rA_decoded;
- std::string sig_decoded;
- if (!tools::base58::decode(sig_str.substr(header_len, rA_len), rA_decoded))
+
+ try
{
- fail_msg_writer() << tr("Signature decoding error");
- return true;
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+ if (m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, args.size() == 4 ? args[3] : "", sig_str, received, in_pool, confirmations))
+ {
+ success_msg_writer() << tr("Good signature");
+ if (received > 0)
+ {
+ success_msg_writer() << get_account_address_as_str(m_wallet->testnet(), info.is_subaddress, info.address) << " " << tr("received") << " " << print_money(received) << " " << tr("in txid") << " " << txid;
+ if (in_pool)
+ {
+ success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
+ }
+ else
+ {
+ if (confirmations != (uint64_t)-1)
+ {
+ success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
+ }
+ else
+ {
+ success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
+ }
+ }
+ }
+ else
+ {
+ fail_msg_writer() << get_account_address_as_str(m_wallet->testnet(), info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid;
+ }
+ }
+ else
+ {
+ fail_msg_writer() << tr("Bad signature");
+ }
}
- if (!tools::base58::decode(sig_str.substr(header_len + rA_len, sig_len), sig_decoded))
+ catch (const std::exception &e)
{
- fail_msg_writer() << tr("Signature decoding error");
- return true;
+ fail_msg_writer() << tr("error: ") << e.what();
}
- if (sizeof(crypto::public_key) != rA_decoded.size() || sizeof(crypto::signature) != sig_decoded.size())
- {
- fail_msg_writer() << tr("Signature decoding error");
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
+{
+ if(args.size() != 1 && args.size() != 2) {
+ fail_msg_writer() << tr("usage: get_spend_proof <txid> [<message>]");
return true;
}
- memcpy(&rA, rA_decoded.data(), sizeof(crypto::public_key));
- memcpy(&sig, sig_decoded.data(), sizeof(crypto::signature));
- // fetch tx pubkey from the daemon
- COMMAND_RPC_GET_TRANSACTIONS::request req;
- COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
- if (!net_utils::invoke_http_json("/gettransactions", req, res, m_http_client) ||
- (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
+ if (m_wallet->watch_only())
{
- fail_msg_writer() << tr("failed to get transaction from daemon");
+ fail_msg_writer() << tr("wallet is watch-only and cannot generate the proof");
return true;
}
- cryptonote::blobdata tx_data;
- bool ok;
- if (res.txs.size() == 1)
- ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
- else
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- if (!ok)
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(args[0], txid))
{
- fail_msg_writer() << tr("failed to parse transaction from daemon");
+ fail_msg_writer() << tr("failed to parse txid");
return true;
}
- crypto::hash tx_hash, tx_prefix_hash;
- cryptonote::transaction tx;
- if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash))
+
+ if (!try_connect_to_daemon())
{
- fail_msg_writer() << tr("failed to validate transaction from daemon");
+ fail_msg_writer() << tr("failed to connect to the daemon");
return true;
}
- if (tx_hash != txid)
+
+ if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+
+ try
{
- fail_msg_writer() << tr("failed to get the right transaction from daemon");
- return true;
+ const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] : "");
+ const std::string filename = "monero_spend_proof";
+ if (epee::file_io_utils::save_string_to_file(filename, sig_str))
+ success_msg_writer() << tr("signature file saved to: ") << filename;
+ else
+ fail_msg_writer() << tr("failed to save signature file");
}
- crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
- if (tx_pub_key == crypto::null_pkey)
+ catch (const std::exception &e)
{
- fail_msg_writer() << tr("Tx pubkey was not found");
+ fail_msg_writer() << e.what();
+ }
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::check_spend_proof(const std::vector<std::string> &args)
+{
+ if(args.size() != 2 && args.size() != 3) {
+ fail_msg_writer() << tr("usage: check_spend_proof <txid> <signature_file> [<message>]");
return true;
}
- const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
- // check signature
- bool good_signature = false;
- if (info.is_subaddress)
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(args[0], txid))
{
- good_signature = crypto::check_tx_proof(txid, tx_pub_key, info.address.m_view_public_key, info.address.m_spend_public_key, rA, sig);
- if (!good_signature)
- {
- for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
- {
- good_signature = crypto::check_tx_proof(txid, additional_tx_pub_keys[i], info.address.m_view_public_key, info.address.m_spend_public_key, rA, sig);
- if (good_signature)
- break;
- }
- }
- }
- else
- {
- good_signature = crypto::check_tx_proof(txid, tx_pub_key, info.address.m_view_public_key, boost::none, rA, sig);
- }
- if (good_signature)
- {
- success_msg_writer() << tr("Good signature");
+ fail_msg_writer() << tr("failed to parse txid");
+ return true;
}
- else
+
+ if (!try_connect_to_daemon())
{
- fail_msg_writer() << tr("Bad signature");
+ fail_msg_writer() << tr("failed to connect to the daemon");
return true;
}
- // obtain key derivation by multiplying scalar 1 to the pubkey r*A included in the signature
- crypto::key_derivation derivation;
- if (!crypto::generate_key_derivation(rA, rct::rct2sk(rct::I), derivation))
+ std::string sig_str;
+ if (!epee::file_io_utils::load_file_to_string(args[1], sig_str))
{
- fail_msg_writer() << tr("failed to generate key derivation");
+ fail_msg_writer() << tr("failed to load signature file");
return true;
}
- return check_tx_key_helper(txid, info.address, info.is_subaddress, derivation);
+ try
+ {
+ if (m_wallet->check_spend_proof(txid, args.size() == 3 ? args[2] : "", sig_str))
+ success_msg_writer() << tr("Good signature");
+ else
+ fail_msg_writer() << tr("Bad signature");
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ return true;
}
//----------------------------------------------------------------------------------------------------
static std::string get_human_readable_timestamp(uint64_t ts)
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index d65784828..d6dde3ea7 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -161,9 +161,10 @@ namespace cryptonote
bool set_log(const std::vector<std::string> &args);
bool get_tx_key(const std::vector<std::string> &args);
bool check_tx_key(const std::vector<std::string> &args);
- bool check_tx_key_helper(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const crypto::key_derivation &derivation);
bool get_tx_proof(const std::vector<std::string> &args);
bool check_tx_proof(const std::vector<std::string> &args);
+ bool get_spend_proof(const std::vector<std::string> &args);
+ bool check_spend_proof(const std::vector<std::string> &args);
bool show_transfers(const std::vector<std::string> &args);
bool unspent_outputs(const std::vector<std::string> &args);
bool rescan_blockchain(const std::vector<std::string> &args);
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index e5c79a447..74992139d 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -33,21 +33,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(wallet_sources
wallet2.cpp
wallet_args.cpp
- node_rpc_proxy.cpp
- api/wallet.cpp
- api/wallet_manager.cpp
- api/transaction_info.cpp
- api/transaction_history.cpp
- api/pending_transaction.cpp
- api/utils.cpp
- api/address_book.cpp
- api/subaddress.cpp
- api/subaddress_account.cpp
- api/unsigned_transaction.cpp)
-
-set(wallet_api_headers
- wallet2_api.h)
-
+ node_rpc_proxy.cpp)
set(wallet_private_headers
wallet2.h
@@ -56,23 +42,12 @@ set(wallet_private_headers
wallet_rpc_server.h
wallet_rpc_server_commands_defs.h
wallet_rpc_server_error_codes.h
- node_rpc_proxy.h
- api/wallet.h
- api/wallet_manager.h
- api/transaction_info.h
- api/transaction_history.h
- api/pending_transaction.h
- api/common_defines.h
- api/address_book.h
- api/subaddress.h
- api/subaddress_account.h
- api/unsigned_transaction.h)
+ node_rpc_proxy.h)
monero_private_headers(wallet
${wallet_private_headers})
monero_add_library(wallet
${wallet_sources}
- ${wallet_api_headers}
${wallet_private_headers})
target_link_libraries(wallet
PUBLIC
@@ -127,6 +102,7 @@ install(TARGETS wallet_rpc_server DESTINATION bin)
# build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS)
set(libs_to_merge
+ wallet_api
wallet
cryptonote_core
cryptonote_basic
@@ -149,6 +125,5 @@ if (BUILD_GUI_DEPS)
install(TARGETS wallet_merged
ARCHIVE DESTINATION ${lib_folder})
- install(FILES ${wallet_api_headers}
- DESTINATION include/wallet)
+ add_subdirectory(api)
endif()
diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt
new file mode 100644
index 000000000..26127b75c
--- /dev/null
+++ b/src/wallet/api/CMakeLists.txt
@@ -0,0 +1,89 @@
+# 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
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# include (${PROJECT_SOURCE_DIR}/cmake/libutils.cmake)
+
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+
+set(wallet_api_sources
+ wallet.cpp
+ wallet_manager.cpp
+ transaction_info.cpp
+ transaction_history.cpp
+ pending_transaction.cpp
+ utils.cpp
+ address_book.cpp
+ subaddress.cpp
+ subaddress_account.cpp
+ unsigned_transaction.cpp)
+
+set(wallet_api_headers
+ wallet2_api.h)
+
+set(wallet_api_private_headers
+ wallet.h
+ wallet_manager.h
+ transaction_info.h
+ transaction_history.h
+ pending_transaction.h
+ common_defines.h
+ address_book.h
+ subaddress.h
+ subaddress_account.h
+ unsigned_transaction.h)
+
+monero_private_headers(wallet_api
+ ${wallet_api_private_headers})
+monero_add_library(wallet_api
+ ${wallet_api_sources}
+ ${wallet_api_headers}
+ ${wallet_api_private_headers})
+target_link_libraries(wallet_api
+ PUBLIC
+ wallet
+ common
+ cryptonote_core
+ mnemonics
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_SERIALIZATION_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
+ PRIVATE
+ ${EXTRA_LIBRARIES})
+
+
+if(IOS)
+ set(lib_folder lib-${ARCH})
+else()
+ set(lib_folder lib)
+endif()
+
+install(FILES ${wallet_api_headers}
+ DESTINATION include/wallet)
diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h
index 25f59128b..b92743fe6 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index e5b33ac01..c98da59da 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
#include <string>
diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h
index e3e28eba1..45fef6e67 100644
--- a/src/wallet/api/subaddress.h
+++ b/src/wallet/api/subaddress.h
@@ -26,7 +26,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h
index 107d7f87f..0a4db9671 100644
--- a/src/wallet/api/subaddress_account.h
+++ b/src/wallet/api/subaddress_account.h
@@ -26,7 +26,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
namespace Monero {
diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h
index 4987bdab2..e5207e53a 100644
--- a/src/wallet/api/transaction_history.h
+++ b/src/wallet/api/transaction_history.h
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include <boost/thread/shared_mutex.hpp>
namespace Monero {
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index d19ef8899..c961c0a9e 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include <string>
#include <ctime>
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
index 9c442f503..33c994d5f 100644
--- a/src/wallet/api/unsigned_transaction.h
+++ b/src/wallet/api/unsigned_transaction.h
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
#include <string>
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 8e747d16b..fd0b65866 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1385,24 +1385,194 @@ std::string WalletImpl::getUserNote(const std::string &txid) const
return m_wallet->get_tx_note(htxid);
}
-std::string WalletImpl::getTxKey(const std::string &txid) const
+std::string WalletImpl::getTxKey(const std::string &txid_str) const
{
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(txid_str, txid))
{
- return "";
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return "";
}
- const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- if (m_wallet->get_tx_key(htxid, tx_key, additional_tx_keys))
+ if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
{
- return epee::string_tools::pod_to_hex(tx_key);
+ m_status = Status_Ok;
+ std::ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ return oss.str();
}
else
{
- return "";
+ m_status = Status_Error;
+ m_errorString = tr("no tx keys found for this txid");
+ return "";
+ }
+}
+
+bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, const std::string &address_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return false;
+ }
+
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse tx key");
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ while (!tx_key_str.empty())
+ {
+ additional_tx_keys.resize(additional_tx_keys.size() + 1);
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse tx key");
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return false;
+ }
+
+ try
+ {
+ m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
+ m_status = Status_Ok;
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const
+{
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return "";
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return "";
+ }
+
+ try
+ {
+ m_status = Status_Ok;
+ return m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, message);
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return "";
+ }
+}
+
+bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return false;
+ }
+
+ try
+ {
+ good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, message, signature, received, in_pool, confirmations);
+ m_status = Status_Ok;
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const {
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return "";
+ }
+
+ try
+ {
+ m_status = Status_Ok;
+ return m_wallet->get_spend_proof(txid, message);
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return "";
+ }
+}
+
+bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string &message, const std::string &signature, bool &good) const {
+ good = false;
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return false;
+ }
+
+ try
+ {
+ m_status = Status_Ok;
+ good = m_wallet->check_spend_proof(txid, message, signature);
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
}
}
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 051eda3ba..01359ffc6 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -31,7 +31,7 @@
#ifndef WALLET_IMPL_H
#define WALLET_IMPL_H
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include "wallet/wallet2.h"
#include <string>
@@ -137,6 +137,11 @@ public:
virtual bool setUserNote(const std::string &txid, const std::string &note);
virtual std::string getUserNote(const std::string &txid) const;
virtual std::string getTxKey(const std::string &txid) const;
+ virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const;
+ virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ virtual std::string getSpendProof(const std::string &txid, const std::string &message) const;
+ virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const;
virtual std::string signMessage(const std::string &message);
virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const;
virtual void startRefresh();
diff --git a/src/wallet/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 432c820cb..b1f8369a3 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -700,6 +700,11 @@ struct Wallet
*/
virtual std::string getUserNote(const std::string &txid) const = 0;
virtual std::string getTxKey(const std::string &txid) const = 0;
+ virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
+ virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0;
+ virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
+ virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;
+ virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0;
/*
* \brief signMessage - sign a message with the spend private key
@@ -819,19 +824,6 @@ struct WalletManager
*/
virtual std::vector<std::string> findWallets(const std::string &path) = 0;
- /*!
- * \brief checkPayment - checks a payment was made using a txkey
- * \param address - the address the payment was sent to
- * \param txid - the transaction id for that payment
- * \param txkey - the transaction's secret key
- * \param daemon_address - the address (host and port) to the daemon to request transaction data
- * \param received - if succesful, will hold the amount of monero received
- * \param height - if succesful, will hold the height of the transaction (0 if only in the pool)
- * \param error - if unsuccesful, will hold an error string with more information about the error
- * \return - true is succesful, false otherwise
- */
- virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
-
//! returns verbose error string regarding last error;
virtual std::string errorString() const = 0;
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 2326f54d3..ee69ec028 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -189,155 +189,6 @@ bool WalletManagerImpl::connected(uint32_t *version) const
return true;
}
-bool WalletManagerImpl::checkPayment(const std::string &address_text, const std::string &txid_text, const std::string &txkey_text, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const
-{
- error = "";
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data) || txid_data.size() != sizeof(crypto::hash))
- {
- error = tr("failed to parse txid");
- return false;
- }
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
-
- if (txkey_text.size() < 64 || txkey_text.size() % 64)
- {
- error = tr("failed to parse tx key");
- return false;
- }
- crypto::secret_key tx_key;
- cryptonote::blobdata tx_key_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data) || tx_key_data.size() != sizeof(crypto::hash))
- {
- error = tr("failed to parse tx key");
- return false;
- }
- tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
-
- bool testnet = address_text[0] != '4';
- cryptonote::address_parse_info info;
- if(!cryptonote::get_account_address_from_str(info, testnet, address_text))
- {
- error = tr("failed to parse address");
- return false;
- }
-
- cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
- cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
- if (!connect_and_invoke(m_daemonAddress, "/gettransactions", req, res) ||
- (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
- {
- error = tr("failed to get transaction from daemon");
- return false;
- }
- cryptonote::blobdata tx_data;
- bool ok;
- if (res.txs.size() == 1)
- ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
- else
- ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- if (!ok)
- {
- error = tr("failed to parse transaction from daemon");
- return false;
- }
- crypto::hash tx_hash, tx_prefix_hash;
- cryptonote::transaction tx;
- if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash))
- {
- error = tr("failed to validate transaction from daemon");
- return false;
- }
- if (tx_hash != txid)
- {
- error = tr("failed to get the right transaction from daemon");
- return false;
- }
-
- crypto::key_derivation derivation;
- if (!crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation))
- {
- error = tr("failed to generate key derivation from supplied parameters");
- return false;
- }
-
- received = 0;
- try {
- for (size_t n = 0; n < tx.vout.size(); ++n)
- {
- if (typeid(cryptonote::txout_to_key) != tx.vout[n].target.type())
- continue;
- const cryptonote::txout_to_key tx_out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[n].target);
- crypto::public_key pubkey;
- derive_public_key(derivation, n, info.address.m_spend_public_key, pubkey);
- if (pubkey == tx_out_to_key.key)
- {
- uint64_t amount;
- if (tx.version == 1)
- {
- amount = tx.vout[n].amount;
- }
- else
- {
- try
- {
- rct::key Ctmp;
- //rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
- crypto::key_derivation derivation;
- bool r = crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation);
- if (!r)
- {
- LOG_ERROR("Failed to generate key derivation to decode rct output " << n);
- amount = 0;
- }
- else
- {
- crypto::secret_key scalar1;
- crypto::derivation_to_scalar(derivation, n, scalar1);
- rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
- rct::key C = tx.rct_signatures.outPk[n].mask;
- rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
- if (rct::equalKeys(C, Ctmp))
- amount = rct::h2d(ecdh_info.amount);
- else
- amount = 0;
- }
- }
- catch (...) { amount = 0; }
- }
- received += amount;
- }
- }
- }
- catch(const std::exception &e)
- {
- LOG_ERROR("error: " << e.what());
- error = std::string(tr("error: ")) + e.what();
- return false;
- }
-
- if (received > 0)
- {
- LOG_PRINT_L1(get_account_address_as_str(testnet, info.is_subaddress, info.address) << " " << tr("received") << " " << cryptonote::print_money(received) << " " << tr("in txid") << " " << txid);
- }
- else
- {
- LOG_PRINT_L1(get_account_address_as_str(testnet, info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid);
- }
- if (res.txs.front().in_pool)
- {
- height = 0;
- }
- else
- {
- height = res.txs.front().block_height;
- }
-
- return true;
-}
-
uint64_t WalletManagerImpl::blockchainHeight() const
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 8455f0f16..978a2d411 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -29,7 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "wallet/wallet2_api.h"
+#include "wallet/api/wallet2_api.h"
#include <string>
namespace Monero {
@@ -55,7 +55,6 @@ public:
std::string errorString() const;
void setDaemonAddress(const std::string &address);
bool connected(uint32_t *version = NULL) const;
- bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const;
uint64_t blockchainHeight() const;
uint64_t blockchainTargetHeight() const;
uint64_t networkDifficulty() const;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 00b096b88..3a8662890 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -39,7 +39,6 @@ using namespace epee;
#include "cryptonote_config.h"
#include "wallet2.h"
-#include "wallet2_api.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "misc_language.h"
@@ -202,6 +201,8 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
return {tools::password_container{std::move(password)}};
}
+ THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
+
return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify);
}
@@ -455,6 +456,59 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
}
}
+size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size)
+{
+ size_t size = 0;
+
+ // tx prefix
+
+ // first few bytes
+ size += 1 + 6;
+
+ // vin
+ size += n_inputs * (1+6+(mixin+1)*2+32);
+
+ // vout
+ size += n_outputs * (6+32);
+
+ // extra
+ size += extra_size;
+
+ // rct signatures
+
+ // type
+ size += 1;
+
+ // rangeSigs
+ size += (2*64*32+32+64*32) * n_outputs;
+
+ // MGs
+ size += n_inputs * (64 * (mixin+1) + 32);
+
+ // mixRing - not serialized, can be reconstructed
+ /* size += 2 * 32 * (mixin+1) * n_inputs; */
+
+ // pseudoOuts
+ size += 32 * n_inputs;
+ // ecdhInfo
+ size += 2 * 32 * n_outputs;
+ // outPk - only commitment is saved
+ size += 32 * n_outputs;
+ // txnFee
+ size += 4;
+
+ LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ return size;
+}
+
+size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size)
+{
+ if (use_rct)
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size);
+ else
+ return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
+}
+
} //namespace
namespace tools
@@ -644,8 +698,7 @@ void wallet2::add_subaddress_account(const std::string& label)
//----------------------------------------------------------------------------------------------------
void wallet2::add_subaddress(uint32_t index_major, const std::string& label)
{
- if (index_major >= m_subaddress_labels.size())
- throw std::runtime_error("index_major is out of bound");
+ THROW_WALLET_EXCEPTION_IF(index_major >= m_subaddress_labels.size(), error::account_index_outofbound);
uint32_t index_minor = (uint32_t)get_num_subaddresses(index_major);
expand_subaddresses({index_major, index_minor});
m_subaddress_labels[index_major][index_minor] = label;
@@ -701,10 +754,9 @@ std::string wallet2::get_subaddress_label(const cryptonote::subaddress_index& in
//----------------------------------------------------------------------------------------------------
void wallet2::set_subaddress_label(const cryptonote::subaddress_index& index, const std::string &label)
{
- if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size())
- MERROR("Subaddress index is out of bounds. Failed to set subaddress label.");
- else
- m_subaddress_labels[index.major][index.minor] = label;
+ THROW_WALLET_EXCEPTION_IF(index.major >= m_subaddress_labels.size(), error::account_index_outofbound);
+ THROW_WALLET_EXCEPTION_IF(index.minor >= m_subaddress_labels[index.major].size(), error::address_index_outofbound);
+ m_subaddress_labels[index.major][index.minor] = label;
}
//----------------------------------------------------------------------------------------------------
/*!
@@ -1025,7 +1077,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
uint64_t tx_money_spent_in_ins = 0;
- boost::optional<uint32_t> subaddr_account;
+ // The line below is equivalent to "boost::optional<uint32_t> subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function
+ // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679
+ auto subaddr_account ([]()->boost::optional<uint32_t> {return boost::none;}());
std::set<uint32_t> subaddr_indices;
// check all outputs for spending (compare key images)
for(auto& in: tx.vin)
@@ -3990,7 +4044,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary.
- uint64_t needed_fee = 0;
+ const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size());
+ uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do
{
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon);
@@ -4580,9 +4635,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
uint64_t needed_money = fee;
LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money));
- LOG_PRINT_L0("selected transfers: ");
- for (auto t: selected_transfers)
- LOG_PRINT_L2(" " << t);
+ LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " "));
// calculate total amount being sent to all destinations
// throw if total amount overflows uint64_t
@@ -4666,21 +4719,25 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
change_dts.amount = found_money - needed_money;
if (change_dts.amount == 0)
{
- // If the change is 0, send it to a random address, to avoid confusing
- // the sender with a 0 amount output. We send a 0 amount in order to avoid
- // letting the destination be able to work out which of the inputs is the
- // real one in our rings
- LOG_PRINT_L2("generating dummy address for 0 change");
- cryptonote::account_base dummy;
- dummy.generate();
- change_dts.addr = dummy.get_keys().m_account_address;
- LOG_PRINT_L2("generated dummy address for 0 change");
+ if (splitted_dsts.size() == 1)
+ {
+ // If the change is 0, send it to a random address, to avoid confusing
+ // the sender with a 0 amount output. We send a 0 amount in order to avoid
+ // letting the destination be able to work out which of the inputs is the
+ // real one in our rings
+ LOG_PRINT_L2("generating dummy address for 0 change");
+ cryptonote::account_base dummy;
+ dummy.generate();
+ change_dts.addr = dummy.get_keys().m_account_address;
+ LOG_PRINT_L2("generated dummy address for 0 change");
+ splitted_dsts.push_back(change_dts);
+ }
}
else
{
change_dts.addr = get_subaddress({subaddr_account, 0});
+ splitted_dsts.push_back(change_dts);
}
- splitted_dsts.push_back(change_dts);
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
@@ -4727,59 +4784,6 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("transfer_selected_rct done");
}
-static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
-{
- size_t size = 0;
-
- // tx prefix
-
- // first few bytes
- size += 1 + 6;
-
- // vin
- size += n_inputs * (1+6+(mixin+1)*2+32);
-
- // vout
- size += n_outputs * (6+32);
-
- // extra
- size += 40;
-
- // rct signatures
-
- // type
- size += 1;
-
- // rangeSigs
- size += (2*64*32+32+64*32) * n_outputs;
-
- // MGs
- size += n_inputs * (64 * (mixin+1) + 32);
-
- // mixRing - not serialized, can be reconstructed
- /* size += 2 * 32 * (mixin+1) * n_inputs; */
-
- // pseudoOuts
- size += 32 * n_inputs;
- // ecdhInfo
- size += 2 * 32 * n_outputs;
- // outPk - only commitment is saved
- size += 32 * n_outputs;
- // txnFee
- size += 4;
-
- LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
- return size;
-}
-
-static size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs)
-{
- if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1);
- else
- return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES;
-}
-
std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const
{
std::vector<size_t> picks;
@@ -4787,19 +4791,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
LOG_PRINT_L2("pick_preferred_rct_inputs: needed_money " << print_money(needed_money));
- // try to find a rct input of enough size
- for (size_t i = 0; i < m_transfers.size(); ++i)
- {
- const transfer_details& td = m_transfers[i];
- if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
- {
- LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
- picks.push_back(i);
- return picks;
- }
- }
-
- // then try to find two outputs
+ // try to find two outputs
// this could be made better by picking one of the outputs to be a small one, since those
// are less useful since often below the needed money, so if one can be used in a pair,
// it gets rid of it for the future
@@ -5553,12 +5545,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);
+ uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size()), fee_multiplier);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
string s;
- for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") ";
+ for (auto i: preferred_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);
// bring the list of available outputs stored by the same subaddress index to the front of the list
@@ -5590,14 +5582,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
unsigned int original_output_index = 0;
std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second;
std::vector<size_t>* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second;
- while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
+ while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
TX &tx = txes.back();
LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size());
- LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size());
LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
- LOG_PRINT_L2("unused_dust_indices:" << strjoin(*unused_dust_indices, " "));
- LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? -1 : dsts[0].amount));
+ LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
+ LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount)));
LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct);
// if we need to spend money and don't have any left, we fail
@@ -5609,7 +5600,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
size_t idx;
- if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
+ if (!preferred_inputs.empty()) {
+ idx = pop_back(preferred_inputs);
+ pop_if_present(*unused_transfers_indices, idx);
+ pop_if_present(*unused_dust_indices, idx);
+ } else if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
// the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
std::vector<size_t> indices = get_only_rct(*unused_dust_indices, *unused_transfers_indices);
idx = pop_best_value(indices, tx.selected_transfers, true);
@@ -5632,10 +5627,6 @@ 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 (!preferred_inputs.empty()) {
- idx = pop_back(preferred_inputs);
- pop_if_present(*unused_transfers_indices, idx);
- pop_if_present(*unused_dust_indices, idx);
} else
idx = pop_best_value(unused_transfers_indices->empty() ? *unused_dust_indices : *unused_transfers_indices, tx.selected_transfers);
@@ -5657,7 +5648,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -5669,7 +5660,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
+ if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@@ -5682,26 +5673,31 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit);
- bool try_tx;
- if (adding_fee)
- {
- /* might not actually be enough if adding this output bumps size to next kB, but we need to try */
- try_tx = available_for_fee >= needed_fee;
- }
- else
+ bool try_tx = false;
+ // if we have preferred picks, but haven't yet used all of them, continue
+ if (preferred_inputs.empty())
{
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size());
- try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ if (adding_fee)
+ {
+ /* might not actually be enough if adding this output bumps size to next kB, but we need to try */
+ try_tx = available_for_fee >= needed_fee;
+ }
+ else
+ {
+ const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size());
+ try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ }
}
if (try_tx) {
cryptonote::transaction test_tx;
pending_tx test_ptx;
- needed_fee = 0;
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size());
+ needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
- LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
- tx.selected_transfers.size() << " outputs");
+ LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
+ tx.selected_transfers.size() << " inputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx);
@@ -5743,8 +5739,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
- do {
+ LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
+ while (needed_fee > test_ptx.fee) {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx);
@@ -5755,7 +5751,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
- } while (needed_fee > test_ptx.fee);
+ }
LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
@@ -5937,14 +5933,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit);
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
+ const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size());
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
if (try_tx) {
cryptonote::transaction test_tx;
pending_tx test_ptx;
- needed_fee = 0;
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size());
+ needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
@@ -5964,7 +5961,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
- do {
+ while (needed_fee > test_ptx.fee) {
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct)
@@ -5977,7 +5974,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
- } while (needed_fee > test_ptx.fee);
+ }
LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
@@ -6207,6 +6204,576 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
additional_tx_keys = j->second;
return true;
}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message)
+{
+ THROW_WALLET_EXCEPTION_IF(m_watch_only, error::wallet_internal_error,
+ "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
+
+ // fetch tx from daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr");
+ cryptonote::transaction tx;
+ crypto::hash tx_hash, tx_prefix_hash;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
+
+ std::vector<std::vector<crypto::signature>> signatures;
+
+ // get signature prefix hash
+ std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
+ sig_prefix_data += message;
+ crypto::hash sig_prefix_hash;
+ crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
+
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key == nullptr)
+ continue;
+
+ // check if the key image belongs to us
+ const auto found = m_key_images.find(in_key->k_image);
+ if(found == m_key_images.end())
+ {
+ THROW_WALLET_EXCEPTION_IF(i > 0, error::wallet_internal_error, "subset of key images belong to us, very weird!");
+ THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "This tx wasn't generated by this wallet!");
+ }
+
+ // derive the real output keypair
+ const transfer_details& in_td = m_transfers[found->second];
+ const txout_to_key* const in_tx_out_pkey = boost::get<txout_to_key>(std::addressof(in_td.m_tx.vout[in_td.m_internal_output_index].target));
+ THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
+ const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
+ const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
+ keypair in_ephemeral;
+ crypto::key_image in_img;
+ THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img),
+ error::wallet_internal_error, "failed to generate key image");
+ THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
+
+ // get output pubkeys in the ring
+ const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
+ const size_t ring_size = in_key->key_offsets.size();
+ THROW_WALLET_EXCEPTION_IF(absolute_offsets.size() != ring_size, error::wallet_internal_error, "absolute offsets size is wrong");
+ COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
+ req.outputs.resize(ring_size);
+ for (size_t j = 0; j < ring_size; ++j)
+ {
+ req.outputs[j].amount = in_key->amount;
+ req.outputs[j].index = absolute_offsets[j];
+ }
+ COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
+
+ // copy pubkey pointers
+ std::vector<const crypto::public_key *> p_output_keys;
+ for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
+ p_output_keys.push_back(&out.key);
+
+ // figure out real output index and secret key
+ size_t sec_index = -1;
+ for (size_t j = 0; j < ring_size; ++j)
+ {
+ if (res.outs[j].key == in_ephemeral.pub)
+ {
+ sec_index = j;
+ break;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(sec_index >= ring_size, error::wallet_internal_error, "secret index not found");
+
+ // generate ring sig for this input
+ signatures.push_back(std::vector<crypto::signature>());
+ std::vector<crypto::signature>& sigs = signatures.back();
+ sigs.resize(in_key->key_offsets.size());
+ crypto::generate_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, in_ephemeral.sec, sec_index, sigs.data());
+ }
+
+ std::string sig_str = "SpendProofV1";
+ for (const std::vector<crypto::signature>& ring_sig : signatures)
+ for (const crypto::signature& sig : ring_sig)
+ sig_str += tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature)));
+ return sig_str;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
+{
+ const std::string header = "SpendProofV1";
+ const size_t header_len = header.size();
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
+ "Signature header check error");
+
+ // fetch tx from daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr");
+ cryptonote::transaction tx;
+ crypto::hash tx_hash, tx_prefix_hash;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
+
+ // check signature size
+ size_t num_sigs = 0;
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key != nullptr)
+ num_sigs += in_key->key_offsets.size();
+ }
+ std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
+ const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * sig_len,
+ error::wallet_internal_error, "incorrect signature size");
+
+ // decode base58
+ signatures.clear();
+ size_t offset = header_len;
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key == nullptr)
+ continue;
+ signatures.resize(signatures.size() + 1);
+ signatures.back().resize(in_key->key_offsets.size());
+ for (size_t j = 0; j < in_key->key_offsets.size(); ++j)
+ {
+ std::string sig_decoded;
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, sig_len), sig_decoded), error::wallet_internal_error, "Signature decoding error");
+ THROW_WALLET_EXCEPTION_IF(sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, "Signature decoding error");
+ memcpy(&signatures.back()[j], sig_decoded.data(), sizeof(crypto::signature));
+ offset += sig_len;
+ }
+ }
+
+ // get signature prefix hash
+ std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
+ sig_prefix_data += message;
+ crypto::hash sig_prefix_hash;
+ crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
+
+ std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key == nullptr)
+ continue;
+
+ // get output pubkeys in the ring
+ COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
+ const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
+ req.outputs.resize(absolute_offsets.size());
+ for (size_t j = 0; j < absolute_offsets.size(); ++j)
+ {
+ req.outputs[j].amount = in_key->amount;
+ req.outputs[j].index = absolute_offsets[j];
+ }
+ COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
+
+ // copy pointers
+ std::vector<const crypto::public_key *> p_output_keys;
+ for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
+ p_output_keys.push_back(&out.key);
+
+ // check this ring
+ if (!crypto::check_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, sig_iter->data()))
+ return false;
+ ++sig_iter;
+ }
+ THROW_WALLET_EXCEPTION_IF(sig_iter != signatures.cend(), error::wallet_internal_error, "Signature iterator didn't reach the end");
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+
+void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ crypto::key_derivation derivation;
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation), error::wallet_internal_error,
+ "Failed to generate key derivation from supplied parameters");
+
+ std::vector<crypto::key_derivation> additional_derivations;
+ additional_derivations.resize(additional_tx_keys.size());
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i]), error::wallet_internal_error,
+ "Failed to generate key derivation from supplied parameters");
+
+ check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
+}
+
+void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::blobdata tx_data;
+ if (res.txs.size() == 1)
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error,
+ "Failed to get the right transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error,
+ "The size of additional derivations is wrong");
+
+ received = 0;
+ for (size_t n = 0; n < tx.vout.size(); ++n)
+ {
+ const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
+ if (!out_key)
+ continue;
+
+ crypto::public_key derived_out_key;
+ derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ bool found = out_key->key == derived_out_key;
+ crypto::key_derivation found_derivation = derivation;
+ if (!found && !additional_derivations.empty())
+ {
+ derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ found = out_key->key == derived_out_key;
+ found_derivation = additional_derivations[n];
+ }
+
+ if (found)
+ {
+ uint64_t amount;
+ if (tx.version == 1 || tx.rct_signatures.type == rct::RCTTypeNull)
+ {
+ amount = tx.vout[n].amount;
+ }
+ else
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(found_derivation, n, scalar1);
+ rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
+ const rct::key C = tx.rct_signatures.outPk[n].mask;
+ rct::key Ctmp;
+ rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
+ if (rct::equalKeys(C, Ctmp))
+ amount = rct::h2d(ecdh_info.amount);
+ else
+ amount = 0;
+ }
+ received += amount;
+ }
+ }
+
+ in_pool = res.txs.front().in_pool;
+ confirmations = (uint64_t)-1;
+ if (!in_pool)
+ {
+ std::string err;
+ uint64_t bc_height = get_daemon_blockchain_height(err);
+ if (err.empty())
+ confirmations = bc_height - (res.txs.front().block_height + 1);
+ }
+}
+
+std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
+{
+ // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
+ const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
+
+ std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
+ prefix_data += message;
+ crypto::hash prefix_hash;
+ crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
+
+ std::vector<crypto::public_key> shared_secret;
+ std::vector<crypto::signature> sig;
+ std::string sig_str;
+ if (is_out)
+ {
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file.");
+
+ const size_t num_sigs = 1 + additional_tx_keys.size();
+ shared_secret.resize(num_sigs);
+ sig.resize(num_sigs);
+
+ shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
+ crypto::public_key tx_pub_key;
+ if (is_subaddress)
+ {
+ tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key)));
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
+ }
+ else
+ {
+ crypto::secret_key_to_public_key(tx_key, tx_pub_key);
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
+ }
+ for (size_t i = 1; i < num_sigs; ++i)
+ {
+ shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1])));
+ if (is_subaddress)
+ {
+ tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1])));
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
+ }
+ else
+ {
+ crypto::secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
+ }
+ }
+ sig_str = std::string("OutProofV1");
+ }
+ else
+ {
+ // fetch tx pubkey from the daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::blobdata tx_data;
+ if (res.txs.size() == 1)
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
+
+ std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
+ const size_t num_sigs = 1 + additional_tx_pub_keys.size();
+ shared_secret.resize(num_sigs);
+ sig.resize(num_sigs);
+
+ const crypto::secret_key& a = m_account.get_keys().m_view_secret_key;
+ shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(tx_pub_key), rct::sk2rct(a)));
+ if (is_subaddress)
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
+ }
+ else
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
+ }
+ for (size_t i = 1; i < num_sigs; ++i)
+ {
+ shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a)));
+ if (is_subaddress)
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
+ }
+ else
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]);
+ }
+ }
+ sig_str = std::string("InProofV1");
+ }
+ const size_t num_sigs = shared_secret.size();
+
+ // check if this address actually received any funds
+ crypto::key_derivation derivation;
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
+ std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
+ for (size_t i = 1; i < num_sigs; ++i)
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+ check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
+ THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
+
+ // concatenate all signature strings
+ for (size_t i = 0; i < num_sigs; ++i)
+ sig_str +=
+ tools::base58::encode(std::string((const char *)&shared_secret[i], sizeof(crypto::public_key))) +
+ tools::base58::encode(std::string((const char *)&sig[i], sizeof(crypto::signature)));
+ return sig_str;
+}
+
+bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ const bool is_out = sig_str.substr(0, 3) == "Out";
+ const std::string header = is_out ? "OutProofV1" : "InProofV1";
+ const size_t header_len = header.size();
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
+ "Signature header check error");
+
+ // decode base58
+ std::vector<crypto::public_key> shared_secret(1);
+ std::vector<crypto::signature> sig(1);
+ const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size();
+ const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size();
+ const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error,
+ "Wrong signature size");
+ shared_secret.resize(num_sigs);
+ sig.resize(num_sigs);
+ for (size_t i = 0; i < num_sigs; ++i)
+ {
+ std::string pk_decoded;
+ std::string sig_decoded;
+ const size_t offset = header_len + i * (pk_len + sig_len);
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error,
+ "Signature decoding error");
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error,
+ "Signature decoding error");
+ THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error,
+ "Signature decoding error");
+ memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key));
+ memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature));
+ }
+
+ // fetch tx pubkey from the daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::blobdata tx_data;
+ if (res.txs.size() == 1)
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
+
+ std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys");
+
+ std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
+ prefix_data += message;
+ crypto::hash prefix_hash;
+ crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
+
+ // check signature
+ std::vector<int> good_signature(num_sigs, 0);
+ if (is_out)
+ {
+ good_signature[0] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
+ crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]);
+
+ for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
+ {
+ good_signature[i + 1] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
+ crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1]);
+ }
+ }
+ else
+ {
+ good_signature[0] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0]);
+
+ for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
+ {
+ good_signature[i + 1] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], boost::none, shared_secret[i + 1], sig[i + 1]);
+ }
+ }
+
+ if (std::any_of(good_signature.begin(), good_signature.end(), [](int i) { return i > 0; }))
+ {
+ // obtain key derivation by multiplying scalar 1 to the shared secret
+ crypto::key_derivation derivation;
+ if (good_signature[0])
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
+
+ std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
+ for (size_t i = 1; i < num_sigs; ++i)
+ if (good_signature[i])
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
+
+ check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
+ return true;
+ }
+ return false;
+}
std::string wallet2::get_wallet_file() const
{
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 6e01d4e28..ce0c67fc3 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -292,6 +292,19 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change
uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer
std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(sources)
+ FIELD(change_dts)
+ FIELD(splitted_dsts)
+ FIELD(selected_transfers)
+ FIELD(extra)
+ FIELD(unlock_time)
+ FIELD(use_rct)
+ FIELD(dests)
+ FIELD(subaddr_account)
+ FIELD(subaddr_indices)
+ END_SERIALIZE()
};
typedef std::vector<transfer_details> transfer_container;
@@ -313,6 +326,20 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dests;
tx_construction_data construction_data;
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(tx)
+ FIELD(dust)
+ FIELD(fee)
+ FIELD(dust_added_to_fee)
+ FIELD(change_dts)
+ FIELD(selected_transfers)
+ FIELD(key_images)
+ FIELD(tx_key)
+ FIELD(additional_tx_keys)
+ FIELD(dests)
+ FIELD(construction_data)
+ END_SERIALIZE()
};
// The term "Unsigned tx" is not really a tx since it's not signed yet.
@@ -684,7 +711,13 @@ namespace tools
uint32_t get_confirm_backlog_threshold() const { return m_confirm_backlog_threshold; };
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
+ void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message);
+ bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ std::string get_spend_proof(const crypto::hash &txid, const std::string &message);
+ bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str);
/*!
* \brief GUI Address book get/store
*/
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index cc6bb1de2..e665042d4 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -178,7 +178,7 @@ namespace wallet_args
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
}
- if(command_line::has_arg(vm, arg_max_concurrency))
+ if (!command_line::is_arg_defaulted(vm, arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 41eb77451..48fce40dd 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -269,6 +269,28 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
+ struct index_outofbound : public wallet_logic_error
+ {
+ explicit index_outofbound(std::string&& loc, const std::string& message)
+ : wallet_logic_error(std::move(loc), message)
+ {
+ }
+ };
+ struct account_index_outofbound : public index_outofbound
+ {
+ explicit account_index_outofbound(std::string&& loc)
+ : index_outofbound(std::move(loc), "account index is out of bound")
+ {
+ }
+ };
+ struct address_index_outofbound: public index_outofbound
+ {
+ explicit address_index_outofbound(std::string&& loc)
+ : index_outofbound(std::move(loc), "address index is out of bound")
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct acc_outs_lookup_error : public refresh_error
{
explicit acc_outs_lookup_error(std::string&& loc, const cryptonote::transaction& tx,
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index f5838d013..c315684de 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -30,6 +30,7 @@
#include <boost/format.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
+#include <boost/algorithm/string.hpp>
#include <cstdint>
#include "include_base_utils.h"
using namespace epee;
@@ -58,6 +59,7 @@ namespace
const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false};
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
+ const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
constexpr const char default_rpc_username[] = "monero";
@@ -376,27 +378,23 @@ namespace tools
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);
- m_wallet->add_subaddress(req.account_index, req.label);
- res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
- res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index});
+ try
+ {
+ m_wallet->add_subaddress(req.account_index, req.label);
+ res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
+ res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index});
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::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)
{
if (!m_wallet) return not_open(er);
- if (req.index.major >= m_wallet->get_num_subaddress_accounts())
- {
- er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND;
- er.message = "Account index is out of bound";
- return false;
- }
- if (req.index.minor >= m_wallet->get_num_subaddresses(req.index.major))
- {
- er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND;
- er.message = "Address index is out of bound";
- return false;
- }
try
{
m_wallet->set_subaddress_label(req.index, req.label);
@@ -458,12 +456,6 @@ namespace tools
bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
- if (req.account_index >= m_wallet->get_num_subaddress_accounts())
- {
- er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND;
- er.message = "Account index is out of bound";
- return false;
- }
try
{
m_wallet->set_subaddress_label({req.account_index, 0}, req.label);
@@ -623,6 +615,8 @@ namespace tools
if (req.get_tx_key)
{
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
+ for (const crypto::secret_key& additional_tx_key : ptx_vector.back().additional_tx_keys)
+ res.tx_key += epee::string_tools::pod_to_hex(additional_tx_key);
}
res.fee = ptx_vector.back().fee;
@@ -632,6 +626,13 @@ namespace tools
tx_to_blob(ptx_vector.back().tx, blob);
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, ptx_vector.back());
+ res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
return true;
}
catch (const std::exception& e)
@@ -679,12 +680,14 @@ namespace tools
}
// populate response with tx hashes
- for (auto & ptx : ptx_vector)
+ for (const auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
+ for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
+ res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key);
}
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
ptx_amount = 0;
@@ -700,6 +703,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
+ }
}
return true;
@@ -730,7 +740,7 @@ namespace tools
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
- for (auto & ptx : ptx_vector)
+ for (const auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
@@ -744,6 +754,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
+ }
}
return true;
@@ -788,7 +805,7 @@ namespace tools
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
- for (auto & ptx : ptx_vector)
+ for (const auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
@@ -801,6 +818,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
+ }
}
return true;
@@ -884,6 +908,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
return true;
}
@@ -908,6 +939,47 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.hex, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+
+ std::stringstream ss;
+ ss << blob;
+ binary_archive<false> ba(ss);
+
+ tools::wallet2::pending_tx ptx;
+ bool r = ::serialization::serialize(ba, ptx);
+ if (!r)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_TX_METADATA;
+ er.message = "Failed to parse tx metadata.";
+ return false;
+ }
+
+ try
+ {
+ m_wallet->commit_tx(ptx);
+ }
+ catch(const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
+ er.message = "Failed to commit tx.";
+ return false;
+ }
+
+ res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -1396,6 +1468,208 @@ namespace tools
res.value = m_wallet->get_attribute(req.key);
return true;
}
+ bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ if (!m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_TXKEY;
+ er.message = "No tx secret key is stored for this tx";
+ return false;
+ }
+
+ std::ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ res.tx_key = oss.str();
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ std::string tx_key_str = req.tx_key;
+ crypto::secret_key tx_key;
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ std::vector<crypto::secret_key> additional_tx_keys;
+ while (!tx_key_str.empty())
+ {
+ additional_tx_keys.resize(additional_tx_keys.size() + 1);
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+
+ try
+ {
+ m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, res.received, res.in_pool, res.confirmations);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+
+ try
+ {
+ res.signature = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, req.message);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+
+ try
+ {
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+ res.good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, req.message, req.signature, res.received, res.in_pool, res.confirmations);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ try
+ {
+ res.signature = m_wallet->get_spend_proof(txid, req.message);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ try
+ {
+ res.good = m_wallet->check_spend_proof(txid, req.message, req.signature);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er)
{
@@ -1904,7 +2178,7 @@ namespace tools
command_line::add_arg(desc, arg_password);
po::store(po::parse_command_line(argc, argv, desc), vm2);
}
- std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, password_prompter).first;
+ std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, nullptr).first;
if (!wal)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1978,7 +2252,7 @@ namespace tools
}
std::unique_ptr<tools::wallet2> wal = nullptr;
try {
- wal = tools::wallet2::make_from_file(vm2, wallet_file, password_prompter).first;
+ wal = tools::wallet2::make_from_file(vm2, wallet_file, nullptr).first;
}
catch (const std::exception& e)
{
@@ -2041,6 +2315,16 @@ namespace tools
er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD;
er.message = "Invalid password.";
}
+ catch (const error::account_index_outofbound& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND;
+ er.message = e.what();
+ }
+ catch (const error::address_index_outofbound& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND;
+ er.message = e.what();
+ }
catch (const std::exception& e)
{
er.code = default_error_code;
@@ -2070,7 +2354,7 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_file);
command_line::add_arg(desc_params, arg_from_json);
command_line::add_arg(desc_params, arg_wallet_dir);
-
+ command_line::add_arg(desc_params, arg_prompt_for_password);
const auto vm = wallet_args::main(
argc, argv,
@@ -2092,6 +2376,8 @@ int main(int argc, char** argv) {
const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file);
const auto from_json = command_line::get_arg(*vm, arg_from_json);
const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
+ const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password);
+ const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
if(!wallet_file.empty() && !from_json.empty())
{
@@ -2114,13 +2400,13 @@ int main(int argc, char** argv) {
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
if(!wallet_file.empty())
{
- wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompter).first;
+ wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompt).first;
}
else
{
try
{
- wal = tools::wallet2::make_from_json(*vm, from_json, password_prompter);
+ wal = tools::wallet2::make_from_json(*vm, from_json, password_prompt);
}
catch (const std::exception &e)
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 36a853a1a..9455c4769 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -80,6 +80,7 @@ namespace tools
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL)
MAP_JON_RPC_WE("sweep_single", on_sweep_single, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE)
+ MAP_JON_RPC_WE("relay_tx", on_relay_tx, wallet_rpc::COMMAND_RPC_RELAY_TX)
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS)
@@ -93,6 +94,12 @@ namespace tools
MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES)
MAP_JON_RPC_WE("set_attribute", on_set_attribute, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE)
MAP_JON_RPC_WE("get_attribute", on_get_attribute, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE)
+ MAP_JON_RPC_WE("get_tx_key", on_get_tx_key, wallet_rpc::COMMAND_RPC_GET_TX_KEY)
+ MAP_JON_RPC_WE("check_tx_key", on_check_tx_key, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY)
+ MAP_JON_RPC_WE("get_tx_proof", on_get_tx_proof, wallet_rpc::COMMAND_RPC_GET_TX_PROOF)
+ MAP_JON_RPC_WE("check_tx_proof", on_check_tx_proof, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF)
+ MAP_JON_RPC_WE("get_spend_proof", on_get_spend_proof, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF)
+ MAP_JON_RPC_WE("check_spend_proof", on_check_spend_proof, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF)
MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS)
MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID)
MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN)
@@ -128,6 +135,7 @@ namespace tools
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);
bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er);
+ bool on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er);
bool on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er);
@@ -140,6 +148,12 @@ namespace tools
bool on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er);
bool on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er);
bool on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er);
+ bool on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er);
+ bool on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er);
+ bool on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er);
bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er);
bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er);
bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 9bcc5138a..e084d9e6d 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -291,6 +291,7 @@ namespace wallet_rpc
bool get_tx_key;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -303,6 +304,7 @@ namespace wallet_rpc
KV_SERIALIZE(get_tx_key)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -313,6 +315,7 @@ namespace wallet_rpc
std::list<std::string> amount_keys;
uint64_t fee;
std::string tx_blob;
+ std::string tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
@@ -320,6 +323,7 @@ namespace wallet_rpc
KV_SERIALIZE(amount_keys)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
+ KV_SERIALIZE(tx_metadata)
END_KV_SERIALIZE_MAP()
};
};
@@ -338,6 +342,7 @@ namespace wallet_rpc
bool get_tx_keys;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -350,6 +355,7 @@ namespace wallet_rpc
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -369,6 +375,7 @@ namespace wallet_rpc
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
+ std::list<std::string> tx_metadata_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
@@ -376,6 +383,7 @@ namespace wallet_rpc
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
+ KV_SERIALIZE(tx_metadata_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -387,11 +395,13 @@ namespace wallet_rpc
bool get_tx_keys;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -410,12 +420,14 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
+ std::list<std::string> tx_metadata_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
+ KV_SERIALIZE(tx_metadata_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -435,6 +447,7 @@ namespace wallet_rpc
uint64_t below_amount;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
@@ -448,6 +461,7 @@ namespace wallet_rpc
KV_SERIALIZE(below_amount)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -466,12 +480,14 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
+ std::list<std::string> tx_metadata_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
+ KV_SERIALIZE(tx_metadata_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -489,6 +505,7 @@ namespace wallet_rpc
std::string key_image;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
@@ -500,6 +517,36 @@ namespace wallet_rpc
KV_SERIALIZE(key_image)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string tx_hash;
+ std::string tx_key;
+ uint64_t fee;
+ std::string tx_blob;
+ std::string tx_metadata;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_hash)
+ KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(tx_blob)
+ KV_SERIALIZE(tx_metadata)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_RELAY_TX
+ {
+ struct request
+ {
+ std::string hex;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(hex)
END_KV_SERIALIZE_MAP()
};
@@ -828,6 +875,114 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_TX_KEY
+ {
+ struct request
+ {
+ std::string txid;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string tx_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_TX_KEY
+ {
+ struct request
+ {
+ std::string txid;
+ std::string tx_key;
+ std::string address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(address)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(received)
+ KV_SERIALIZE(in_pool)
+ KV_SERIALIZE(confirmations)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_GET_TX_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string address;
+ std::string message;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(message)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_TX_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string address;
+ std::string message;
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(message)
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ bool good;
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(good)
+ KV_SERIALIZE(received)
+ KV_SERIALIZE(in_pool)
+ KV_SERIALIZE(confirmations)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct transfer_entry
{
std::string txid;
@@ -859,6 +1014,54 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
+ struct COMMAND_RPC_GET_SPEND_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string message;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(message)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_SPEND_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string message;
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(message)
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ bool good;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(good)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_TRANSFERS
{
struct request
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index e74e9110b..c3f3e20d1 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -54,3 +54,7 @@
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS -21
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD -22
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR -23
+#define WALLET_RPC_ERROR_CODE_NO_TXKEY -24
+#define WALLET_RPC_ERROR_CODE_WRONG_KEY -25
+#define WALLET_RPC_ERROR_CODE_BAD_HEX -26
+#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA -27