aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/address_book.cpp22
-rw-r--r--src/wallet/api/address_book.h2
-rw-r--r--src/wallet/api/wallet.cpp76
-rw-r--r--src/wallet/api/wallet.h7
-rw-r--r--src/wallet/api/wallet_manager.cpp74
-rw-r--r--src/wallet/api/wallet_manager.h7
-rw-r--r--src/wallet/wallet2.cpp162
-rw-r--r--src/wallet/wallet2.h93
-rw-r--r--src/wallet/wallet2_api.h39
-rw-r--r--src/wallet/wallet_rpc_server.cpp215
-rw-r--r--src/wallet/wallet_rpc_server.h8
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h73
12 files changed, 551 insertions, 227 deletions
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index bbf96c81a..b878491ce 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -103,6 +103,28 @@ bool AddressBookImpl::deleteRow(std::size_t rowId)
return r;
}
+int AddressBookImpl::lookupPaymentID(const std::string &payment_id) const
+{
+ // turn short ones into long ones for comparison
+ const std::string long_payment_id = payment_id + std::string(64 - payment_id.size(), '0');
+
+ int idx = -1;
+ for (const auto &row: m_rows) {
+ ++idx;
+ // this does short/short and long/long
+ if (payment_id == row->getPaymentId())
+ return idx;
+ // short/long
+ if (long_payment_id == row->getPaymentId())
+ return idx;
+ // one case left: payment_id was long, row's is short
+ const std::string long_row_payment_id = row->getPaymentId() + std::string(64 - row->getPaymentId().size(), '0');
+ if (payment_id == long_row_payment_id)
+ return idx;
+ }
+ return -1;
+}
+
void AddressBookImpl::clearRows() {
for (auto r : m_rows) {
delete r;
diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h
index 7f30e4387..33d06a078 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -51,6 +51,8 @@ public:
// Error codes. See AddressBook:ErrorCode enum in wallet2_api.h
std::string errorString() const {return m_errorString;}
int errorCode() const {return m_errorCode;}
+
+ int lookupPaymentID(const std::string &payment_id) const;
private:
void clearRows();
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 3a4493ec3..0fb832d65 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -277,6 +277,46 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co
return true;
}
+bool WalletImpl::createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const
+{
+ clearStatus();
+ std::unique_ptr<tools::wallet2> view_wallet(new tools::wallet2(m_wallet->testnet()));
+
+ // Store same refresh height as original wallet
+ view_wallet->set_refresh_from_block_height(m_wallet->get_refresh_from_block_height());
+
+ bool keys_file_exists;
+ bool wallet_file_exists;
+ tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
+ LOG_PRINT_L3("wallet_path: " << path << "");
+ LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha
+ << " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha);
+
+ // add logic to error out if new wallet requested but named wallet file exists
+ if (keys_file_exists || wallet_file_exists) {
+ m_errorString = "attempting to generate view only wallet, but specified file(s) exist. Exiting to not risk overwriting.";
+ LOG_ERROR(m_errorString);
+ m_status = Status_Error;
+ return false;
+ }
+ // TODO: validate language
+ view_wallet->set_seed_language(language);
+
+ const crypto::secret_key viewkey = m_wallet->get_account().get_keys().m_view_secret_key;
+ const cryptonote::account_public_address address = m_wallet->get_account().get_keys().m_account_address;
+
+ try {
+ view_wallet->generate(path, password, address, viewkey);
+ m_status = Status_Ok;
+ } catch (const std::exception &e) {
+ LOG_ERROR("Error creating view only wallet: " << e.what());
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+ return true;
+}
+
bool WalletImpl::open(const std::string &path, const std::string &password)
{
clearStatus();
@@ -415,6 +455,11 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id) const
return m_wallet->get_account().get_public_integrated_address_str(pid, m_wallet->testnet());
}
+std::string WalletImpl::privateViewKey() const
+{
+ return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
+}
+
std::string WalletImpl::path() const
{
return m_wallet->path();
@@ -966,7 +1011,12 @@ bool WalletImpl::trustedDaemon() const
return m_trustedDaemon;
}
-void WalletImpl::clearStatus()
+bool WalletImpl::watchOnly() const
+{
+ return m_wallet->watch_only();
+}
+
+void WalletImpl::clearStatus() const
{
m_status = Status_Ok;
m_errorString.clear();
@@ -1010,9 +1060,6 @@ void WalletImpl::doRefresh()
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced.
if (daemonSynced()) {
- // Use fast refresh for new wallets
- if (isNewWallet())
- m_wallet->set_refresh_from_block_height(daemonBlockChainHeight());
m_wallet->refresh();
if (!m_synchronized) {
m_synchronized = true;
@@ -1023,7 +1070,9 @@ void WalletImpl::doRefresh()
if (m_history->count() == 0) {
m_history->refresh();
}
- }
+ } else {
+ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced");
+ }
} catch (const std::exception &e) {
m_status = Status_Error;
m_errorString = e.what();
@@ -1070,8 +1119,9 @@ bool WalletImpl::isNewWallet() const
// in case wallet created without daemon connection, closed and opened again,
// it's the same case as if it created from scratch, i.e. we need "fast sync"
// with the daemon (pull hashes instead of pull blocks).
- // If wallet cache is rebuilt, creation height stored in .keys is used.
- return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_rebuildWalletCache);
+ // If wallet cache is rebuilt, creation height stored in .keys is used.
+ // Watch only wallet is a copy of an existing wallet.
+ return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_rebuildWalletCache) && !watchOnly();
}
void WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit)
@@ -1079,16 +1129,26 @@ void WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction
m_wallet->init(daemon_address, upper_transaction_size_limit);
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
- if (isNewWallet()) {
+ // If daemon isn't synced a calculated block height will be used instead
+ if (isNewWallet() && daemonSynced()) {
+ LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight());
m_wallet->set_refresh_from_block_height(daemonBlockChainHeight());
}
+ if (m_rebuildWalletCache)
+ LOG_PRINT_L2(__FUNCTION__ << ": Rebuilding wallet cache, fast refresh until block " << m_wallet->get_refresh_from_block_height());
+
if (Utils::isAddressLocal(daemon_address)) {
this->setTrustedDaemon(true);
}
}
+bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error)
+{
+ return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error);
+}
+
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index d245a78d0..41b3b22f3 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -53,6 +53,8 @@ public:
~WalletImpl();
bool create(const std::string &path, const std::string &password,
const std::string &language);
+ bool createWatchOnly(const std::string &path, const std::string &password,
+ const std::string &language) const;
bool open(const std::string &path, const std::string &password);
bool recover(const std::string &path, const std::string &seed);
bool close();
@@ -65,6 +67,7 @@ public:
bool setPassword(const std::string &password);
std::string address() const;
std::string integratedAddress(const std::string &payment_id) const;
+ std::string privateViewKey() const;
std::string path() const;
bool store(const std::string &path);
std::string filename() const;
@@ -88,6 +91,7 @@ public:
int autoRefreshInterval() const;
void setRefreshFromBlockHeight(uint64_t refresh_from_block_height);
void setRecoveringFromSeed(bool recoveringFromSeed);
+ bool watchOnly() const;
@@ -109,9 +113,10 @@ public:
virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const;
virtual void startRefresh();
virtual void pauseRefresh();
+ virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
private:
- void clearStatus();
+ void clearStatus() const;
void refreshThreadFunc();
void doRefresh();
bool daemonSynced() const;
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 4ee5ab8df..48faa3183 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -32,6 +32,7 @@
#include "wallet_manager.h"
#include "wallet.h"
#include "common_defines.h"
+#include "common/dns_utils.h"
#include "net/http_client.h"
#include <boost/filesystem.hpp>
@@ -137,7 +138,7 @@ void WalletManagerImpl::setDaemonAddress(const std::string &address)
m_daemonAddress = address;
}
-bool WalletManagerImpl::connected(uint32_t *version = NULL) const
+bool WalletManagerImpl::connected(uint32_t *version) const
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t);
epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
@@ -345,16 +346,83 @@ double WalletManagerImpl::miningHashRate() const
cryptonote::COMMAND_RPC_MINING_STATUS::response mres;
epee::net_utils::http::http_simple_client http_client;
- if (!epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/getinfo", mreq, mres, http_client))
+ if (!epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/mining_status", mreq, mres, http_client))
return 0.0;
if (!mres.active)
return 0.0;
return mres.speed;
}
+void WalletManagerImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const
+{
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_HARD_FORK_INFO::request> req_t = AUTO_VAL_INIT(req_t);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_HARD_FORK_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
+
+ version = 0;
+ earliest_height = 0;
+
+ epee::net_utils::http::http_simple_client http_client;
+ req_t.jsonrpc = "2.0";
+ req_t.id = epee::serialization::storage_entry(0);
+ req_t.method = "hard_fork_info";
+ req_t.params.version = 0;
+ bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/json_rpc", req_t, resp_t, http_client);
+ if (!r || resp_t.result.status != CORE_RPC_STATUS_OK)
+ return;
+ version = resp_t.result.version;
+ earliest_height = resp_t.result.earliest_height;
+}
+
+uint64_t WalletManagerImpl::blockTarget() const
+{
+ cryptonote::COMMAND_RPC_GET_INFO::request ireq;
+ cryptonote::COMMAND_RPC_GET_INFO::response ires;
+
+ epee::net_utils::http::http_simple_client http_client;
+ if (!epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/getinfo", ireq, ires, http_client))
+ return 0;
+ return ires.target;
+}
+
+bool WalletManagerImpl::isMining() const
+{
+ cryptonote::COMMAND_RPC_MINING_STATUS::request mreq;
+ cryptonote::COMMAND_RPC_MINING_STATUS::response mres;
+
+ epee::net_utils::http::http_simple_client http_client;
+ if (!epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/mining_status", mreq, mres, http_client))
+ return false;
+ return mres.active;
+}
+
+bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads)
+{
+ cryptonote::COMMAND_RPC_START_MINING::request mreq;
+ cryptonote::COMMAND_RPC_START_MINING::response mres;
+
+ mreq.miner_address = address;
+ mreq.threads_count = threads;
+
+ epee::net_utils::http::http_simple_client http_client;
+ if (!epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/start_mining", mreq, mres, http_client))
+ return false;
+ return mres.status == CORE_RPC_STATUS_OK;
+}
+
+bool WalletManagerImpl::stopMining()
+{
+ cryptonote::COMMAND_RPC_STOP_MINING::request mreq;
+ cryptonote::COMMAND_RPC_STOP_MINING::response mres;
+
+ epee::net_utils::http::http_simple_client http_client;
+ if (!epee::net_utils::invoke_http_json_remote_command2(m_daemonAddress + "/stop_mining", mreq, mres, http_client))
+ return false;
+ return mres.status == CORE_RPC_STATUS_OK;
+}
+
std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool &dnssec_valid) const
{
- std::vector<std::string> addresses = tools::wallet2::addresses_from_url(address, dnssec_valid);
+ std::vector<std::string> addresses = tools::dns_utils::addresses_from_url(address, dnssec_valid);
if (addresses.empty())
return "";
return addresses.front();
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 214afc3fa..ca9570254 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -46,12 +46,17 @@ public:
std::vector<std::string> findWallets(const std::string &path);
std::string errorString() const;
void setDaemonAddress(const std::string &address);
- bool connected(uint32_t *version) const;
+ 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;
double miningHashRate() const;
+ void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const;
+ uint64_t blockTarget() const;
+ bool isMining() const;
+ bool startMining(const std::string &address, uint32_t threads = 1);
+ bool stopMining();
std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const;
private:
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9b2d04a07..639c2c5fc 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -51,7 +51,6 @@ using namespace epee;
#include "cryptonote_protocol/blobdatatype.h"
#include "mnemonics/electrum-words.h"
#include "common/i18n.h"
-#include "common/dns_utils.h"
#include "common/util.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
@@ -77,8 +76,8 @@ using namespace cryptonote;
// arbitrary, used to generate different hashes from the same input
#define CHACHA8_KEY_TAIL 0x8c
-#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\002"
-#define SIGNED_TX_PREFIX "Monero signed tx set\002"
+#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003"
+#define SIGNED_TX_PREFIX "Monero signed tx set\003"
#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone
#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone
@@ -1613,6 +1612,9 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// and then fall through to regular refresh processing
}
+ // If stop() is called during fast refresh we don't need to continue
+ if(!m_run.load(std::memory_order_relaxed))
+ return;
pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
// always reset start_height to 0 to force short_chain_ history to be used on
// subsequent pulls in this refresh.
@@ -1632,7 +1634,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
blocks_fetched += added_blocks;
pull_thread.join();
- if(!added_blocks)
+ if(blocks_start_height == next_blocks_start_height)
break;
// switch to the new blocks from the daemon
@@ -1668,7 +1670,9 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
try
{
- update_pool_state();
+ // If stop() is called we don't need to check pending transactions
+ if(m_run.load(std::memory_order_relaxed))
+ update_pool_state();
}
catch (...)
{
@@ -1804,6 +1808,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p
value2.SetInt(m_always_confirm_transfers ? 1 :0);
json.AddMember("always_confirm_transfers", value2, json.GetAllocator());
+ value2.SetInt(m_print_ring_members ? 1 :0);
+ json.AddMember("print_ring_members", value2, json.GetAllocator());
+
value2.SetInt(m_store_tx_info ? 1 :0);
json.AddMember("store_tx_info", value2, json.GetAllocator());
@@ -1886,6 +1893,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
is_old_file_format = true;
m_watch_only = false;
m_always_confirm_transfers = false;
+ m_print_ring_members = false;
m_default_mixin = 0;
m_default_priority = 0;
m_auto_refresh = true;
@@ -1916,6 +1924,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_watch_only = field_watch_only;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true);
m_always_confirm_transfers = field_always_confirm_transfers;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true);
+ m_print_ring_members = field_print_ring_members;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
@@ -2139,7 +2149,7 @@ void wallet2::rewrite(const std::string& wallet_name, const std::string& passwor
prepare_file_names(wallet_name);
boost::system::error_code ignored_ec;
THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file);
- bool r = store_keys(m_keys_file, password, false);
+ bool r = store_keys(m_keys_file, password, m_watch_only);
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
}
/*!
@@ -2854,74 +2864,7 @@ std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
return retVal;
}
} // anonymous namespace
-
-/**
- * @brief gets a monero address from the TXT record of a DNS entry
- *
- * gets the monero address from the TXT record of the DNS entry associated
- * with <url>. If this lookup fails, or the TXT record does not contain an
- * XMR address in the correct format, returns an empty string. <dnssec_valid>
- * will be set true or false according to whether or not the DNS query passes
- * DNSSEC validation.
- *
- * @param url the url to look up
- * @param dnssec_valid return-by-reference for DNSSEC status of query
- *
- * @return a monero address (as a string) or an empty string
- */
-std::vector<std::string> wallet2::addresses_from_url(const std::string& url, bool& dnssec_valid)
-{
- std::vector<std::string> addresses;
- // get txt records
- bool dnssec_available, dnssec_isvalid;
- std::string oa_addr = tools::DNSResolver::instance().get_dns_format_from_oa_address(url);
- auto records = tools::DNSResolver::instance().get_txt_record(oa_addr, dnssec_available, dnssec_isvalid);
-
- // TODO: update this to allow for conveying that dnssec was not available
- if (dnssec_available && dnssec_isvalid)
- {
- dnssec_valid = true;
- }
- else dnssec_valid = false;
-
- // for each txt record, try to find a monero address in it.
- for (auto& rec : records)
- {
- std::string addr = address_from_txt_record(rec);
- if (addr.size())
- {
- addresses.push_back(addr);
- }
- }
-
- return addresses;
-}
-
//----------------------------------------------------------------------------------------------------
-// TODO: parse the string in a less stupid way, probably with regex
-std::string wallet2::address_from_txt_record(const std::string& s)
-{
- // make sure the txt record has "oa1:xmr" and find it
- auto pos = s.find("oa1:xmr");
-
- // search from there to find "recipient_address="
- pos = s.find("recipient_address=", pos);
-
- pos += 18; // move past "recipient_address="
-
- // find the next semicolon
- auto pos2 = s.find(";", pos);
- if (pos2 != std::string::npos)
- {
- // length of address == 95, we can at least validate that much here
- if (pos2 - pos == 95)
- {
- return s.substr(pos, 95);
- }
- }
- return std::string();
-}
-
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
@@ -3059,14 +3002,19 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
}
txs.transfers = m_transfers;
- std::string s = obj_to_json_str(txs);
- if (s.empty())
- return false;
- LOG_PRINT_L2("Saving unsigned tx data: " << s);
- // save as binary as there's no implementation of loading a json_archive
- if (!::serialization::dump_binary(txs, s))
+ // save as binary
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << txs;
+ }
+ catch (...)
+ {
return false;
- return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + s);
+ }
+ LOG_PRINT_L2("Saving unsigned tx data: " << oss.str());
+ return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + oss.str());
}
//----------------------------------------------------------------------------------------------------
bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector<wallet2::pending_tx> &txs, std::function<bool(const unsigned_tx_set&)> accept_func)
@@ -3091,7 +3039,14 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
return false;
}
unsigned_tx_set exported_txs;
- if (!::serialization::parse_binary(std::string(s.c_str() + magiclen, s.size() - magiclen), exported_txs))
+ s = s.substr(magiclen);
+ try
+ {
+ std::istringstream iss(s);
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> exported_txs;
+ }
+ catch (...)
{
LOG_PRINT_L0("Failed to parse data from " << unsigned_filename);
return false;
@@ -3164,14 +3119,19 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
signed_txes.key_images[i] = m_transfers[i].m_key_image;
}
- s = obj_to_json_str(signed_txes);
- if (s.empty())
- return false;
- LOG_PRINT_L2("Saving signed tx data: " << s);
- // save as binary as there's no implementation of loading a json_archive
- if (!::serialization::dump_binary(signed_txes, s))
+ // save as binary
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << signed_txes;
+ }
+ catch(...)
+ {
return false;
- return epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + s);
+ }
+ LOG_PRINT_L2("Saving signed tx data: " << oss.str());
+ return epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + oss.str());
}
//----------------------------------------------------------------------------------------------------
bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func)
@@ -3197,7 +3157,14 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
LOG_PRINT_L0("Bad magic from " << signed_filename);
return false;
}
- if (!::serialization::parse_binary(std::string(s.c_str() + magiclen, s.size() - magiclen), signed_txs))
+ s = s.substr(magiclen);
+ try
+ {
+ std::istringstream iss(s);
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> signed_txs;
+ }
+ catch (...)
{
LOG_PRINT_L0("Failed to parse data from " << signed_filename);
return false;
@@ -3562,6 +3529,23 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si
outs.back().reserve(fake_outputs_count + 1);
const rct::key mask = td.is_rct() ? rct::commit(td.amount(), td.m_mask) : rct::zeroCommit(td.amount());
+ // make sure the real outputs we asked for are really included, along
+ // with the correct key and mask: this guards against an active attack
+ // where the node sends dummy data for all outputs, and we then send
+ // the real one, which the node can then tell from the fake outputs,
+ // as it has different data than the dummy data it had sent earlier
+ bool real_out_found = false;
+ for (size_t n = 0; n < requested_outputs_count; ++n)
+ {
+ size_t i = base + n;
+ if (req.outputs[i].index == td.m_global_output_index)
+ if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
+ if (daemon_resp.outs[i].mask == mask)
+ real_out_found = true;
+ }
+ THROW_WALLET_EXCEPTION_IF(!real_out_found, error::wallet_internal_error,
+ "Daemon response did not include the requested real output");
+
// pick real out first (it will be sorted when done)
outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index b6dc06ef0..2d4896b55 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -58,6 +58,8 @@
#include <iostream>
#define WALLET_RCP_CONNECTION_TIMEOUT 200000
+class Serialization_portability_wallet_Test;
+
namespace tools
{
class i_wallet2_callback
@@ -86,6 +88,7 @@ namespace tools
class wallet2
{
+ friend class ::Serialization_portability_wallet_Test;
public:
enum RefreshType {
RefreshFull,
@@ -95,7 +98,7 @@ namespace tools
};
private:
- wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true) {}
+ wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true) {}
public:
static const char* tr(const char* str);// { return i18n_translate(str, "cryptonote::simple_wallet"); }
@@ -116,7 +119,7 @@ namespace tools
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm);
- wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_restricted(restricted), is_old_file_format(false) {}
+ wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_restricted(restricted), is_old_file_format(false) {}
struct transfer_details
{
uint64_t m_block_height;
@@ -201,17 +204,6 @@ namespace tools
uint64_t unlock_time;
bool use_rct;
std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change
-
- BEGIN_SERIALIZE_OBJECT()
- FIELD(sources)
- FIELD(change_dts)
- FIELD(splitted_dsts)
- FIELD(selected_transfers)
- FIELD(extra)
- VARINT_FIELD(unlock_time)
- FIELD(use_rct)
- FIELD(dests)
- END_SERIALIZE()
};
typedef std::vector<transfer_details> transfer_container;
@@ -232,39 +224,18 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dests;
tx_construction_data construction_data;
-
- BEGIN_SERIALIZE_OBJECT()
- FIELD(tx)
- VARINT_FIELD(dust)
- VARINT_FIELD(fee)
- FIELD(dust_added_to_fee)
- FIELD(change_dts)
- FIELD(selected_transfers)
- FIELD(key_images)
- FIELD(tx_key)
- FIELD(dests)
- FIELD(construction_data)
- END_SERIALIZE()
};
struct unsigned_tx_set
{
std::vector<tx_construction_data> txes;
wallet2::transfer_container transfers;
- BEGIN_SERIALIZE_OBJECT()
- FIELD(txes)
- FIELD(transfers)
- END_SERIALIZE()
};
struct signed_tx_set
{
std::vector<pending_tx> ptx;
std::vector<crypto::key_image> key_images;
- BEGIN_SERIALIZE_OBJECT()
- FIELD(ptx)
- FIELD(key_images)
- END_SERIALIZE()
};
struct keys_file_data
@@ -354,6 +325,7 @@ namespace tools
const cryptonote::account_base& get_account()const{return m_account;}
void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;}
+ uint64_t get_refresh_from_block_height() const {return m_refresh_from_block_height;}
// upper_transaction_size_limit as defined below is set to
// approximately 125% of the fixed minimum allowable penalty
@@ -503,12 +475,10 @@ namespace tools
static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id);
static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id);
- static std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid);
-
- static std::string address_from_txt_record(const std::string& s);
-
bool always_confirm_transfers() const { return m_always_confirm_transfers; }
void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; }
+ bool print_ring_members() const { return m_print_ring_members; }
+ void print_ring_members(bool value) { m_print_ring_members = value; }
bool store_tx_info() const { return m_store_tx_info; }
void store_tx_info(bool store) { m_store_tx_info = store; }
uint32_t default_mixin() const { return m_default_mixin; }
@@ -658,6 +628,7 @@ namespace tools
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
bool m_watch_only; /*!< no spend key */
bool m_always_confirm_transfers;
+ bool m_print_ring_members;
bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */
uint32_t m_default_mixin;
uint32_t m_default_priority;
@@ -673,6 +644,10 @@ BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 3)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 16)
+BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
+BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
+BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 0)
+BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 0)
namespace boost
{
@@ -870,6 +845,48 @@ namespace boost
a & x.m_payment_id;
a & x.m_description;
}
+
+ template <class Archive>
+ inline void serialize(Archive &a, tools::wallet2::unsigned_tx_set &x, const boost::serialization::version_type ver)
+ {
+ a & x.txes;
+ a & x.transfers;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, tools::wallet2::signed_tx_set &x, const boost::serialization::version_type ver)
+ {
+ a & x.ptx;
+ a & x.key_images;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, tools::wallet2::tx_construction_data &x, const boost::serialization::version_type ver)
+ {
+ a & x.sources;
+ a & x.change_dts;
+ a & x.splitted_dsts;
+ a & x.selected_transfers;
+ a & x.extra;
+ a & x.unlock_time;
+ a & x.use_rct;
+ a & x.dests;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, tools::wallet2::pending_tx &x, const boost::serialization::version_type ver)
+ {
+ a & x.tx;
+ a & x.dust;
+ a & x.fee;
+ a & x.dust_added_to_fee;
+ a & x.change_dts;
+ a & x.selected_transfers;
+ a & x.key_images;
+ a & x.tx_key;
+ a & x.dests;
+ a & x.construction_data;
+ }
}
}
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 2e1d95b58..d4c0388a6 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -175,6 +175,7 @@ struct AddressBook
virtual void refresh() = 0;
virtual std::string errorString() const = 0;
virtual int errorCode() const = 0;
+ virtual int lookupPaymentID(const std::string &payment_id) const = 0;
};
struct WalletListener
@@ -255,6 +256,12 @@ struct Wallet
*/
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
+ /*!
+ * \brief privateViewKey - returns private view key
+ * \return - private view key
+ */
+ virtual std::string privateViewKey() const = 0;
+
/*!
* \brief store - stores wallet to file.
* \param path - main filename to store wallet to. additionally stores address file and keys file.
@@ -294,6 +301,15 @@ struct Wallet
virtual void initAsync(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0;
/*!
+ * \brief createWatchOnly - Creates a watch only wallet
+ * \param path - where to store the wallet
+ * \param password
+ * \param language
+ * \return - true if created successfully
+ */
+ virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
+
+ /*!
* \brief setRefreshFromBlockHeight - start refresh from block height on recover
*
* \param refresh_from_block_height - blockchain start height
@@ -323,6 +339,12 @@ struct Wallet
virtual uint64_t balance() const = 0;
virtual uint64_t unlockedBalance() const = 0;
+ /**
+ * @brief watchOnly - checks if wallet is watch only
+ * @return - true if watch only
+ */
+ virtual bool watchOnly() const = 0;
+
/**
* @brief blockChainHeight - returns current blockchain height
* @return
@@ -468,6 +490,8 @@ struct Wallet
* \return true if the signature verified, false otherwise
*/
virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
+
+ virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
};
/**
@@ -562,6 +586,21 @@ struct WalletManager
//! returns current mining hash rate (0 if not mining)
virtual double miningHashRate() const = 0;
+ //! returns current hard fork info
+ virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
+
+ //! returns current block target
+ virtual uint64_t blockTarget() const = 0;
+
+ //! returns true iff mining
+ virtual bool isMining() const = 0;
+
+ //! starts mining with the set number of threads
+ virtual bool startMining(const std::string &address, uint32_t threads = 1) = 0;
+
+ //! stops mining
+ virtual bool stopMining() = 0;
+
//! resolves an OpenAlias address to a monero address
virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0;
};
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 2a259029d..d61b11f8a 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -125,7 +125,7 @@ namespace tools
}
}
- epee::net_utils::http::http_auth::login login{};
+ epee::net_utils::http::login login{};
const bool disable_auth = command_line::get_arg(vm, arg_disable_rpc_login);
const std::string user_pass = command_line::get_arg(vm, arg_rpc_login);
@@ -201,6 +201,73 @@ namespace tools
);
}
//------------------------------------------------------------------------------------------------------------------------------
+ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
+ {
+ entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
+ entry.payment_id = string_tools::pod_to_hex(payment_id);
+ if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
+ entry.payment_id = entry.payment_id.substr(0,16);
+ entry.height = pd.m_block_height;
+ entry.timestamp = pd.m_timestamp;
+ entry.amount = pd.m_amount;
+ entry.fee = 0; // TODO
+ entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ entry.type = "in";
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
+ {
+ entry.txid = string_tools::pod_to_hex(txid);
+ entry.payment_id = string_tools::pod_to_hex(pd.m_payment_id);
+ if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
+ entry.payment_id = entry.payment_id.substr(0,16);
+ entry.height = pd.m_block_height;
+ entry.timestamp = pd.m_timestamp;
+ entry.fee = pd.m_amount_in - pd.m_amount_out;
+ uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
+ entry.amount = pd.m_amount_in - change - entry.fee;
+ entry.note = m_wallet.get_tx_note(txid);
+
+ for (const auto &d: pd.m_dests) {
+ entry.destinations.push_back(wallet_rpc::transfer_destination());
+ wallet_rpc::transfer_destination &td = entry.destinations.back();
+ td.amount = d.amount;
+ td.address = get_account_address_as_str(m_wallet.testnet(), d.addr);
+ }
+
+ entry.type = "out";
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
+ {
+ bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
+ entry.txid = string_tools::pod_to_hex(txid);
+ entry.payment_id = string_tools::pod_to_hex(pd.m_payment_id);
+ entry.payment_id = string_tools::pod_to_hex(pd.m_payment_id);
+ if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
+ entry.payment_id = entry.payment_id.substr(0,16);
+ entry.height = 0;
+ entry.timestamp = pd.m_timestamp;
+ entry.fee = pd.m_amount_in - pd.m_amount_out;
+ entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
+ entry.note = m_wallet.get_tx_note(txid);
+ entry.type = is_failed ? "failed" : "pending";
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
+ {
+ entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
+ entry.payment_id = string_tools::pod_to_hex(payment_id);
+ if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
+ entry.payment_id = entry.payment_id.substr(0,16);
+ entry.height = 0;
+ entry.timestamp = pd.m_timestamp;
+ entry.amount = pd.m_amount;
+ entry.fee = 0; // TODO
+ entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ entry.type = "pool";
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
{
try
@@ -1014,19 +1081,8 @@ namespace tools
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet.get_payments(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- res.in.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
- wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = res.in.back();
- const tools::wallet2::payment_details &pd = i->second;
-
- entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
- entry.payment_id = string_tools::pod_to_hex(i->first);
- if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
- entry.payment_id = entry.payment_id.substr(0,16);
- entry.height = pd.m_block_height;
- entry.timestamp = pd.m_timestamp;
- entry.amount = pd.m_amount;
- entry.fee = 0; // TODO
- entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ res.in.push_back(wallet_rpc::transfer_entry());
+ fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second);
}
}
@@ -1035,27 +1091,8 @@ namespace tools
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
m_wallet.get_payments_out(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- res.out.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
- wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = res.out.back();
- const tools::wallet2::confirmed_transfer_details &pd = i->second;
-
- entry.txid = string_tools::pod_to_hex(i->first);
- entry.payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
- if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
- entry.payment_id = entry.payment_id.substr(0,16);
- entry.height = pd.m_block_height;
- entry.timestamp = pd.m_timestamp;
- entry.fee = pd.m_amount_in - pd.m_amount_out;
- uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
- entry.amount = pd.m_amount_in - change - entry.fee;
- entry.note = m_wallet.get_tx_note(i->first);
-
- for (const auto &d: pd.m_dests) {
- entry.destinations.push_back(wallet_rpc::transfer_destination());
- wallet_rpc::transfer_destination &td = entry.destinations.back();
- td.amount = d.amount;
- td.address = get_account_address_as_str(m_wallet.testnet(), d.addr);
- }
+ res.out.push_back(wallet_rpc::transfer_entry());
+ fill_transfer_entry(res.out.back(), i->first, i->second);
}
}
@@ -1067,20 +1104,9 @@ namespace tools
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
if (!((req.failed && is_failed) || (!is_failed && req.pending)))
continue;
- std::list<wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry> &entries = is_failed ? res.failed : res.pending;
- entries.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
- wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = entries.back();
-
- entry.txid = string_tools::pod_to_hex(i->first);
- entry.payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
- entry.payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
- if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
- entry.payment_id = entry.payment_id.substr(0,16);
- entry.height = 0;
- entry.timestamp = pd.m_timestamp;
- entry.fee = pd.m_amount_in - pd.m_amount_out;
- entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
- entry.note = m_wallet.get_tx_note(i->first);
+ std::list<wallet_rpc::transfer_entry> &entries = is_failed ? res.failed : res.pending;
+ entries.push_back(wallet_rpc::transfer_entry());
+ fill_transfer_entry(entries.back(), i->first, i->second);
}
}
@@ -1091,25 +1117,90 @@ namespace tools
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet.get_unconfirmed_payments(payments);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- res.pool.push_back(wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry());
- wallet_rpc::COMMAND_RPC_GET_TRANSFERS::entry &entry = res.pool.back();
- const tools::wallet2::payment_details &pd = i->second;
-
- entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
- entry.payment_id = string_tools::pod_to_hex(i->first);
- if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
- entry.payment_id = entry.payment_id.substr(0,16);
- entry.height = 0;
- entry.timestamp = pd.m_timestamp;
- entry.amount = pd.m_amount;
- entry.fee = 0; // TODO
- entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ res.pool.push_back(wallet_rpc::transfer_entry());
+ fill_transfer_entry(res.pool.back(), i->first, i->second);
}
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::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)
+ {
+ if (m_wallet.restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ crypto::hash txid;
+ cryptonote::blobdata txid_blob;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(req.txid, txid_blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "Transaction ID has invalid format";
+ return false;
+ }
+
+ if(sizeof(txid) == txid_blob.size())
+ {
+ txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
+ }
+ else
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "Transaction ID has invalid size: " + req.txid;
+ return false;
+ }
+
+ std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ m_wallet.get_payments(payments, 0);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ if (i->first == txid)
+ {
+ fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
+ return true;
+ }
+ }
+
+ std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
+ m_wallet.get_payments_out(payments_out, 0);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
+ if (i->first == txid)
+ {
+ fill_transfer_entry(res.transfer, i->first, i->second);
+ return true;
+ }
+ }
+
+ std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
+ m_wallet.get_unconfirmed_payments_out(upayments);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
+ if (i->first == txid)
+ {
+ fill_transfer_entry(res.transfer, i->first, i->second);
+ return true;
+ }
+ }
+
+ m_wallet.update_pool_state();
+
+ std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
+ m_wallet.get_unconfirmed_payments(pool_payments);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
+ if (i->second.m_tx_hash == txid)
+ {
+ fill_transfer_entry(res.transfer, i->first, i->second);
+ return true;
+ }
+ }
+
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "Transaction not found.";
+ return false;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er)
{
try
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 92deec043..4ff1b267f 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -78,6 +78,7 @@ namespace tools
MAP_JON_RPC_WE("set_tx_notes", on_set_tx_notes, wallet_rpc::COMMAND_RPC_SET_TX_NOTES)
MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES)
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)
MAP_JON_RPC_WE("verify", on_verify, wallet_rpc::COMMAND_RPC_VERIFY)
MAP_JON_RPC_WE("export_key_images", on_export_key_images, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES)
@@ -107,6 +108,7 @@ namespace tools
bool on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er);
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_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);
bool on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er);
bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er);
@@ -117,6 +119,12 @@ namespace tools
//json rpc v2
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er);
+ // helpers
+ void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
+ void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd);
+ void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd);
+ void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
+
wallet2& m_wallet;
std::string rpc_login_filename;
std::atomic<bool> m_stop;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 50b1613f9..ea0fc685f 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -525,6 +525,31 @@ namespace wallet_rpc
};
};
+ struct transfer_entry
+ {
+ std::string txid;
+ std::string payment_id;
+ uint64_t height;
+ uint64_t timestamp;
+ uint64_t amount;
+ uint64_t fee;
+ std::string note;
+ std::list<transfer_destination> destinations;
+ std::string type;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid);
+ KV_SERIALIZE(payment_id);
+ KV_SERIALIZE(height);
+ KV_SERIALIZE(timestamp);
+ KV_SERIALIZE(amount);
+ KV_SERIALIZE(fee);
+ KV_SERIALIZE(note);
+ KV_SERIALIZE(destinations);
+ KV_SERIALIZE(type);
+ END_KV_SERIALIZE_MAP()
+ };
+
struct COMMAND_RPC_GET_TRANSFERS
{
struct request
@@ -551,43 +576,41 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
- struct entry
+ struct response
+ {
+ std::list<transfer_entry> in;
+ std::list<transfer_entry> out;
+ std::list<transfer_entry> pending;
+ std::list<transfer_entry> failed;
+ std::list<transfer_entry> pool;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(in);
+ KV_SERIALIZE(out);
+ KV_SERIALIZE(pending);
+ KV_SERIALIZE(failed);
+ KV_SERIALIZE(pool);
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_GET_TRANSFER_BY_TXID
+ {
+ struct request
{
std::string txid;
- std::string payment_id;
- uint64_t height;
- uint64_t timestamp;
- uint64_t amount;
- uint64_t fee;
- std::string note;
- std::list<transfer_destination> destinations;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid);
- KV_SERIALIZE(payment_id);
- KV_SERIALIZE(height);
- KV_SERIALIZE(timestamp);
- KV_SERIALIZE(amount);
- KV_SERIALIZE(fee);
- KV_SERIALIZE(note);
- KV_SERIALIZE(destinations);
END_KV_SERIALIZE_MAP()
};
struct response
{
- std::list<entry> in;
- std::list<entry> out;
- std::list<entry> pending;
- std::list<entry> failed;
- std::list<entry> pool;
+ transfer_entry transfer;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(in);
- KV_SERIALIZE(out);
- KV_SERIALIZE(pending);
- KV_SERIALIZE(failed);
- KV_SERIALIZE(pool);
+ KV_SERIALIZE(transfer);
END_KV_SERIALIZE_MAP()
};
};