Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bin/cleanup_test_environment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

# remove docker containers
sudo docker compose -f tests/docker-compose.yml down -v
4 changes: 4 additions & 0 deletions bin/setup_test_environment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

# start docker containers
sudo docker compose -f tests/docker-compose.yml up -d node_1 node_2 xmr_wallet_1 xmr_wallet_2
143 changes: 2 additions & 141 deletions src/cpp/py_monero.cpp

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions src/cpp/wallet/py_monero_wallet.cpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
#include "py_monero_wallet.h"

namespace {
std::unordered_map<const void*, bool> wallet_closed_map;
std::mutex wallet_map_mutex;
}

void set_wallet_closed(const void* wallet, bool value) {
std::lock_guard<std::mutex> lock(wallet_map_mutex);
wallet_closed_map[wallet] = value;
}

bool is_wallet_closed(const void* wallet) {
std::lock_guard<std::mutex> lock(wallet_map_mutex);
auto it = wallet_closed_map.find(wallet);
return it != wallet_closed_map.end() ? it->second : false;
}

void assert_wallet_is_not_closed(const void* wallet) {
if (is_wallet_closed(wallet)) throw std::runtime_error("Wallet is closed");
}

PyMoneroWalletConnectionManagerListener::PyMoneroWalletConnectionManagerListener(monero::monero_wallet* wallet) {
m_wallet = wallet;
}
Expand Down
4 changes: 0 additions & 4 deletions src/cpp/wallet/py_monero_wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
#include "py_monero_wallet_model.h"
#include "daemon/py_monero_daemon.h"

void set_wallet_closed(const void* wallet, bool value);
bool is_wallet_closed(const void* wallet);
void assert_wallet_is_not_closed(const void* wallet);

