Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple-URI support in parse_uri and make_uri #9756

Open
wants to merge 54 commits into
base: master
Choose a base branch
from

Conversation

U65535F
Copy link

@U65535F U65535F commented Jan 30, 2025

This pull request addresses the issues raised in #7737 and introduces a new feature to the Monero codebase: multi-URL support for parse_uri and make_uri functions.

Key Changes:

  • Fixed loop index misuse (replaced i with j for proper iteration).
  • Removed the usage of NA as a filler in certain cases.
  • General code cleanup for readability and maintainability.
  • Introduced new test cases in uri.cpp to validate multi-URL functionality.

Notes:
This PR was rewritten from the latest Monero repository rather than being forked from the original issue (#7737).
All builds are successfully passing, and existing tests have cleared without any regressions.

Please check for any overlooked issues or mistakes, especially in the multi-URL parsing logic.
Validate the new test cases and suggest improvements to ensure robustness.

@U65535F U65535F changed the title Multiple-URL Multiple-URI support in parse_uri and make_uri Jan 30, 2025
@U65535F
Copy link
Author

U65535F commented Jan 31, 2025

@selsta

@selsta
Copy link
Collaborator

selsta commented Jan 31, 2025

Thank you. The URI functional test case fails now. Can you take a look?

https://github.com/monero-project/monero/actions/runs/13057491252/job/36433416280?pr=9756#step:11:1471

@U65535F
Copy link
Author

U65535F commented Jan 31, 2025

Alright, will ping you when it's done. It seems like I didn't update the python source files to comply with the new updated C++ code.

@iamamyth
Copy link

As illustrated by the test failures, this PR would introduce a backwards-incompatible change to the wallet RPC interface, which generally should not be done.

@iamamyth
Copy link

Two possible ways to address the compatibility issue:

  1. Introduce a new version of the endpoint which works with only the new format, retaining the old one (you could still transform requests to the old endpoint into the new format upon receipt and share the address construction code).
  2. Modify the endpoint to detect "old format" and "new format" requests, and pick the appropriate logic (e.g. payments field present implies new format, or address field present implies old format).

@U65535F U65535F marked this pull request as draft February 1, 2025 02:29
…allet.py for handling both new and old formats plus rewrite uri.py to handle tests properly. Rewrite was necessary to remove all broken tests written previously, restore the file and modify the way it accesses data (the structure is different).
@U65535F
Copy link
Author

U65535F commented Feb 11, 2025

@selsta @iamamyth I think I have addressed your compatibility issue properly. Introduced v2 versions of parse_uri and make_uri into the RPC. While the C++ code does not explicitly have those functions, but there are overloads which will select the version based on the parameters (as mentioned by @iamamyth). The tests are restored to their original versions, except for there are new tests for multi-URI functionality. The tests are passing, but there seems be to the p2p test which is failing (due to the recent PR merges I guess) which is not linked to this PR. The tests are passing in my fork though. So that confirms it.

Copy link

@SNeedlewoods SNeedlewoods left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, not sure what exactly I should look at.
I haven't done a review, just skimmed over the changes and noticed some unnecessary edits, mainly in tests/functional_tests/uri.py.
Other than that I left a comment regarding the API.

Maybe you can clarify how I can help?

@@ -214,7 +214,9 @@ class WalletImpl : public Wallet
virtual void startRefresh() override;
virtual void pauseRefresh() override;
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override;
virtual bool parse_uri(const std::string &uri, std::vector<tools::wallet2::uri_data> &data, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see two issues here:

  1. AFAICT we don't want to have "non-standard" types as arguments for methods in the Wallet API, so for simplicity I'd change std::vector<tools::wallet2::uri_data> &data to vectors of std::string/std::uint64_t here, see example below.
  2. This belongs to the interface in src/wallet/api/wallet2_api.h and there it should look like this (with respect to the proposed change from point 1):
virtual bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error) = 0;

And here it should look like this (no virtual in the beginning but override at the end):

bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error) override;

Same goes for make_uri() below.

@iamamyth
Copy link

@SNeedlewoods The reason I suggested pinging you was for your feedback on the wallet API changes, as you've put in some effort to establish a coherent interface. And, it seems your comments address those changes, as hoped. Thanks.

@iamamyth
Copy link

Please note that I have no interest in formally reviewing/approving this PR, so, if/when it gains the necessary approvals, there's no sense in pinging me.

@U65535F
Copy link
Author

U65535F commented Feb 12, 2025

Sorry, not sure what exactly I should look at. I haven't done a review, just skimmed over the changes and noticed some unnecessary edits, mainly in tests/functional_tests/uri.py. Other than that I left a comment regarding the API.

Maybe you can clarify how I can help?

@SNeedlewoods What exactly are those unnecessary edits? Could you please list them so I can remove.

