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

wallet2: check wallet compatibility with daemon's hard fork version [release] #8544

Merged

Conversation

j-berman
Copy link
Collaborator

Overview

Since the fork, there have been a number of support requests caused by pre-fork clients pointing to post-fork daemons and vice versa. This PR ensures hard fork compatibility between client and daemon, and paves the way for a cleaner UX when client and daemon are incompatible. Credit to reddit user u/MoneroArbo for the catalyst to this PR:

post 0.18 wallets should at least warn you when a node reports a pre-HF version :(

Implementation

  1. If an updated wallet tries to connect to a daemon that has not updated for the latest hard fork AND the chain height has passed the fork height, the wallet will fail to connect to that daemon and error out.

  2. If a wallet has not updated for the latest hard fork and tries to connect to an updated daemon AND the chain height has passed the fork height, the wallet will fail to connect to that daemon and error out.

  3. If an updated wallet connects to a daemon that has not updated for the latest fork when the fork height has not yet passed, and then the fork height passes while the wallet is still connected, the wallet will error out when scanning blocks and give a clean error indicating the daemon has not updated.

  4. If a wallet has not updated for the latest fork and connects to an updated daemon when the fork height has not yet passed, and then the fork height passes while the wallet is still connected, the wallet will error out when scanning blocks and give a clean error indicating the wallet has not updated.

Reasoning

I think this versioning approach makes sense over using the RPC versioning system as-is because this approach allows wallets to function smoothly while connecting to incompatible daemons until the hard fork height. It also sets up a nice way for wallets to warn that a hard fork is coming in X blocks/days (though this PR does not give that warning), and to tell users to update either their daemon or wallet as needed, rather than immediately break wallets connecting to incompatible daemons upon release.

/**
* @brief returns info for all known hard forks
*/
std::vector<hardfork_t> get_hardforks() const { return heights; }
Copy link
Contributor

Choose a reason for hiding this comment

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

It's more efficient to return const std::vector<hardfork_t>& here.

*
* @return the hardforks
*/
std::vector<hardfork_t> get_hardforks() const { return m_hardfork->get_hardforks(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same, it's more efficient to return const std::vector<hardfork_t>& here.

@SChernykh
Copy link
Contributor

@j-berman looks good. Did you test it on a real v0.17 node?

@j-berman
Copy link
Collaborator Author

j-berman commented Sep 7, 2022

@SChernykh yep :) here's one: http://support.ablative.hosting:18081

I manually tested every code path on real nodes where possible, and my own modified node where not

@j-berman
Copy link
Collaborator Author

j-berman commented Sep 9, 2022

Rebased

m_rpc_version = resp_t.version;
for (const auto &hf : resp_t.hard_forks)
m_daemon_hard_forks.push_back(std::make_pair(hf.hf_version, hf.height));
Copy link
Contributor

Choose a reason for hiding this comment

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

If this method is called multiple times on the same object, then it's m_daemon_hard_forks field will keep expanding indefinitely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added m_daemon_hard_forks.clear() above the for loop for better defense

Comment on lines 2149 to 2151
KV_SERIALIZE(current_height)
KV_SERIALIZE(target_height)
KV_SERIALIZE(hard_forks)
Copy link
Contributor

@jeffro256 jeffro256 Sep 12, 2022

Choose a reason for hiding this comment

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

COMMAND_RPC_GET_VERSION::response_t packets serialized by pre-PR daemons will not be able to be deserialized by post-PR wallets (node_rpc_proxy specifically). I would supply some optional values through KV_SERIALIZE_OPT to maintain backwards compatibility.

Edit: I would also rename entry b/c it's not descriptive (I don't immediately think hard fork height/version pair in the context of getting the version), and other commands have a ::entry member type. Even if it's just something like hf_entry.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I would supply some optional values through KV_SERIALIZE_OPT

I agree this seems safer, see the latest.

FWIW before this change, I had tested a wallet running the PR's code pointing to pre-PR daemons and vice versa; it worked fine deserializing with 0 values thanks to this. But I prefer handling it more explicitly as you suggested.

@luigi1111 luigi1111 merged commit 0be63cf into monero-project:release-v0.18 Sep 15, 2022
{
if (!silent)
{
if (m_wallet->is_offline())
fail_msg_writer() << tr("wallet failed to connect to daemon, because it is set to offline mode");
else if (wallet_is_outdated)
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems all errors always take this branch, the elseif/else below won't activate. I tried using a v0.18 wallet on a couple v0.17 nodes as well (including the one linked: http://support.ablative.hosting:18081), same thing.

These all return the old wallet error:

  • monerod online, but it's v0.17.x.x
  • monerod online, but connected to wrong port
  • monerod offline, either local or remote
  • random fake ip: 1.2.3.4:18081
  • bad ip: 1:18081 (returns wallet outdated error after hanging for a bit)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you @hinto-janaiyo . Should be fixed in #8585

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

Successfully merging this pull request may close these issues.

5 participants