class PyMoneroWalletConnectionManagerListener : public PyMoneroConnectionManagerListener {
public:
PyMoneroWalletConnectionManagerListener(monero::monero_wallet* wallet);
Expand Down
4 changes: 2 additions & 2 deletions src/cpp/wallet/py_monero_wallet_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1269,8 +1269,8 @@ rapidjson::Value PyMoneroSetSubaddressLabelParams::to_rapidjson_val(rapidjson::D
if (m_label != boost::none) monero_utils::add_json_member("label", m_label.get(), allocator, root, val_str);
if (m_account_index != boost::none && m_subaddress_index != boost::none) {
rapidjson::Value index(rapidjson::kObjectType);
monero_utils::add_json_member("major", m_account_index.get(), allocator, root, val_num);
monero_utils::add_json_member("minor", m_subaddress_index.get(), allocator, root, val_num);
monero_utils::add_json_member("major", m_account_index.get(), allocator, index, val_num);
monero_utils::add_json_member("minor", m_subaddress_index.get(), allocator, index, val_num);
root.AddMember("index", index, allocator);
}
return root;
Expand Down
91 changes: 61 additions & 30 deletions src/cpp/wallet/py_monero_wallet_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::open_wallet(const std::string& name, const
return open_wallet(config);
}

void handle_create_wallet_error(const PyMoneroRpcError& ex, const std::string& path) {
std::string msg = ex.what();
std::transform(msg.begin(), msg.end(), msg.begin(), [](unsigned char c){ return std::tolower(c); });
if (msg.find("already exists") != std::string::npos) throw PyMoneroRpcError(ex.code, std::string("Wallet already exists: ") + path);
if (msg == std::string("electrum-style word list failed verification")) throw PyMoneroRpcError(ex.code, std::string("Invalid mnemonic"));
throw ex;
}

PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet(const std::shared_ptr<PyMoneroWalletConfig> &config) {
if (config == nullptr) throw std::runtime_error("Must specify config to create wallet");
if (config->m_network_type != boost::none) throw std::runtime_error("Cannot specify network type when creating RPC wallet");
Expand Down Expand Up @@ -303,6 +311,16 @@ boost::optional<monero::monero_rpc_connection> PyMoneroWalletRpc::get_daemon_con
return boost::optional<monero::monero_rpc_connection>(*m_daemon_connection);
}

void PyMoneroWalletRpc::set_daemon_connection(const std::string& uri, const std::string& username, const std::string& password, const std::string& proxy_uri) {
if (uri.empty()) {
set_daemon_connection(boost::none);
return;
}
boost::optional<monero_rpc_connection> rpc = monero_rpc_connection(uri, username, password, proxy_uri);
set_daemon_connection(rpc);
}


void PyMoneroWalletRpc::set_daemon_connection(const boost::optional<monero_rpc_connection>& connection, bool is_trusted, const boost::optional<std::shared_ptr<PyMoneroSslOptions>> ssl_options) {
auto params = std::make_shared<PyMoneroSetDaemonParams>();
if (connection == boost::none) {
Expand Down Expand Up @@ -348,8 +366,8 @@ bool PyMoneroWalletRpc::is_connected_to_daemon() const {
return false;
}
catch (const PyMoneroRpcError& e) {
if (e.message == std::string("Failed to connect to daemon")) return false;
return true;
if (e.code == -13) throw; // no wallet file
return e.message.find("Failed to connect to daemon") == std::string::npos;
}
}

Expand Down Expand Up @@ -479,21 +497,26 @@ uint64_t PyMoneroWalletRpc::get_height_by_date(uint16_t year, uint8_t month, uin
monero_sync_result PyMoneroWalletRpc::sync() {
auto params = std::make_shared<PyMoneroRefreshWalletParams>();
boost::lock_guard<boost::recursive_mutex> lock(m_sync_mutex);
PyMoneroJsonRequest request("refresh", params);
poll();
auto response = m_rpc->send_json_request(request);
if (response->m_result == boost::none) throw std::runtime_error("Invalid Monero JSONRPC response");
auto node = response->m_result.get();
monero_sync_result sync_result(0, false);
try {
PyMoneroJsonRequest request("refresh", params);
auto response = m_rpc->send_json_request(request);
poll();
if (response->m_result == boost::none) throw std::runtime_error("Invalid Monero JSONRPC response");
auto node = response->m_result.get();
monero_sync_result sync_result(0, false);

for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it->first;
for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it->first;

if (key == std::string("blocks_fetched")) sync_result.m_num_blocks_fetched = it->second.get_value<uint64_t>();
else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value<bool>();
}
if (key == std::string("blocks_fetched")) sync_result.m_num_blocks_fetched = it->second.get_value<uint64_t>();
else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value<bool>();
}

return sync_result;
return sync_result;
} catch (const PyMoneroRpcError& ex) {
if (ex.message == std::string("no connection to daemon")) throw PyMoneroError("Wallet is not connected to daemon");
throw;
}
}

monero_sync_result PyMoneroWalletRpc::sync(monero_wallet_listener& listener) {
Expand All @@ -507,21 +530,26 @@ monero_sync_result PyMoneroWalletRpc::sync(uint64_t start_height, monero_wallet_
monero_sync_result PyMoneroWalletRpc::sync(uint64_t start_height) {
auto params = std::make_shared<PyMoneroRefreshWalletParams>(start_height);
boost::lock_guard<boost::recursive_mutex> lock(m_sync_mutex);
PyMoneroJsonRequest request("refresh", params);
auto response = m_rpc->send_json_request(request);
poll();
if (response->m_result == boost::none) throw std::runtime_error("Invalid Monero JSONRPC response");
auto node = response->m_result.get();
monero_sync_result sync_result(0, false);
try {
PyMoneroJsonRequest request("refresh", params);
auto response = m_rpc->send_json_request(request);
poll();
if (response->m_result == boost::none) throw std::runtime_error("Invalid Monero JSONRPC response");
auto node = response->m_result.get();
monero_sync_result sync_result(0, false);

for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it->first;
for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it->first;

if (key == std::string("blocks_fetched")) sync_result.m_num_blocks_fetched = it->second.get_value<uint64_t>();
else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value<bool>();
}
if (key == std::string("blocks_fetched")) sync_result.m_num_blocks_fetched = it->second.get_value<uint64_t>();
else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value<bool>();
}

return sync_result;
return sync_result;
} catch (const PyMoneroRpcError& ex) {
if (ex.message == std::string("no connection to daemon")) throw PyMoneroError("Wallet is not connected to daemon");
throw;
}
}

void PyMoneroWalletRpc::start_syncing(uint64_t sync_period_in_ms) {
Expand Down Expand Up @@ -1538,7 +1566,8 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_random(const std::shared_ptr

auto params = std::make_shared<PyMoneroCreateOpenWalletParams>(filename, password, language);
PyMoneroJsonRequest request("create_wallet", params);
m_rpc->send_json_request(request);
try { m_rpc->send_json_request(request); }
catch (const PyMoneroRpcError& ex) { handle_create_wallet_error(ex, filename); }
clear();
m_path = config.m_path.get();
return this;
Expand All @@ -1547,7 +1576,7 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_random(const std::shared_ptr
PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_from_seed(const std::shared_ptr<PyMoneroWalletConfig> &conf) {
auto config = conf->copy();
if (config.m_language == boost::none || config.m_language->empty()) config.m_language = "English";
auto filename = config.m_path;
auto filename = config.m_path.get();
auto password = config.m_password;
auto seed = config.m_seed;
auto seed_offset = config.m_seed_offset;
Expand All @@ -1559,7 +1588,8 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_from_seed(const std::shared_
if (config.m_is_multisig != boost::none) enable_multisig_experimental = config.m_is_multisig.get();
auto params = std::make_shared<PyMoneroCreateOpenWalletParams>(filename, password, seed, seed_offset, restore_height, language, autosave_current, enable_multisig_experimental);
PyMoneroJsonRequest request("restore_deterministic_wallet", params);
m_rpc->send_json_request(request);
try { m_rpc->send_json_request(request); }
catch (const PyMoneroRpcError& ex) { handle_create_wallet_error(ex, filename); }
clear();
m_path = config.m_path.get();
return this;
Expand All @@ -1580,7 +1610,8 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_from_keys(const std::shared_
if (config->m_save_current != boost::none) autosave_current = config->m_save_current.get();
auto params = std::make_shared<PyMoneroCreateOpenWalletParams>(filename, password, address, view_key, spend_key, restore_height, autosave_current);
PyMoneroJsonRequest request("generate_from_keys", params);
m_rpc->send_json_request(request);
try { m_rpc->send_json_request(request); }
catch (const PyMoneroRpcError& ex) { handle_create_wallet_error(ex, filename); }
clear();
m_path = config->m_path.get();
return this;
Expand Down
1 change: 1 addition & 0 deletions src/cpp/wallet/py_monero_wallet_rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class PyMoneroWalletRpc : public PyMoneroWallet {
boost::optional<monero::monero_rpc_connection> get_daemon_connection() const override;
void set_daemon_connection(const boost::optional<monero_rpc_connection>& connection, bool is_trusted, const boost::optional<std::shared_ptr<PyMoneroSslOptions>> ssl_options);
void set_daemon_connection(const boost::optional<monero_rpc_connection>& connection) override;
void set_daemon_connection(const std::string& uri, const std::string& username = "", const std::string& password = "", const std::string& proxy_uri = "") override;
bool is_connected_to_daemon() const override;
monero::monero_version get_version() const override;
std::string get_path() const override;
Expand Down
11 changes: 10 additions & 1 deletion tests/config/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ lite_mode=False
test_notifications=True
network_type=regtest
auto_connect_timeout_ms=3000
mining_address=42U9v3qs5CjZEePHBZHwuSckQXebuZu299NSmVEmQ41YJZQhKcPyujyMSzpDH4VMMVSBo3U3b54JaNvQLwAjqDhKS3rvM3L

[daemon]
rpc_uri=127.0.0.1:18081
Expand Down Expand Up @@ -33,3 +32,13 @@ rpc_zmq_port_start=48083
rpc_zmq_bind_port_start=48083
rpc_zmq_domain=127.0.0.1
sync_period_in_ms=5000

[mining_wallet]
name=mining_wallet
password=supersecretpassword123
address=49cvU1JnXFAH7r1RbDLJw78aaDnhW4sCKRbLaTYa9eHvcz9PK1YXwod5npWZvMyQ8L4waVjUhuCp6btFyELkRpA4SWNKeQH
private_view_key=b811a2d9faa429e05befab53565029463b162686837f1897816beda1f02a3104
private_spend_key=9d63579d97a32ac8832c4efbe916b251bc399724bd0fa0a8eaa4746bad6f0d08
public_view_key=244a5729aa0b481ca32fe64f533185178fc8351b7d7337d08e6ea69a4e0d39e2
public_spend_key=d305ac259f18c5605d02cf52443b9e2d4f990586f82c209306afc5471aecb9d7
seed=lush waffle neither siblings when sanity pencil grunt ghost fall rabbits adapt items ornament sequence nitrogen orders fall bagpipe width hope fainted hexagon imagine orders
2 changes: 1 addition & 1 deletion tests/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ services:
"--no-zmq",
"--max-connections-per-ip=100",
"--rpc-max-connections-per-private-ip=100",
"--start-mining=42U9v3qs5CjZEePHBZHwuSckQXebuZu299NSmVEmQ41YJZQhKcPyujyMSzpDH4VMMVSBo3U3b54JaNvQLwAjqDhKS3rvM3L",
"--start-mining=49cvU1JnXFAH7r1RbDLJw78aaDnhW4sCKRbLaTYa9eHvcz9PK1YXwod5npWZvMyQ8L4waVjUhuCp6btFyELkRpA4SWNKeQH",
"--mining-threads=1",
"--non-interactive"
]
Expand Down
Loading