diff --git a/bin/cleanup_test_environment.sh b/bin/cleanup_test_environment.sh new file mode 100755 index 0000000..be7b9d8 --- /dev/null +++ b/bin/cleanup_test_environment.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# remove docker containers +sudo docker compose -f tests/docker-compose.yml down -v diff --git a/bin/setup_test_environment.sh b/bin/setup_test_environment.sh new file mode 100755 index 0000000..99c5770 --- /dev/null +++ b/bin/setup_test_environment.sh @@ -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 diff --git a/src/cpp/py_monero.cpp b/src/cpp/py_monero.cpp index d136e89..f07aa2c 100644 --- a/src/cpp/py_monero.cpp +++ b/src/cpp/py_monero.cpp @@ -7,6 +7,8 @@ return expr; \ } catch (const PyMoneroRpcError& e) { \ throw; \ + } catch (const PyMoneroError& e) { \ + throw; \ } \ catch (const std::exception& e) { \ throw PyMoneroError(e.what()); \ @@ -1426,15 +1428,10 @@ PYBIND11_MODULE(monero, m) { py_monero_wallet .def(py::init<>()) .def_property_readonly_static("DEFAULT_LANGUAGE", [](py::object /* self */) { return std::string("English"); }) - .def("is_closed", [](const monero::monero_wallet& self) { - return is_wallet_closed(&self); - }) .def("is_view_only", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_view_only()); }) .def("set_connection_manager", [](PyMoneroWallet& self, const std::optional> &connection_manager) { - assert_wallet_is_not_closed(&self); if (connection_manager.has_value()) { MONERO_CATCH_AND_RETHROW(self.set_connection_manager(connection_manager.value())); } @@ -1443,123 +1440,93 @@ PYBIND11_MODULE(monero, m) { } }, py::arg("connection_manager")) .def("get_connection_manager", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_connection_manager()); }) .def("set_daemon_connection", [](PyMoneroWallet& self, const boost::optional& connection) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_daemon_connection(connection)); }, py::arg("connection")) .def("set_daemon_connection", [](PyMoneroWallet& self, const std::string& uri, const std::string& username, const std::string& password, const std::string& proxy) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_daemon_connection(uri, username, password, proxy)); }, py::arg("uri"), py::arg("username") = "", py::arg("password") = "", py::arg("proxy") = "") .def("get_daemon_connection", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_daemon_connection()); }) .def("is_connected_to_daemon", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_connected_to_daemon()); }) .def("is_daemon_trusted", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_daemon_trusted()); }) .def("is_synced", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_synced()); }) .def("get_version", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_version()); }) .def("get_path", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_path()); }) .def("get_network_type", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_network_type()); }) .def("get_seed", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_seed()); }) .def("get_seed_language", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_seed_language()); }) .def("get_public_view_key", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_public_view_key()); }) .def("get_private_view_key", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_private_view_key()); }) .def("get_public_spend_key", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_public_spend_key()); }) .def("get_private_spend_key", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_private_spend_key()); }) .def("get_primary_address", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_primary_address()); }) .def("get_address", [](PyMoneroWallet& self, uint32_t account_idx, uint32_t subaddress_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_address(account_idx, subaddress_idx)); }, py::arg("account_idx"), py::arg("subaddress_idx")) .def("get_address_index", [](PyMoneroWallet& self, const std::string& address) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_address_index(address)); }, py::arg("address")) .def("get_integrated_address", [](PyMoneroWallet& self, const std::string& standard_address, const std::string& payment_id) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_integrated_address(standard_address, payment_id)); }, py::arg("standard_address") = "", py::arg("payment_id") = "") .def("decode_integrated_address", [](PyMoneroWallet& self, const std::string& integrated_address) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.decode_integrated_address(integrated_address)); }, py::arg("integrated_address")) .def("get_height", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_height()); }) .def("get_restore_height", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_restore_height()); }) .def("set_restore_height", [](PyMoneroWallet& self, uint64_t restore_height) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_restore_height(restore_height)); }, py::arg("restore_height")) .def("get_daemon_height", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_daemon_height()); }) .def("get_daemon_max_peer_height", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_daemon_max_peer_height()); }) .def("get_height_by_date", [](PyMoneroWallet& self, uint16_t year, uint8_t month, uint8_t day) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_height_by_date(year, month, day)); }, py::arg("year"), py::arg("month"), py::arg("day")) .def("add_listener", [](PyMoneroWallet& self, monero::monero_wallet_listener& listener) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.add_listener(listener)); }, py::arg("listener")) .def("remove_listener", [](PyMoneroWallet& self, monero::monero_wallet_listener& listener) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.remove_listener(listener)); }, py::arg("listener")) .def("get_listeners", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); try { std::set listeners = self.get_listeners(); std::vector> result(listeners.size()); @@ -1575,95 +1542,72 @@ PYBIND11_MODULE(monero, m) { } }) .def("sync", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sync()); }) .def("sync", [](PyMoneroWallet& self, monero::monero_wallet_listener& listener) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sync(listener)); }, py::arg("listener")) .def("sync", [](PyMoneroWallet& self, uint64_t start_height) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sync(start_height)); }, py::arg("start_height")) .def("sync", [](PyMoneroWallet& self, uint64_t start_height, monero::monero_wallet_listener& listener) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sync(start_height, listener)); }, py::arg("start_height"), py::arg("listener")) .def("start_syncing", [](PyMoneroWallet& self, uint64_t sync_period_in_ms) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.start_syncing(sync_period_in_ms)); }, py::arg("sync_period_in_ms") = 10000) .def("stop_syncing", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.stop_syncing()); }) .def("scan_txs", [](PyMoneroWallet& self, const std::vector& tx_hashes) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.scan_txs(tx_hashes)); }, py::arg("tx_hashes")) .def("rescan_spent", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.rescan_spent()); }) .def("rescan_blockchain", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.rescan_blockchain()); }) .def("get_balance", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_balance()); }) .def("get_balance", [](PyMoneroWallet& self, uint32_t account_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_balance(account_idx)); }, py::arg("account_idx")) .def("get_balance", [](PyMoneroWallet& self, uint32_t account_idx, uint32_t subaddress_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_balance(account_idx, subaddress_idx)); }, py::arg("account_idx"), py::arg("subaddress_idx")) .def("get_unlocked_balance", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_unlocked_balance()); }) .def("get_unlocked_balance", [](PyMoneroWallet& self, uint32_t account_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_unlocked_balance(account_idx)); }, py::arg("account_idx")) .def("get_unlocked_balance", [](PyMoneroWallet& self, uint32_t account_idx, uint32_t subaddress_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_unlocked_balance(account_idx, subaddress_idx)); }, py::arg("account_idx"), py::arg("subaddress_idx")) .def("get_accounts", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_accounts()); }) .def("get_accounts", [](PyMoneroWallet& self, bool include_subaddresses) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_accounts(include_subaddresses)); }, py::arg("include_subaddresses")) .def("get_accounts", [](PyMoneroWallet& self, const std::string& tag) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_accounts(tag)); }, py::arg("tag")) .def("get_accounts", [](PyMoneroWallet& self, bool include_subaddresses, const std::string& tag) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_accounts(include_subaddresses, tag)); }, py::arg("include_subaddresses"), py::arg("tag")) .def("get_account", [](PyMoneroWallet& self, uint32_t account_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_account(account_idx)); }, py::arg("account_idx")) .def("get_account", [](PyMoneroWallet& self, uint32_t account_idx, bool include_subaddresses) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_account(account_idx, include_subaddresses)); }, py::arg("account_idx"), py::arg("include_subaddresses")) .def("create_account", [](PyMoneroWallet& self, const std::string& label) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.create_account(label)); }, py::arg("label") = "") .def("get_subaddress", [](PyMoneroWallet& wallet, uint32_t account_idx, uint32_t subaddress_idx) { - assert_wallet_is_not_closed(&wallet); // TODO move this to monero-cpp? try { std::vector subaddress_indices; @@ -1679,227 +1623,171 @@ PYBIND11_MODULE(monero, m) { } }, py::arg("account_idx"), py::arg("subaddress_idx")) .def("get_subaddresses", [](PyMoneroWallet& self, uint32_t account_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_subaddresses(account_idx)); }, py::arg("account_idx")) .def("get_subaddresses", [](PyMoneroWallet& self, uint32_t account_idx, const std::vector& subaddress_indices) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_subaddresses(account_idx, subaddress_indices)); }, py::arg("account_idx"), py::arg("subaddress_indices")) .def("create_subaddress", [](PyMoneroWallet& self, uint32_t account_idx, const std::string& label) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.create_subaddress(account_idx, label)); }, py::arg("account_idx"), py::arg("label") = "") .def("set_subaddress_label", [](PyMoneroWallet& self, uint32_t account_idx, uint32_t subaddress_idx, const std::string& label) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_subaddress_label(account_idx, subaddress_idx, label)); }, py::arg("account_idx"), py::arg("subaddress_idx"), py::arg("label") = "") .def("get_txs", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_txs()); }) .def("get_txs", [](PyMoneroWallet& self, const monero::monero_tx_query& query) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_txs(query)); }, py::arg("query")) .def("get_transfers", [](PyMoneroWallet& self, const monero::monero_transfer_query& query) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_transfers(query)); }, py::arg("query")) .def("get_outputs", [](PyMoneroWallet& self, const monero::monero_output_query& query) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_outputs(query)); }, py::arg("query")) .def("export_outputs", [](PyMoneroWallet& self, bool all) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.export_outputs(all)); }, py::arg("all") = false) .def("import_outputs", [](PyMoneroWallet& self, const std::string& outputs_hex) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.import_outputs(outputs_hex)); }, py::arg("outputs_hex")) .def("export_key_images", [](PyMoneroWallet& self, bool all) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.export_key_images(all)); }, py::arg("all") = false) .def("import_key_images", [](PyMoneroWallet& self, const std::vector>& key_images) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.import_key_images(key_images)); }, py::arg("key_images")) .def("get_new_key_images_from_last_import", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_new_key_images_from_last_import()); }) .def("freeze_output", [](PyMoneroWallet& self, const std::string& key_image) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.freeze_output(key_image)); }, py::arg("key_image")) .def("thaw_output", [](PyMoneroWallet& self, const std::string& key_image) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.thaw_output(key_image)); }, py::arg("key_image")) .def("is_output_frozen", [](PyMoneroWallet& self, const std::string& key_image) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_output_frozen(key_image)); }, py::arg("key_image")) .def("get_default_fee_priority", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_default_fee_priority()); }) .def("create_tx", [](PyMoneroWallet& self, const monero::monero_tx_config& config) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.create_tx(config)); }, py::arg("config")) .def("create_txs", [](PyMoneroWallet& self, const monero::monero_tx_config& config) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.create_txs(config)); }, py::arg("config")) .def("sweep_unlocked", [](PyMoneroWallet& self, const monero::monero_tx_config& config) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sweep_unlocked(config)); }, py::arg("config")) .def("sweep_output", [](PyMoneroWallet& self, const monero::monero_tx_config& config) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sweep_output(config)); }, py::arg("config")) .def("sweep_dust", [](PyMoneroWallet& self, bool relay) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sweep_dust(relay)); }, py::arg("relay") = false) .def("relay_tx", [](PyMoneroWallet& self, const std::string& tx_metadata) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.relay_tx(tx_metadata)); }, py::arg("tx_metadata")) .def("relay_tx", [](PyMoneroWallet& self, const monero::monero_tx_wallet& tx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.relay_tx(tx)); }, py::arg("tx")) .def("relay_txs", [](PyMoneroWallet& self, const std::vector>& txs) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.relay_txs(txs)); }, py::arg("txs")) .def("relay_txs", [](PyMoneroWallet& self, const std::vector& tx_metadatas) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.relay_txs(tx_metadatas)); }, py::arg("tx_metadatas")) .def("describe_tx_set", [](PyMoneroWallet& self, const monero::monero_tx_set& tx_set) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.describe_tx_set(tx_set)); }, py::arg("tx_set")) .def("sign_txs", [](PyMoneroWallet& self, const std::string& unsigned_tx_hex) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sign_txs(unsigned_tx_hex)); }, py::arg("unsigned_tx_hex")) .def("submit_txs", [](PyMoneroWallet& self, const std::string& signed_tx_hex) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.submit_txs(signed_tx_hex)); }, py::arg("signed_tx_hex")) .def("sign_message", [](PyMoneroWallet& self, const std::string& msg, monero_message_signature_type signature_type, uint32_t account_idx, uint32_t subaddress_idx) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sign_message(msg, signature_type, account_idx, subaddress_idx)); }, py::arg("msg"), py::arg("signature_type"), py::arg("account_idx") = 0, py::arg("subaddress_idx") = 0) .def("verify_message", [](PyMoneroWallet& self, const std::string& msg, const std::string& address, const std::string& signature) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.verify_message(msg, address, signature)); }, py::arg("msg"), py::arg("address"), py::arg("signature")) .def("get_tx_key", [](PyMoneroWallet& self, const std::string& tx_hash) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_tx_key(tx_hash)); }, py::arg("tx_hash")) .def("check_tx_key", [](PyMoneroWallet& self, const std::string& tx_hash, const std::string& tx_key, const std::string& address) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.check_tx_key(tx_hash, tx_key, address)); }, py::arg("tx_hash"), py::arg("tx_key"), py::arg("address")) .def("get_tx_proof", [](PyMoneroWallet& self, const std::string& tx_hash, const std::string& address, const std::string& message) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_tx_proof(tx_hash, address, message)); }, py::arg("tx_hash"), py::arg("address"), py::arg("message")) .def("check_tx_proof", [](PyMoneroWallet& self, const std::string& tx_hash, const std::string& address, const std::string& message, const std::string& signature) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.check_tx_proof(tx_hash, address, message, signature)); }, py::arg("tx_hash"), py::arg("address"), py::arg("message"), py::arg("signature")) .def("get_spend_proof", [](PyMoneroWallet& self, const std::string& tx_hash, const std::string& message) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_spend_proof(tx_hash, message)); }, py::arg("tx_hash"), py::arg("message")) .def("check_spend_proof", [](PyMoneroWallet& self, const std::string& tx_hash, const std::string& message, const std::string& signature) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.check_spend_proof(tx_hash, message, signature)); }, py::arg("tx_hash"), py::arg("message"), py::arg("signature")) .def("get_reserve_proof_wallet", [](PyMoneroWallet& self, const std::string& message) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_reserve_proof_wallet(message)); }, py::arg("message")) .def("get_reserve_proof_account", [](PyMoneroWallet& self, uint32_t account_idx, uint64_t amount, const std::string& message) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_reserve_proof_account(account_idx, amount, message)); }, py::arg("account_idx"), py::arg("amount"), py::arg("message")) .def("check_reserve_proof", [](PyMoneroWallet& self, const std::string& address, const std::string& message, const std::string& signature) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.check_reserve_proof(address, message, signature)); }, py::arg("address"), py::arg("message"), py::arg("signature")) .def("get_tx_note", [](PyMoneroWallet& self, const std::string& tx_hash) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_tx_note(tx_hash)); }, py::arg("tx_hash")) .def("get_tx_notes", [](PyMoneroWallet& self, const std::vector& tx_hashes) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_tx_notes(tx_hashes)); }, py::arg("tx_hashes")) .def("set_tx_note", [](PyMoneroWallet& self, const std::string& tx_hash, const std::string& note) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_tx_note(tx_hash, note)); }, py::arg("tx_hash"), py::arg("note")) .def("set_tx_notes", [](PyMoneroWallet& self, const std::vector& tx_hashes, const std::vector& notes) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_tx_notes(tx_hashes, notes)); }, py::arg("tx_hashes"), py::arg("notes")) .def("get_address_book_entries", [](PyMoneroWallet& self, const std::vector& indices) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_address_book_entries(indices)); }, py::arg("indices")) .def("add_address_book_entry", [](PyMoneroWallet& self, const std::string& address, const std::string& description) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.add_address_book_entry(address, description)); }, py::arg("address"), py::arg("description")) .def("edit_address_book_entry", [](PyMoneroWallet& self, uint64_t index, bool set_address, const std::string& address, bool set_description, const std::string& description) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.edit_address_book_entry(index, set_address, address, set_description, description)); }, py::arg("index"), py::arg("set_address"), py::arg("address"), py::arg("set_description"), py::arg("description")) .def("delete_address_book_entry", [](PyMoneroWallet& self, uint64_t index) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.delete_address_book_entry(index)); }, py::arg("index")) .def("tag_accounts", [](PyMoneroWallet& self, const std::string& tag, const std::vector& account_indices) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.tag_accounts(tag, account_indices)); }, py::arg("tag"), py::arg("account_indices")) .def("untag_accounts", [](PyMoneroWallet& self, const std::vector& account_indices) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.untag_accounts(account_indices)); }, py::arg("account_indices")) .def("get_account_tags", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_account_tags()); }) .def("set_account_tag_label", [](PyMoneroWallet& self, const std::string& tag, const std::string& label) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_account_tag_label(tag, label)); }, py::arg("tag"), py::arg("label")) .def("set_account_label", [](PyMoneroWallet& self, uint32_t account_idx, const std::string& label) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_account_label(account_idx, label)); }, py::arg("account_idx"), py::arg("label")) .def("get_payment_uri", [](PyMoneroWallet& self, const monero::monero_tx_config& config) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_payment_uri(config)); }, py::arg("config")) .def("parse_payment_uri", [](PyMoneroWallet& self, const std::string& uri) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.parse_payment_uri(uri)); }, py::arg("uri")) .def("get_attribute", [](PyMoneroWallet& self, const std::string& key) { - assert_wallet_is_not_closed(&self); try { std::string val; self.get_attribute(key, val); @@ -1909,77 +1797,58 @@ PYBIND11_MODULE(monero, m) { } }, py::arg("key")) .def("set_attribute", [](PyMoneroWallet& self, const std::string& key, const std::string& val) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.set_attribute(key, val)); }, py::arg("key"), py::arg("val")) .def("start_mining", [](PyMoneroWallet& self, boost::optional num_threads, boost::optional background_mining, boost::optional ignore_battery) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.start_mining(num_threads, background_mining, ignore_battery)); }, py::arg("num_threads") = py::none(), py::arg("background_mining") = py::none(), py::arg("ignore_battery") = py::none()) .def("stop_mining", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.stop_mining()); }) .def("wait_for_next_block", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.wait_for_next_block()); }) .def("is_multisig_import_needed", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_multisig_import_needed()); }) .def("is_multisig", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.is_multisig()); }) .def("get_multisig_info", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_multisig_info()); }) .def("prepare_multisig", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.prepare_multisig()); }) .def("make_multisig", [](PyMoneroWallet& self, const std::vector& multisig_hexes, int threshold, const std::string& password) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.make_multisig(multisig_hexes, threshold, password)); }, py::arg("multisig_hexes"), py::arg("threshold"), py::arg("password")) .def("exchange_multisig_keys", [](PyMoneroWallet& self, const std::vector& multisig_hexes, const std::string& password) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.exchange_multisig_keys(multisig_hexes, password)); }, py::arg("multisig_hexes"), py::arg("password")) .def("export_multisig_hex", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.export_multisig_hex()); }) .def("import_multisig_hex", [](PyMoneroWallet& self, const std::vector& multisig_hexes) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.import_multisig_hex(multisig_hexes)); }, py::arg("multisig_hexes")) .def("sign_multisig_tx_hex", [](PyMoneroWallet& self, const std::string& multisig_tx_hex) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.sign_multisig_tx_hex(multisig_tx_hex)); }, py::arg("multisig_tx_hex")) .def("submit_multisig_tx_hex", [](PyMoneroWallet& self, const std::string& signed_multisig_tx_hex) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.submit_multisig_tx_hex(signed_multisig_tx_hex)); }, py::arg("signed_multisig_tx_hex")) .def("change_password", [](PyMoneroWallet& self, const std::string& old_password, const std::string& new_password) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.change_password(old_password, new_password)); }, py::arg("old_password"), py::arg("new_password")) .def("move_to", [](PyMoneroWallet& self, const std::string& path, const std::string& password) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.move_to(path, password)); }, py::arg("path"), py::arg("password")) .def("save", [](PyMoneroWallet& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.save()); }) .def("close", [](monero::monero_wallet& self, bool save) { - assert_wallet_is_not_closed(&self); self.close(save); - set_wallet_closed(&self, true); }, py::arg("save") = false); // monero_wallet_keys @@ -2018,11 +1887,9 @@ PYBIND11_MODULE(monero, m) { MONERO_CATCH_AND_RETHROW(monero::monero_wallet_full::get_seed_languages()); }) .def("get_keys_file_buffer", [](monero::monero_wallet_full& self, std::string& password, bool view_only) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_keys_file_buffer(password, view_only)); }, py::arg("password"), py::arg("view_only")) .def("get_cache_file_buffer", [](monero::monero_wallet_full& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.get_cache_file_buffer()); }); @@ -2033,7 +1900,6 @@ PYBIND11_MODULE(monero, m) { .def("create_wallet", [](PyMoneroWalletRpc& self, const std::shared_ptr config) { try { self.create_wallet(config); - set_wallet_closed(&self, false); return &self; } catch(const PyMoneroRpcError& ex) { @@ -2049,7 +1915,6 @@ PYBIND11_MODULE(monero, m) { .def("open_wallet", [](PyMoneroWalletRpc& self, const std::shared_ptr config) { try { self.open_wallet(config); - set_wallet_closed(&self, false); return &self; } catch(const PyMoneroRpcError& ex) { @@ -2065,7 +1930,6 @@ PYBIND11_MODULE(monero, m) { .def("open_wallet", [](PyMoneroWalletRpc& self, const std::string& name, const std::string& password) { try { self.open_wallet(name, password); - set_wallet_closed(&self, false); return &self; } catch(const PyMoneroRpcError& ex) { @@ -2079,15 +1943,12 @@ PYBIND11_MODULE(monero, m) { } }, 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("stop", [](PyMoneroWalletRpc& self) { - assert_wallet_is_not_closed(&self); MONERO_CATCH_AND_RETHROW(self.stop()); }); diff --git a/src/cpp/wallet/py_monero_wallet.cpp b/src/cpp/wallet/py_monero_wallet.cpp index 762e7fe..bae84b2 100644 --- a/src/cpp/wallet/py_monero_wallet.cpp +++ b/src/cpp/wallet/py_monero_wallet.cpp @@ -1,25 +1,5 @@ #include "py_monero_wallet.h" -namespace { - std::unordered_map wallet_closed_map; - std::mutex wallet_map_mutex; -} - -void set_wallet_closed(const void* wallet, bool value) { - std::lock_guard lock(wallet_map_mutex); - wallet_closed_map[wallet] = value; -} - -bool is_wallet_closed(const void* wallet) { - std::lock_guard 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; } diff --git a/src/cpp/wallet/py_monero_wallet.h b/src/cpp/wallet/py_monero_wallet.h index b80f0ae..5121183 100644 --- a/src/cpp/wallet/py_monero_wallet.h +++ b/src/cpp/wallet/py_monero_wallet.h @@ -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); diff --git a/src/cpp/wallet/py_monero_wallet_model.cpp b/src/cpp/wallet/py_monero_wallet_model.cpp index cdc9acb..9fcd47b 100644 --- a/src/cpp/wallet/py_monero_wallet_model.cpp +++ b/src/cpp/wallet/py_monero_wallet_model.cpp @@ -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; diff --git a/src/cpp/wallet/py_monero_wallet_rpc.cpp b/src/cpp/wallet/py_monero_wallet_rpc.cpp index dbadff8..7f14376 100644 --- a/src/cpp/wallet/py_monero_wallet_rpc.cpp +++ b/src/cpp/wallet/py_monero_wallet_rpc.cpp @@ -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 &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"); @@ -303,6 +311,16 @@ boost::optional PyMoneroWalletRpc::get_daemon_con return boost::optional(*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 rpc = monero_rpc_connection(uri, username, password, proxy_uri); + set_daemon_connection(rpc); +} + + void PyMoneroWalletRpc::set_daemon_connection(const boost::optional& connection, bool is_trusted, const boost::optional> ssl_options) { auto params = std::make_shared(); if (connection == boost::none) { @@ -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; } } @@ -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(); boost::lock_guard 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(); - else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value(); - } + if (key == std::string("blocks_fetched")) sync_result.m_num_blocks_fetched = it->second.get_value(); + else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value(); + } - 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) { @@ -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(start_height); boost::lock_guard 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(); - else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value(); - } + if (key == std::string("blocks_fetched")) sync_result.m_num_blocks_fetched = it->second.get_value(); + else if (key == std::string("received_money")) sync_result.m_received_money = it->second.get_value(); + } - 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) { @@ -1538,7 +1566,8 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_random(const std::shared_ptr auto params = std::make_shared(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; @@ -1547,7 +1576,7 @@ PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_random(const std::shared_ptr PyMoneroWalletRpc* PyMoneroWalletRpc::create_wallet_from_seed(const std::shared_ptr &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; @@ -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(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; @@ -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(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; diff --git a/src/cpp/wallet/py_monero_wallet_rpc.h b/src/cpp/wallet/py_monero_wallet_rpc.h index 568670f..2a209ce 100644 --- a/src/cpp/wallet/py_monero_wallet_rpc.h +++ b/src/cpp/wallet/py_monero_wallet_rpc.h @@ -68,6 +68,7 @@ class PyMoneroWalletRpc : public PyMoneroWallet { boost::optional get_daemon_connection() const override; void set_daemon_connection(const boost::optional& connection, bool is_trusted, const boost::optional> ssl_options); void set_daemon_connection(const boost::optional& 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; diff --git a/tests/config/config.ini b/tests/config/config.ini index da1ea31..83e4464 100644 --- a/tests/config/config.ini +++ b/tests/config/config.ini @@ -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 @@ -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 diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 403205b..0321d15 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -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" ] diff --git a/tests/test_monero_wallet_common.py b/tests/test_monero_wallet_common.py index 0379576..d6bf59c 100644 --- a/tests/test_monero_wallet_common.py +++ b/tests/test_monero_wallet_common.py @@ -11,7 +11,7 @@ MoneroTxConfig, MoneroDestination, MoneroRpcConnection, MoneroError, MoneroKeyImage, MoneroTxQuery, MoneroUtils ) -from utils import TestUtils, WalletEqualityUtils +from utils import TestUtils, WalletEqualityUtils, MiningUtils logger: logging.Logger = logging.getLogger("TestMoneroWalletCommon") @@ -66,6 +66,9 @@ def _open_wallet_from_path(self, path: str, password: str | None) -> MoneroWalle return self._open_wallet(config) + def get_daemon_rpc_uri(self) -> str: + return TestUtils.DAEMON_RPC_URI + @classmethod def is_random_wallet_config(cls, config: Optional[MoneroWalletConfig]) -> bool: assert config is not None @@ -81,6 +84,11 @@ def test_config(self) -> BaseTestMoneroWallet.Config: parser.read('tests/config/test_monero_wallet_common.ini') return BaseTestMoneroWallet.Config.parse(parser) + @pytest.fixture(scope="class", autouse=True) + def before_all(self): + MiningUtils.wait_until_blockchain_ready() + MiningUtils.try_stop_mining() + @pytest.fixture(autouse=True) def before_each(self, request: pytest.FixtureRequest): logger.info(f"Before {request.node.name}") # type: ignore @@ -96,106 +104,89 @@ def test_create_wallet_random(self) -> None: """ Can create a random wallet. """ - e1: Exception | None = None - try: - config = MoneroWalletConfig() - wallet = self._create_wallet(config) - path = wallet.get_path() - e2: Exception | None = None - - try: - MoneroUtils.validate_address(wallet.get_primary_address(), TestUtils.NETWORK_TYPE) - MoneroUtils.validate_private_view_key(wallet.get_private_view_key()) - MoneroUtils.validate_private_spend_key(wallet.get_private_spend_key()) - MoneroUtils.validate_mnemonic(wallet.get_seed()) - if not isinstance(wallet, MoneroWalletRpc): - # TODO monero-wallet-rpc: get seed language - TestUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 + config = MoneroWalletConfig() + wallet = self._create_wallet(config) + path = wallet.get_path() + e2: Exception | None = None - # attempt to create wallet at same path - try: - config = MoneroWalletConfig() - config.path = path - self._create_wallet(config) - except Exception as e: - TestUtils.assert_equals("Wallet already exists: " + path, str(e)) + try: + MoneroUtils.validate_address(wallet.get_primary_address(), TestUtils.NETWORK_TYPE) + MoneroUtils.validate_private_view_key(wallet.get_private_view_key()) + MoneroUtils.validate_private_spend_key(wallet.get_private_spend_key()) + MoneroUtils.validate_mnemonic(wallet.get_seed()) + if not isinstance(wallet, MoneroWalletRpc): + # TODO monero-wallet-rpc: get seed language + TestUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) + except Exception as e: + e2 = e - # attempt to create wallet with unknown language - try: - config = MoneroWalletConfig() - config.language = "english" - self._create_wallet(config) - raise Exception("Should have thrown error") - except Exception as e: - TestUtils.assert_equals("Unknown language: english", str(e)) + self._close_wallet(wallet) + if e2 is not None: + raise e2 + # attempt to create wallet at same path + try: + config = MoneroWalletConfig() + config.path = path + self._create_wallet(config) except Exception as e: - e1 = e + TestUtils.assert_equals("Wallet already exists: " + path, str(e)) - if e1 is not None: - raise e1 + # attempt to create wallet with unknown language + try: + config = MoneroWalletConfig() + config.language = "english" + self._create_wallet(config) + raise Exception("Should have thrown error") + except Exception as e: + TestUtils.assert_equals("Unknown language: english", str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") def test_create_wallet_from_seed(self, test_config: BaseTestMoneroWallet.Config) -> None: - e1: Exception | None = None + # save for comparison + primary_address = self._wallet.get_primary_address() + private_view_key = self._wallet.get_private_view_key() + private_spend_key = self._wallet.get_private_spend_key() + + # recreate test wallet from seed + config = MoneroWalletConfig() + config.seed = TestUtils.SEED + config.restore_height = TestUtils.FIRST_RECEIVE_HEIGHT + + wallet: MoneroWallet = self._create_wallet(config) + path = wallet.get_path() + e2: Exception | None = None try: + TestUtils.assert_equals(primary_address, wallet.get_primary_address()) + TestUtils.assert_equals(private_view_key, wallet.get_private_view_key()) + TestUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) + TestUtils.assert_equals(TestUtils.SEED, wallet.get_seed()) + if not isinstance(wallet, MoneroWalletRpc): + TestUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) + except Exception as e: + e2 = e - # save for comparison - primary_address = self._wallet.get_primary_address() - private_view_key = self._wallet.get_private_view_key() - private_spend_key = self._wallet.get_private_spend_key() + self._close_wallet(wallet) + if e2 is not None: + raise e2 - # recreate test wallet from seed + # attempt to create wallet with two missing words + try: config = MoneroWalletConfig() - config.seed = TestUtils.SEED + config.seed = test_config.seed config.restore_height = TestUtils.FIRST_RECEIVE_HEIGHT - - wallet: MoneroWallet = self._create_wallet(config) - path = wallet.get_path() - e2: Exception | None = None - try: - TestUtils.assert_equals(primary_address, wallet.get_primary_address()) - TestUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - TestUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - TestUtils.assert_equals(TestUtils.SEED, wallet.get_seed()) - if not isinstance(wallet, MoneroWalletRpc): - TestUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 - - # attempt to create wallet with two missing words - try: - config = MoneroWalletConfig() - config.seed = test_config.seed - config.restore_height = TestUtils.FIRST_RECEIVE_HEIGHT - self._create_wallet(config) - except Exception as e: - TestUtils.assert_equals("Invalid mnemonic", str(e)) - - # attempt to create wallet at same path - try: - config = MoneroWalletConfig() - config.path = path - self._create_wallet(config) - raise Exception("Should have thrown error") - except Exception as e: - TestUtils.assert_equals("Wallet already exists: " + path, str(e)) - + self._create_wallet(config) except Exception as e: - e1 = e + TestUtils.assert_equals("Invalid mnemonic", str(e)) - if e1 is not None: - raise e1 + # attempt to create wallet at same path + try: + config = MoneroWalletConfig() + config.path = path + self._create_wallet(config) + raise Exception("Should have thrown error") + except Exception as e: + TestUtils.assert_equals("Wallet already exists: " + path, str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") def test_create_wallet_from_seed_with_offset(self) -> None: @@ -231,29 +222,54 @@ def test_create_wallet_from_seed_with_offset(self) -> None: @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") def test_create_wallet_from_keys(self) -> None: - e1: Exception | None = None + # save for comparison + primary_address = self._wallet.get_primary_address() + private_view_key = self._wallet.get_private_view_key() + private_spend_key = self._wallet.get_private_spend_key() + + # recreate test wallet from keys + config = MoneroWalletConfig() + config.primary_address = primary_address + config.private_view_key = private_view_key + config.private_spend_key = private_spend_key + config.restore_height = self._daemon.get_height() + wallet: MoneroWallet = self._create_wallet(config) + path = wallet.get_path() + e2: Exception | None = None try: - # save for comparison - primary_address = self._wallet.get_primary_address() - private_view_key = self._wallet.get_private_view_key() - private_spend_key = self._wallet.get_private_spend_key() + TestUtils.assert_equals(primary_address, wallet.get_primary_address()) + TestUtils.assert_equals(private_view_key, wallet.get_private_view_key()) + TestUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) + if not wallet.is_connected_to_daemon(): + # TODO monero-project: keys wallets not connected + logger.warning(f"WARNING: {self.CREATED_WALLET_KEYS_ERROR}") + TestUtils.assert_true(wallet.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) + if not isinstance(wallet, MoneroWalletRpc): + # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? + MoneroUtils.validate_mnemonic(wallet.get_seed()) + TestUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - # recreate test wallet from keys + except Exception as e: + e2 = e + + self._close_wallet(wallet) + if e2 is not None: + raise e2 + + # recreate test wallet from spend key + if not isinstance(wallet, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot create wallet from spend key? config = MoneroWalletConfig() - config.primary_address = primary_address - config.private_view_key = private_view_key config.private_spend_key = private_spend_key config.restore_height = self._daemon.get_height() - wallet: MoneroWallet = self._create_wallet(config) - path = wallet.get_path() - e2: Exception | None = None + wallet = self._create_wallet(config) + e2 = None try: TestUtils.assert_equals(primary_address, wallet.get_primary_address()) TestUtils.assert_equals(private_view_key, wallet.get_private_view_key()) TestUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) if not wallet.is_connected_to_daemon(): # TODO monero-project: keys wallets not connected - logger.warning(f"WARNING: {self.CREATED_WALLET_KEYS_ERROR}") + logger.warning(f"{self.CREATED_WALLET_KEYS_ERROR}") TestUtils.assert_true(wallet.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) if not isinstance(wallet, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? @@ -267,47 +283,14 @@ def test_create_wallet_from_keys(self) -> None: if e2 is not None: raise e2 - # recreate test wallet from spend key - if not isinstance(wallet, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot create wallet from spend key? - config = MoneroWalletConfig() - config.private_spend_key = private_spend_key - config.restore_height = self._daemon.get_height() - wallet = self._create_wallet(config) - e2 = None - try: - TestUtils.assert_equals(primary_address, wallet.get_primary_address()) - TestUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - TestUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - if not wallet.is_connected_to_daemon(): - # TODO monero-project: keys wallets not connected - logger.warning(f"{self.CREATED_WALLET_KEYS_ERROR}") - TestUtils.assert_true(wallet.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) - if not isinstance(wallet, MoneroWalletRpc): - # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? - MoneroUtils.validate_mnemonic(wallet.get_seed()) - TestUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 - - # attempt to create wallet at same path - try: - config = MoneroWalletConfig() - config.path = path - self._create_wallet(config) - raise Exception("Should have thrown error") - except Exception as e: - TestUtils.assert_equals("Wallet already exists: " + path, str(e)) - + # attempt to create wallet at same path + try: + config = MoneroWalletConfig() + config.path = path + self._create_wallet(config) + raise Exception("Should have thrown error") except Exception as e: - e1 = e - - if e1 is not None: - raise e1 + TestUtils.assert_equals("Wallet already exists: " + path, str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") def test_subaddress_lookahead(self) -> None: @@ -373,12 +356,12 @@ def test_get_path(self) -> None: self._close_wallet(wallet) def test_set_daemon_connection(self): - # create random wallet with default daemon connection config = MoneroWalletConfig() wallet = self._create_wallet(config) + daemon_rpc_uri = self.get_daemon_rpc_uri() connection = MoneroRpcConnection( - TestUtils.DAEMON_RPC_URI, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD + daemon_rpc_uri, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD ) TestUtils.assert_equals(connection, wallet.get_daemon_connection()) TestUtils.assert_true(wallet.is_connected_to_daemon()) # uses default localhost connection @@ -395,8 +378,8 @@ def test_set_daemon_connection(self): TestUtils.assert_false(wallet.is_connected_to_daemon()) # set daemon with wrong credentials - wallet.set_daemon_connection(TestUtils.DAEMON_RPC_URI, "wronguser", "wrongpass") - connection = MoneroRpcConnection(TestUtils.DAEMON_RPC_URI, "wronguser", "wrongpass") + wallet.set_daemon_connection(daemon_rpc_uri, "wronguser", "wrongpass") + connection = MoneroRpcConnection(daemon_rpc_uri, "wronguser", "wrongpass") TestUtils.assert_equals(connection, wallet.get_daemon_connection()) if "" == TestUtils.DAEMON_RPC_USERNAME: # TODO: monerod without authentication works with bad credentials? @@ -406,10 +389,10 @@ def test_set_daemon_connection(self): # set daemon with authentication wallet.set_daemon_connection( - TestUtils.DAEMON_RPC_URI, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD + daemon_rpc_uri, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD ) connection = MoneroRpcConnection( - TestUtils.DAEMON_RPC_URI, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD + daemon_rpc_uri, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD ) TestUtils.assert_equals(connection, wallet.get_daemon_connection()) TestUtils.assert_true(wallet.is_connected_to_daemon()) @@ -417,17 +400,18 @@ def test_set_daemon_connection(self): # nullify daemon connection wallet.set_daemon_connection(None) TestUtils.assert_equals(None, wallet.get_daemon_connection()) - wallet.set_daemon_connection(TestUtils.DAEMON_RPC_URI) - connection = MoneroRpcConnection(TestUtils.DAEMON_RPC_URI) + wallet.set_daemon_connection(daemon_rpc_uri) + connection = MoneroRpcConnection(daemon_rpc_uri) TestUtils.assert_equals(connection, wallet.get_daemon_connection()) wallet.set_daemon_connection(None) TestUtils.assert_equals(None, wallet.get_daemon_connection()) # set daemon uri to non-daemon - wallet.set_daemon_connection("www.getmonero.org") - connection = MoneroRpcConnection("www.getmonero.org") - TestUtils.assert_equals(connection, wallet.get_daemon_connection()) - TestUtils.assert_false(wallet.is_connected_to_daemon()) + if not TestUtils.IN_CONTAINER: # TODO sometimes this fails in container... + wallet.set_daemon_connection("www.getmonero.org") + connection = MoneroRpcConnection("www.getmonero.org") + TestUtils.assert_equals(connection, wallet.get_daemon_connection()) + TestUtils.assert_false(wallet.is_connected_to_daemon()) # set daemon to invalid uri wallet.set_daemon_connection("abc123") diff --git a/tests/test_monero_wallet_rpc.py b/tests/test_monero_wallet_rpc.py index c2ea326..854e8c3 100644 --- a/tests/test_monero_wallet_rpc.py +++ b/tests/test_monero_wallet_rpc.py @@ -27,11 +27,16 @@ def _open_wallet(self, config: MoneroWalletConfig | None) -> MoneroWallet: @override def _create_wallet(self, config: MoneroWalletConfig) -> MoneroWallet: - return Utils.create_wallet_rpc(config) + try: + return Utils.create_wallet_rpc(config) + except Exception as e: + Utils.free_wallet_rpc_resources() + raise e @override def _close_wallet(self, wallet: MoneroWallet, save: bool = False) -> None: wallet.close(save) + Utils.free_wallet_rpc_resource(wallet) @override def _get_seed_languages(self) -> list[str]: @@ -42,8 +47,13 @@ def _get_seed_languages(self) -> list[str]: def before_each(self, request: pytest.FixtureRequest): logger.info(f"Before test {request.node.name}") # type: ignore yield + Utils.free_wallet_rpc_resources() logger.info(f"After test {request.node.name}") # type: ignore + @override + def get_daemon_rpc_uri(self) -> str: + return Utils.DAEMON_RPC_URI if not Utils.IN_CONTAINER else Utils.CONTAINER_DAEMON_RPC_URI + #endregion #region Tests @@ -84,36 +94,11 @@ def test_get_public_view_key(self): def test_get_public_spend_key(self): return super().test_get_public_spend_key() - @pytest.mark.skip(reason="TODO") - @override - def test_create_wallet_random(self) -> None: - return super().test_create_wallet_random() - - @pytest.mark.skip(reason="TODO") - @override - def test_create_wallet_from_seed(self, test_config: BaseTestMoneroWallet.Config) -> None: - return super().test_create_wallet_from_seed(test_config) - - @pytest.mark.skip(reason="TODO") - @override - def test_create_wallet_from_keys(self) -> None: - return super().test_create_wallet_from_keys() - - @pytest.mark.skip(reason="TODO") - @override - def test_create_wallet_from_seed_with_offset(self) -> None: - return super().test_create_wallet_from_seed_with_offset() - @pytest.mark.skip(reason="TODO") @override def test_wallet_equality_ground_truth(self): return super().test_wallet_equality_ground_truth() - @pytest.mark.skip(reason="TODO") - @override - def test_get_path(self) -> None: - return super().test_get_path() - @pytest.mark.skip(reason="TODO") @override def test_get_payment_uri(self): @@ -129,11 +114,6 @@ def test_set_tx_note(self) -> None: def test_set_tx_notes(self): return super().test_set_tx_notes() - @pytest.mark.skip(reason="TODO implement TestUtils.create_wallet_rpc()") - @override - def test_set_daemon_connection(self): - return super().test_set_daemon_connection() - @pytest.mark.skip(reason="TODO") @override def test_export_key_images(self): @@ -154,21 +134,6 @@ def test_get_new_key_images_from_last_import(self): def test_subaddress_lookahead(self) -> None: return super().test_subaddress_lookahead() - @pytest.mark.skip(reason="TODO") - @override - def test_set_account_label(self): - return super().test_set_account_label() - - @pytest.mark.skip(reason="TODO") - @override - def test_set_subaddress_label(self): - return super().test_set_subaddress_label() - - @pytest.mark.skip(reason="TODO") - @override - def test_get_subaddresses_by_indices(self): - return super().test_get_subaddresses_by_indices() - @pytest.mark.skip(reason="TODO") @override def test_get_subaddress_address_out_of_range(self): diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index 8b92a40..f1147f1 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -31,18 +31,26 @@ class TestUtils(ABC): __test__ = False _LOADED: bool = False - IN_CONTAINER: bool = False + IN_CONTAINER: bool = True """indicates if tests are running in docker container""" MIN_BLOCK_HEIGHT: int = 0 """min blockchain height for tests""" - MINING_ADDRESS: str = "" WALLET_PORT_OFFSETS: dict[MoneroWalletRpc, int] = {} + # objects cache _WALLET_FULL: Optional[MoneroWalletFull] = None + """Default wallet full used for tests""" _WALLET_KEYS: Optional[MoneroWalletKeys] = None + """Default wallet keys used for tests""" _WALLET_RPC: Optional[MoneroWalletRpc] = None + """Default wallet rpc used for tests""" _DAEMON_RPC: Optional[MoneroDaemonRpc] = None + """Default daemon rpc used for tests""" + _WALLET_RPC_2: Optional[MoneroWalletRpc] = None + """Additional wallet rpc instance""" + DAEMON_RPC_URI: str = "" + CONTAINER_DAEMON_RPC_URI: str = "" """monero daemon rpc endpoint configuration (change per your configuration)""" DAEMON_RPC_USERNAME: str = "" DAEMON_RPC_PASSWORD: str = "" @@ -63,6 +71,7 @@ class TestUtils(ABC): WALLET_RPC_ZMQ_DOMAIN: str = "" WALLET_RPC_DOMAIN: str = "" WALLET_RPC_URI: str = "" + WALLET_RPC_URI_2: str = "" WALLET_RPC_ZMQ_URI: str = "" WALLET_RPC_ACCESS_CONTROL_ORIGINS: str = "" """cors access from web browser""" @@ -91,6 +100,16 @@ class TestUtils(ABC): """dummy server uri to remain offline because wallet2 connects to default if not given""" AUTO_CONNECT_TIMEOUT_MS: int = 3000 + # mining wallet config + MINING_WALLET_NAME: str = "" + MINING_WALLET_PASSWORD: str = "" + MINING_SEED: str = "" + MINING_ADDRESS: str = "" + MINING_PRIVATE_VIEW_KEY: str = "" + MINING_PRIVATE_SPEND_KEY: str = "" + MINING_PUBLIC_SPEND_KEY: str = "" + MINING_PUBLIC_VIEW_KEY: str = "" + @classmethod def load_config(cls) -> None: """ @@ -115,7 +134,6 @@ def load_config(cls) -> None: cls.AUTO_CONNECT_TIMEOUT_MS = parser.getint('general', 'auto_connect_timeout_ms') cls.NETWORK_TYPE = cls.parse_network_type(nettype_str) cls.REGTEST = cls.is_regtest(nettype_str) - cls.MINING_ADDRESS = parser.get('general', 'mining_address') cls.WALLET_TX_TRACKER = WalletTxTracker(cls.MINING_ADDRESS) if cls.REGTEST: @@ -123,6 +141,7 @@ def load_config(cls) -> None: # parse daemon config cls.DAEMON_RPC_URI = parser.get('daemon', 'rpc_uri') + cls.CONTAINER_DAEMON_RPC_URI = cls.DAEMON_RPC_URI.replace("127.0.0.1", "node_2") cls.DAEMON_RPC_USERNAME = parser.get('daemon', 'rpc_username') cls.DAEMON_RPC_PASSWORD = parser.get('daemon', 'rpc_password') @@ -149,10 +168,22 @@ def load_config(cls) -> None: cls.WALLET_RPC_ZMQ_BIND_PORT_START = parser.getint('wallet', 'rpc_zmq_bind_port_start') cls.WALLET_RPC_ZMQ_DOMAIN = parser.get('wallet', 'rpc_zmq_domain') cls.WALLET_RPC_URI = cls.WALLET_RPC_DOMAIN + ":" + str(cls.WALLET_RPC_PORT_START) + cls.WALLET_RPC_URI_2 = cls.WALLET_RPC_DOMAIN + ":" + str(cls.WALLET_RPC_PORT_START + 1) cls.WALLET_RPC_ZMQ_URI = "tcp:#" + cls.WALLET_RPC_ZMQ_DOMAIN + ":" + str(cls.WALLET_RPC_ZMQ_PORT_START) cls.SYNC_PERIOD_IN_MS = parser.getint('wallet', 'sync_period_in_ms') - in_container = getenv("IN_CONTAINER", "false") + in_container = getenv("IN_CONTAINER", "true") cls.IN_CONTAINER = in_container.lower() == "true" or in_container == "1" + + # parse mining wallet config + cls.MINING_WALLET_NAME = parser.get('mining_wallet', 'name') + cls.MINING_WALLET_PASSWORD = parser.get('mining_wallet', 'password') + cls.MINING_ADDRESS = parser.get('mining_wallet', 'address') + cls.MINING_PRIVATE_VIEW_KEY = parser.get('mining_wallet', 'private_view_key') + cls.MINING_PRIVATE_SPEND_KEY = parser.get('mining_wallet', 'private_spend_key') + cls.MINING_PUBLIC_VIEW_KEY = parser.get('mining_wallet', 'public_view_key') + cls.MINING_PUBLIC_SPEND_KEY = parser.get('mining_wallet', 'public_spend_key') + cls.MINING_SEED = parser.get('mining_wallet', 'seed') + cls._LOADED = True @classmethod @@ -374,12 +405,100 @@ def get_wallet_rpc(cls) -> MoneroWalletRpc: return cls._WALLET_RPC @classmethod - def open_wallet_rpc(cls, config: Optional[MoneroWalletConfig]) -> MoneroWalletRpc: - raise NotImplementedError("Utils.open_wallet_rpc(): not implemented") + def open_wallet_rpc(cls, c: Optional[MoneroWalletConfig]) -> MoneroWalletRpc: + config = c if c is not None else MoneroWalletConfig() + + # assign defaults + if config.password is None: + config.password = cls.WALLET_PASSWORD + + if config.server is None: + config.server = cls.get_daemon_rpc().get_rpc_connection() + if cls.IN_CONTAINER: + config.server.uri = "http://node_2:18081" + + if cls._WALLET_RPC_2 is not None: + raise Exception(f"Cannot open wallet rpc: no resources left") + + if cls._WALLET_RPC_2 is None: + rpc = MoneroRpcConnection( + cls.WALLET_RPC_URI_2, cls.WALLET_RPC_USERNAME, cls.WALLET_RPC_PASSWORD, + cls.WALLET_RPC_ZMQ_URI if cls.WALLET_RPC_ZMQ_ENABLED else '' + ) + cls._WALLET_RPC_2 = MoneroWalletRpc(rpc) + + # open wallet + cls._WALLET_RPC_2.stop_syncing() + cls._WALLET_RPC_2.open_wallet(config) + # TODO set trusted daemon connection + # cls._WALLET_RPC_2.set_daemon_connection() + if cls._WALLET_RPC_2.is_connected_to_daemon(): + cls._WALLET_RPC_2.start_syncing(TestUtils.SYNC_PERIOD_IN_MS) + + return cls._WALLET_RPC_2 + + @classmethod + def create_wallet_rpc(cls, c: Optional[MoneroWalletConfig]) -> MoneroWalletRpc: + # assign defaults + config = c if c is not None else MoneroWalletConfig() + random = config.seed is None and config.primary_address is None + + if config.path is None: + config.path = TestUtils.get_random_string() + + if config.password is None: + config.password = TestUtils.WALLET_PASSWORD + + if config.restore_height is None and not random: + config.restore_height = 0 + + if config.server is None: + config.server = TestUtils.get_daemon_rpc().get_rpc_connection() + if cls.IN_CONTAINER: + # TODO make this configurable + config.server.uri = "http://node_2:18081" + + # create client connected to monero-wallet-rpc process + wallet_rpc = cls._WALLET_RPC_2 + if wallet_rpc is not None: + raise Exception(f"Cannot open wallet rpc: no resources left") + + if wallet_rpc is None: + rpc = MoneroRpcConnection( + cls.WALLET_RPC_URI_2, cls.WALLET_RPC_USERNAME, cls.WALLET_RPC_PASSWORD, + cls.WALLET_RPC_ZMQ_URI if cls.WALLET_RPC_ZMQ_ENABLED else '' + ) + wallet_rpc = MoneroWalletRpc(rpc) + + # create wallet + wallet_rpc.stop_syncing() + wallet_rpc.create_wallet(config) + # TODO set trusted daemon connection + # cls._WALLET_RPC_2.set_daemon_connection() + if wallet_rpc.is_connected_to_daemon(): + wallet_rpc.start_syncing(TestUtils.SYNC_PERIOD_IN_MS) + + cls._WALLET_RPC_2 = wallet_rpc + return cls._WALLET_RPC_2 + + @classmethod + def free_wallet_rpc_resources(cls) -> None: + if cls._WALLET_RPC_2 is not None: + try: + cls._WALLET_RPC_2.close() + except Exception as e: + pass + cls._WALLET_RPC_2 = None + + @classmethod + def is_wallet_rpc_resource(cls, wallet: MoneroWallet) -> bool: + return wallet is cls._WALLET_RPC_2 @classmethod - def create_wallet_rpc(cls, config: MoneroWalletConfig) -> MoneroWalletRpc: - raise NotImplementedError("Utils.create_wallet_rpc(): not implemented") + def free_wallet_rpc_resource(cls, wallet: MoneroWallet) -> None: + if cls.is_wallet_rpc_resource(wallet): + # TODO free specific wallet rpc resource + cls.free_wallet_rpc_resources() @classmethod def create_wallet_ground_truth( @@ -1062,7 +1181,8 @@ def assert_connection_equals(cls, c1: Optional[MoneroRpcConnection], c2: Optiona assert c1 is not None assert c2 is not None - assert c1.uri == c2.uri + if not cls.IN_CONTAINER: # TODO + assert c1.uri == c2.uri assert c1.username == c2.username assert c1.password == c2.password