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
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[pytest]
minversion = 6.0
addopts = -ra -q
log_cli_level = INFO
testpaths =
tests
5 changes: 0 additions & 5 deletions src/cpp/daemon/py_monero_daemon_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,7 @@ bool PyMoneroRpcConnection::check_connection(int timeout_ms) {
throw std::runtime_error("Could not set rpc connection: " + m_uri.get());
}

std::cout << "Connecting to server " << m_uri.get() << ", timeout: " << timeout_ms << std::endl;
bool connected = m_http_client->connect(std::chrono::milliseconds(timeout_ms));

if (connected) std::cout << "Connected to server" << std::endl;
else std::cout << "Could not connect to server" << std::endl;

auto start = std::chrono::high_resolution_clock::now();
PyMoneroJsonRequest request("get_version");
auto response = send_json_request(request);
Expand Down
61 changes: 28 additions & 33 deletions src/cpp/py_monero.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ PYBIND11_MODULE(monero, m) {
auto py_monero_tx = py::class_<monero::monero_tx, monero::serializable_struct, std::shared_ptr<monero::monero_tx>>(m, "MoneroTx");
auto py_monero_key_image = py::class_<monero::monero_key_image, monero::serializable_struct, std::shared_ptr<monero::monero_key_image>>(m, "MoneroKeyImage");
auto py_monero_output = py::class_<monero::monero_output, monero::serializable_struct, std::shared_ptr<monero::monero_output>>(m, "MoneroOutput");
auto py_monero_wallet_config = py::class_<monero::monero_wallet_config, PyMoneroWalletConfig, std::shared_ptr<monero::monero_wallet_config>>(m, "MoneroWalletConfig");
auto py_monero_wallet_config = py::class_<PyMoneroWalletConfig, monero::serializable_struct, std::shared_ptr<PyMoneroWalletConfig>>(m, "MoneroWalletConfig");
auto py_monero_subaddress = py::class_<monero::monero_subaddress, monero::serializable_struct, std::shared_ptr<monero::monero_subaddress>>(m, "MoneroSubaddress");
auto py_monero_sync_result = py::class_<monero::monero_sync_result, monero::serializable_struct, std::shared_ptr<monero::monero_sync_result>>(m, "MoneroSyncResult");
auto py_monero_account = py::class_<monero::monero_account, monero::serializable_struct, std::shared_ptr<monero::monero_account>>(m, "MoneroAccount");
Expand Down Expand Up @@ -772,32 +772,27 @@ PYBIND11_MODULE(monero, m) {
// monero_wallet_config
py_monero_wallet_config
.def(py::init<>())
.def(py::init<const PyMoneroWalletConfig&>(), py::arg("config"))
.def(py::init<const PyMoneroWalletConfig&>(), py::arg("config"), py::keep_alive<1, 2>())
.def_static("deserialize", [](const std::string& config_json) {
MONERO_CATCH_AND_RETHROW(monero::monero_wallet_config::deserialize(config_json));
MONERO_CATCH_AND_RETHROW(PyMoneroWalletConfig::deserialize(config_json));
}, py::arg("config_json"))
.def_readwrite("path", &monero::monero_wallet_config::m_path)
.def_readwrite("password", &monero::monero_wallet_config::m_password)
.def_readwrite("network_type", &monero::monero_wallet_config::m_network_type)
.def_readwrite("server", &monero::monero_wallet_config::m_server)
.def_readwrite("seed", &monero::monero_wallet_config::m_seed)
.def_readwrite("seed_offset", &monero::monero_wallet_config::m_seed_offset)
.def_readwrite("primary_address", &monero::monero_wallet_config::m_primary_address)
.def_readwrite("private_view_key", &monero::monero_wallet_config::m_private_view_key)
.def_readwrite("private_spend_key", &monero::monero_wallet_config::m_private_spend_key)
.def_readwrite("save_current", &monero::monero_wallet_config::m_save_current)
.def_readwrite("language", &monero::monero_wallet_config::m_language)
.def_readwrite("restore_height", &monero::monero_wallet_config::m_restore_height)
.def_readwrite("account_lookahead", &monero::monero_wallet_config::m_account_lookahead)
.def_readwrite("subaddress_lookahead", &monero::monero_wallet_config::m_subaddress_lookahead)
.def_readwrite("is_multisig", &monero::monero_wallet_config::m_is_multisig)
.def_property("connection_manager",
[](const PyMoneroWalletConfig& self) { return self.m_connection_manager; },
[](PyMoneroWalletConfig& self, std::optional<std::shared_ptr<PyMoneroConnectionManager>> val) {
if (val.has_value()) self.m_connection_manager = val.value();
else self.m_connection_manager = boost::none;
})
.def("copy", [](monero::monero_wallet_config& self) {
.def_readwrite("path", &PyMoneroWalletConfig::m_path)
.def_readwrite("password", &PyMoneroWalletConfig::m_password)
.def_readwrite("network_type", &PyMoneroWalletConfig::m_network_type)
.def_readwrite("server", &PyMoneroWalletConfig::m_server)
.def_readwrite("seed", &PyMoneroWalletConfig::m_seed)
.def_readwrite("seed_offset", &PyMoneroWalletConfig::m_seed_offset)
.def_readwrite("primary_address", &PyMoneroWalletConfig::m_primary_address)
.def_readwrite("private_view_key", &PyMoneroWalletConfig::m_private_view_key)
.def_readwrite("private_spend_key", &PyMoneroWalletConfig::m_private_spend_key)
.def_readwrite("save_current", &PyMoneroWalletConfig::m_save_current)
.def_readwrite("language", &PyMoneroWalletConfig::m_language)
.def_readwrite("restore_height", &PyMoneroWalletConfig::m_restore_height)
.def_readwrite("account_lookahead", &PyMoneroWalletConfig::m_account_lookahead)
.def_readwrite("subaddress_lookahead", &PyMoneroWalletConfig::m_subaddress_lookahead)
.def_readwrite("is_multisig", &PyMoneroWalletConfig::m_is_multisig)
.def_readwrite("connection_manager", &PyMoneroWalletConfig::m_connection_manager)
.def("copy", [](PyMoneroWalletConfig& self) {
MONERO_CATCH_AND_RETHROW(self.copy());
});

Expand Down Expand Up @@ -1975,13 +1970,13 @@ PYBIND11_MODULE(monero, m) {

// monero_wallet_keys
py_monero_wallet_keys
.def_static("create_wallet_random", [](const monero::monero_wallet_config& config) {
.def_static("create_wallet_random", [](const PyMoneroWalletConfig& config) {
MONERO_CATCH_AND_RETHROW(monero::monero_wallet_keys::create_wallet_random(config));
}, py::arg("config"))
.def_static("create_wallet_from_seed", [](const monero::monero_wallet_config& config) {
.def_static("create_wallet_from_seed", [](const PyMoneroWalletConfig& config) {
MONERO_CATCH_AND_RETHROW(monero::monero_wallet_keys::create_wallet_from_seed(config));
}, py::arg("config"))
.def_static("create_wallet_from_keys", [](const monero::monero_wallet_config& config) {
.def_static("create_wallet_from_keys", [](const PyMoneroWalletConfig& config) {
MONERO_CATCH_AND_RETHROW(monero::monero_wallet_keys::create_wallet_from_keys(config));
}, py::arg("config"))
.def_static("get_seed_languages", []() {
Expand All @@ -2002,7 +1997,7 @@ PYBIND11_MODULE(monero, m) {
.def_static("open_wallet_data", [](const std::string& password, monero::monero_network_type nettype, const std::string& keys_data, const std::string& cache_data, const monero_rpc_connection& daemon_connection) {
MONERO_CATCH_AND_RETHROW(monero::monero_wallet_full::open_wallet_data(password, nettype, keys_data, cache_data, daemon_connection));
}, py::arg("password"), py::arg("nettype"), py::arg("keys_data"), py::arg("cache_data"), py::arg("daemon_connection"))
.def_static("create_wallet", [](const monero::monero_wallet_config& config) {
.def_static("create_wallet", [](const PyMoneroWalletConfig& config) {
MONERO_CATCH_AND_RETHROW(monero::monero_wallet_full::create_wallet(config));
}, py::arg("config"))
.def_static("get_seed_languages", []() {
Expand Down Expand Up @@ -2069,14 +2064,14 @@ PYBIND11_MODULE(monero, m) {
throw py::value_error(ex.what());
}
}, py::arg("name"), py::arg("password"))
.def("get_seed_languages", [](PyMoneroWalletRpc& self) {
assert_wallet_is_not_closed(&self);
MONERO_CATCH_AND_RETHROW(self.get_seed_languages());
})
.def("get_rpc_connection", [](PyMoneroWalletRpc& self) {
assert_wallet_is_not_closed(&self);
MONERO_CATCH_AND_RETHROW(self.get_rpc_connection());
})
.def("get_accounts", [](PyMoneroWalletRpc& self, bool include_subaddresses, const std::string& tag, bool skip_balances) {
assert_wallet_is_not_closed(&self);
MONERO_CATCH_AND_RETHROW(self.get_accounts(include_subaddresses, tag, skip_balances));
}, py::arg("include_subaddresses"), py::arg("tag"), py::arg("skip_balances"))
.def("stop", [](PyMoneroWalletRpc& self) {
assert_wallet_is_not_closed(&self);
MONERO_CATCH_AND_RETHROW(self.stop());
Expand Down
84 changes: 52 additions & 32 deletions src/cpp/wallet/py_monero_wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,6 @@ void PyMoneroWallet::set_connection_manager(const std::shared_ptr<PyMoneroConnec
if (connection) set_daemon_connection(*connection);
}

std::optional<std::shared_ptr<PyMoneroConnectionManager>> PyMoneroWallet::get_connection_manager() const {
std::optional<std::shared_ptr<PyMoneroConnectionManager>> result;
if (m_connection_manager != nullptr) result = m_connection_manager;
return result;
}

void PyMoneroWallet::announce_new_block(uint64_t height) {
for (const auto &listener : m_listeners) {
try {
Expand Down Expand Up @@ -365,7 +359,7 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::open_wallet(const std::shared_ptr<PyMonero
else if (config->m_server != boost::none) {
set_daemon_connection(config->m_server);
}

return this;
}

Expand All @@ -377,16 +371,21 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::open_wallet(const std::string& name, const
}

PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet(const std::shared_ptr<PyMoneroWalletConfig> &config) {
if (!config) throw std::runtime_error("Must specify config to create wallet");
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");
if (config->m_seed != boost::none && (config->m_primary_address != boost::none || config->m_private_view_key != boost::none || config->m_private_spend_key != boost::none)) {
throw std::runtime_error("Wallet can be initialized with a seed or keys but not both");
}
if (config->m_account_lookahead != boost::none || config->m_subaddress_lookahead != boost::none) throw std::runtime_error("monero-wallet-rpc does not support creating wallets with subaddress lookahead over rpc");
if (config->m_connection_manager != boost::none) {
if (config->m_server != boost::none) throw std::runtime_error("Wallet can be opened with a server or connection manager but not both");
auto connection = config->m_connection_manager.get()->get_connection();
if (connection) config->m_server = *connection;
auto cm = config->m_connection_manager.value();
if (cm != nullptr) {
auto connection = cm->get_connection();
if (connection) {
config->m_server = *connection;
}
}
}

if (config->m_seed != boost::none) create_wallet_from_seed(config);
Expand Down Expand Up @@ -551,16 +550,20 @@ std::string PyMoneroWalletRpc::get_address(const uint32_t account_idx, const uin
get_subaddresses(account_idx, empty_indices, true);
return get_address(account_idx, subaddress_idx);
}

auto subaddress_map = it->second;
auto it2 = subaddress_map.find(subaddress_idx);

if (it2 == subaddress_map.end()) {
get_subaddresses(account_idx, empty_indices, true);
return get_address(account_idx, subaddress_idx);
auto it3 = m_address_cache.find(account_idx);
if (it3 == m_address_cache.end()) throw std::runtime_error("Could not find account address at index (" + std::to_string(account_idx) + ", " + std::to_string(subaddress_idx) + ")" );
auto it4 = it3->second.find(subaddress_idx);
if (it4 == it3->second.end()) throw std::runtime_error("Could not find address at index (" + std::to_string(account_idx) + ", " + std::to_string(subaddress_idx) + ")" );
return it4->second;
}

return it2->second.data();
return it2->second;
}

monero_subaddress PyMoneroWalletRpc::get_address_index(const std::string& address) const {
Expand Down Expand Up @@ -736,6 +739,25 @@ uint64_t PyMoneroWalletRpc::get_unlocked_balance(uint32_t account_idx, uint32_t
return wallet_balance->m_unlocked_balance;
}

monero_account PyMoneroWalletRpc::get_account(const uint32_t account_idx, bool include_subaddresses) const {
return get_account(account_idx, include_subaddresses, false);
}

monero_account PyMoneroWalletRpc::get_account(const uint32_t account_idx, bool include_subaddresses, bool skip_balances) const {
for(auto& account : monero::monero_wallet::get_accounts()) {
if (account.m_index.get() == account_idx) {
std::vector<uint32_t> empty_indices;
if (include_subaddresses) account.m_subaddresses = get_subaddresses(account_idx, empty_indices, skip_balances);
return account;
}
}
throw std::runtime_error("Account with index " + std::to_string(account_idx) + " does not exist");
}

std::vector<monero_account> PyMoneroWalletRpc::get_accounts(bool include_subaddresses, const std::string& tag) const {
return get_accounts(include_subaddresses, tag, false);
}

std::vector<monero_account> PyMoneroWalletRpc::get_accounts(bool include_subaddresses, const std::string& tag, bool skip_balances) const {
auto params = std::make_shared<PyMoneroGetAccountsParams>(tag);
PyMoneroJsonRequest request("get_accounts", params);
Expand All @@ -744,7 +766,6 @@ std::vector<monero_account> PyMoneroWalletRpc::get_accounts(bool include_subaddr
auto node = response->m_result.get();
std::vector<monero_account> accounts;
PyMoneroAccount::from_property_tree(node, accounts);

if (include_subaddresses) {

for (auto &account : accounts) {
Expand All @@ -769,7 +790,6 @@ std::vector<monero_account> PyMoneroWalletRpc::get_accounts(bool include_subaddr
auto node2 = response2->m_result.get();
auto bal_res = std::make_shared<PyMoneroGetBalanceResponse>();
PyMoneroGetBalanceResponse::from_property_tree(node2, bal_res);

for (const auto &subaddress : bal_res->m_per_subaddress) {
// merge info
auto account = &accounts[subaddress->m_account_index.get()];
Expand All @@ -795,6 +815,8 @@ monero_account PyMoneroWalletRpc::create_account(const std::string& label) {
if (response->m_result == boost::none) throw std::runtime_error("Invalid Monero JSONRPC response");
auto node = response->m_result.get();
monero_account res;
res.m_balance = 0;
res.m_unlocked_balance = 0;
bool found_index = false;
bool address_found = false;

Expand Down Expand Up @@ -826,18 +848,19 @@ std::vector<monero_subaddress> PyMoneroWalletRpc::get_subaddresses(const uint32_

std::vector<monero_subaddress> subaddresses;

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

if (key == std::string("addresses")) {
auto node2 = it->second;
for (auto it2 = node2.begin(); it != node2.end(); ++it2) {
for (auto it2 = node2.begin(); it2 != node2.end(); ++it2) {
auto subaddress = std::make_shared<monero::monero_subaddress>();
PyMoneroSubaddress::from_rpc_property_tree(node2, subaddress);
PyMoneroSubaddress::from_rpc_property_tree(it2->second, subaddress);
subaddress->m_account_index = account_idx;
subaddresses.push_back(*subaddress);
}
break;
}

}
Expand All @@ -864,28 +887,25 @@ std::vector<monero_subaddress> PyMoneroWalletRpc::get_subaddresses(const uint32_

for (auto &tgt_subaddress: subaddresses) {
for (const auto &rpc_subaddress : subaddresses2) {
if (rpc_subaddress->m_index != tgt_subaddress.m_index) continue;

if (rpc_subaddress->m_index != tgt_subaddress.m_index) continue; // skip to subaddress with same index
if (rpc_subaddress->m_balance != boost::none) tgt_subaddress.m_balance = rpc_subaddress->m_balance;
if (rpc_subaddress->m_unlocked_balance != boost::none) tgt_subaddress.m_unlocked_balance = rpc_subaddress->m_unlocked_balance;
if (rpc_subaddress->m_num_unspent_outputs != boost::none) tgt_subaddress.m_num_unspent_outputs = rpc_subaddress->m_num_unspent_outputs;
if (rpc_subaddress->m_num_blocks_to_unlock != boost::none) tgt_subaddress.m_num_blocks_to_unlock = rpc_subaddress->m_num_blocks_to_unlock;
}
}
}

// cache addresses
/*
Map<Integer, String> subaddressMap = addressCache.get(accountIdx);
if (subaddressMap == null) {
subaddressMap = new HashMap<Integer, String>();
addressCache.put(accountIdx, subaddressMap);
auto it = m_address_cache.find(account_idx);
if (it == m_address_cache.end()) {
m_address_cache[account_idx] = serializable_unordered_map<uint32_t, std::string>();
}

for (const auto &subaddress : subaddresses) {
subaddressMap.put(subaddress.getIndex(), subaddress.getAddress());
for (const auto& subaddress : subaddresses) {
m_address_cache[account_idx][subaddress.m_index.get()] = subaddress.m_address.get();
}
*/

// return results
return subaddresses;
}
Expand All @@ -895,8 +915,8 @@ std::vector<monero_subaddress> PyMoneroWalletRpc::get_subaddresses(uint32_t acco
}

std::vector<monero_subaddress> PyMoneroWalletRpc::get_subaddresses(const uint32_t account_idx) const {
std::vector<uint32_t> subaddress_indices;
return get_subaddresses(account_idx, subaddress_indices);
std::vector<uint32_t> empty_indices;
return get_subaddresses(account_idx, empty_indices);
}

monero_subaddress PyMoneroWalletRpc::get_subaddress(const uint32_t account_idx, const uint32_t subaddress_idx) const {
Expand All @@ -916,7 +936,7 @@ monero_subaddress PyMoneroWalletRpc::create_subaddress(uint32_t account_idx, con
auto node = response->m_result.get();
monero_subaddress sub;
sub.m_account_index = account_idx;
sub.m_label = label;
if (!label.empty()) sub.m_label = label;
sub.m_balance = 0;
sub.m_unlocked_balance = 0;
sub.m_num_unspent_outputs = 0;
Expand Down
7 changes: 5 additions & 2 deletions src/cpp/wallet/py_monero_wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ class PyMoneroWallet : public monero::monero_wallet {
}

virtual void set_connection_manager(const std::shared_ptr<PyMoneroConnectionManager> &connection_manager);
virtual std::optional<std::shared_ptr<PyMoneroConnectionManager>> get_connection_manager() const;
virtual std::shared_ptr<PyMoneroConnectionManager> get_connection_manager() const { return m_connection_manager; }
virtual void announce_new_block(uint64_t height);
virtual void announce_sync_progress(uint64_t height, uint64_t start_height, uint64_t end_height, float percent_done, const std::string &message);
virtual void announce_balances_changed(uint64_t balance, uint64_t unlocked_balance);
Expand Down Expand Up @@ -678,6 +678,9 @@ class PyMoneroWalletRpc : public PyMoneroWallet {
uint64_t get_unlocked_balance() const override;
uint64_t get_unlocked_balance(uint32_t account_index) const override;
uint64_t get_unlocked_balance(uint32_t account_idx, uint32_t subaddress_idx) const override;
monero_account get_account(const uint32_t account_idx, bool include_subaddresses) const override;
monero_account get_account(const uint32_t account_idx, bool include_subaddresses, bool skip_balances) const;
std::vector<monero_account> get_accounts(bool include_subaddresses, const std::string& tag) const override;
std::vector<monero_account> get_accounts(bool include_subaddresses, const std::string& tag, bool skip_balances) const;
monero_account create_account(const std::string& label = "") override;
std::vector<monero_subaddress> get_subaddresses(const uint32_t account_idx, const std::vector<uint32_t>& subaddress_indices, bool skip_balances) const;
Expand Down Expand Up @@ -755,7 +758,7 @@ class PyMoneroWalletRpc : public PyMoneroWallet {
std::shared_ptr<PyMoneroWalletPoller> m_poller;

mutable boost::recursive_mutex m_sync_mutex;
serializable_unordered_map<uint32_t, serializable_unordered_map<uint32_t, std::string>> m_address_cache;
mutable serializable_unordered_map<uint32_t, serializable_unordered_map<uint32_t, std::string>> m_address_cache;

PyMoneroWalletRpc* create_wallet_random(const std::shared_ptr<PyMoneroWalletConfig> &conf);
PyMoneroWalletRpc* create_wallet_from_seed(const std::shared_ptr<PyMoneroWalletConfig> &conf);
Expand Down
Loading