@@ -214,7 +214,9 @@ class WalletImpl : public Wallet
virtual void startRefresh() override;
virtual void pauseRefresh() override;
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override;
virtual bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error) override;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be no virtual keyword in the front here (it should only be used in the interface class src/wallet/api/wallet2_api.h (here you can find some general information about "pure virtual function" and "interface class")), IMO it's unfortunate that many methods in src/wallet/api/wallet.h already have this wrong, but for example these methods are considered to do it the "right way":

Device getDeviceType() const override;
bool close(bool store = true);
std::string seed(const std::string& seed_offset = "") const override;
std::string getSeedLanguage() const override;
void setSeedLanguage(const std::string &arg) override;
// void setListener(Listener *) {}
int status() const override;
std::string errorString() const override;
void statusWithErrorString(int& status, std::string& errorString) const override;
bool setPassword(const std::string &password) override;
const std::string& getPassword() const override;
bool setDevicePin(const std::string &password) override;
bool setDevicePassphrase(const std::string &password) override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
std::string integratedAddress(const std::string &payment_id) const override;
std::string secretViewKey() const override;
std::string publicViewKey() const override;
std::string secretSpendKey() const override;
std::string publicSpendKey() const override;
std::string publicMultisigSignerKey() const override;
std::string path() const override;
void stop() override;
bool store(const std::string &path) override;
std::string filename() const override;
std::string keysFilename() const override;
bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false, const std::string &proxy_address = "") override;
bool connectToDaemon() override;
ConnectionStatus connected() const override;
void setTrustedDaemon(bool arg) override;
bool trustedDaemon() const override;
bool setProxy(const std::string &address) override;
uint64_t balance(uint32_t accountIndex = 0) const override;
uint64_t unlockedBalance(uint32_t accountIndex = 0) const override;
uint64_t blockChainHeight() const override;
uint64_t approximateBlockChainHeight() const override;
uint64_t estimateBlockChainHeight() const override;
uint64_t daemonBlockChainHeight() const override;
uint64_t daemonBlockChainTargetHeight() const override;
bool synchronized() const override;
bool refresh() override;
void refreshAsync() override;
bool rescanBlockchain() override;
void rescanBlockchainAsync() override;
void setAutoRefreshInterval(int millis) override;
int autoRefreshInterval() const override;
void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override;
uint64_t getRefreshFromBlockHeight() const override { return m_wallet->get_refresh_from_block_height(); };
void setRecoveringFromSeed(bool recoveringFromSeed) override;
void setRecoveringFromDevice(bool recoveringFromDevice) override;
void setSubaddressLookahead(uint32_t major, uint32_t minor) override;
bool watchOnly() const override;
bool isDeterministic() const override;
bool rescanSpent() override;
NetworkType nettype() const override {return static_cast<NetworkType>(m_wallet->nettype());}
void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override;
bool useForkRules(uint8_t version, int64_t early_blocks) const override;

Again same for make_uri() below.

@@ -1066,8 +1066,10 @@ struct Wallet
virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0;

virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
virtual bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
virtual bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error);
virtual bool parse_uri(const std::string &uri, std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, std::string &payment_id, std::string &tx_description, std::vector<std::string> &unknown_parameters, std::string &error) = 0;

= 0 at the end for "pure virtual" functions

virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0;

virtual std::string make_uri(std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, const std::string &payment_id, const std::string &tx_description, std::string &error) const;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
virtual std::string make_uri(std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, const std::string &payment_id, const std::string &tx_description, std::string &error) const;
virtual std::string make_uri(std::vector<std::string> &addresses, std::vector<std::uint64_t> &amounts, std::vector<std::string> &recipient_names, const std::string &payment_id, const std::string &tx_description, std::string &error) const = 0;

Comment on lines 87 to 92
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 0
assert res.uri.tx_description == ''
assert res.uri.recipient_name == ''
assert res.uri.payment_id == ''
assert res.uri.tx_description == ''

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several places where the order of asserts is changed, without changing the logic, these I'd consider unnecessary changes.

bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error)
bool WalletImpl::parse_uri(const std::string& uri, std::string& address, std::string& payment_id, uint64_t& amount, std::string& tx_description, std::string& recipient_name, std::vector<std::string>& unknown_parameters, std::string& error)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say this is also unnecessary

@U65535F
Copy link
Author

U65535F commented Feb 12, 2025

@SNeedlewoods I think I've addressed your issues and fixed them. Please check again if you are willing to, and tell me if I missed anything. As it seems, I've missed 2 unnecessary edits in uri.py which I will tackle in newer commits.

@SNeedlewoods
Copy link

The things I addressed look good to me now. Thanks for your contribution.

I want to make clear though, I haven't looked into changes in wallet2, simplewallet, wallet_rpc_server or the tests, because I'm not as familiar with those areas and would need way more time for a proper review.

@U65535F
Copy link
Author

U65535F commented Feb 13, 2025

@selsta How do I get more people to review it?

@U65535F
Copy link
Author

U65535F commented Feb 13, 2025

@selsta ping

@selsta
Copy link
Collaborator

selsta commented Feb 13, 2025

I'm busy with the current release, will be able to take a look afterwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants