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

feat(utxo): prioritize electrum connections #1966

Merged
merged 206 commits into from
Oct 25, 2024
Merged

Conversation

rozhkovdmitrii
Copy link

@rozhkovdmitrii rozhkovdmitrii commented Sep 11, 2023

This PR refactors the electrum part of the RPC clients. And also introduces a new feature related to connection management decisions.

Within the electrum enable request, one could now specify two parameters (min_connected & max_connected).

  • min_connected: KDF needs to keep at least min_connected working active (maintained) connections at all times.
  • max_connected: KDF needs to keep at most max_connected working active (maintained) connections at any time.
  • To achieve the selective/single-server effect set these parameters to (1, 1)
  • To achieve the multiple/all-servers effect, set these parameters to (1, len(servers)) (or don't set at all, as this is used as the default)
    • The connection manager will initially try to connect to a maximum of len(servers) connections and keep them maintained. As some connections get lost for whatever reason, they will not be retried right away. Retries only take place:
      • every 5 minutes.
      • if we fall below min_connected, which is 1 in this case.
    • So one could use (10, 68) for instance to make sure we are connected to at least 10 servers at all times, and if we ever fall below 10 we fire another round of reconnections. After this round we are guaranteed to have <= 68 connections.
      • e.g. we have 10 connections now, we lose 1 and have 9 now. A round of reconnections start and we end up with some number of active connections between 9 (assuming only our 9 connected servers were the non-faulty ones) and 68. If after the reconnections round we still fall below 10=min_connected connections, we keep retrying.
  • These parameters must satisfy: 0 < min_connected <= max_connected <= len(servers)

The connection manager maintains these maintained (working active) connections and these are the ones used for any electrum request (they are all used concurrently and whichever returns the success result first is considered the response for this request).

For requests directed for specific servers (like server_version, get_block_count_from, etc...), the connections to these servers are established first if they are not in the maintained servers set and then the request is performed. So even if the server isn't maintained/active, it could still be queried.

The servers order in the electrum enable request encode an implicit priority (previously in this PR, was encoded as primary & secondary). Servers that appear first in the list are assumed to be more robust and thus have higher priority compared to servers after.

Since we do an altruistic round of retries every 10 minutes, if we ever had the case were a high priority server was down and thus wasn't maintained by us and now is back online, we will prefer maintaining it over another already-maintained low priority server (this is assuming the maintained servers set is full, i.e. len(maintained server) == min_connected). Simply put, high priority servers will kick out low priority ones when they come back online if we have no room to fit both.

Testing

Without any changes to the former request format, the multiple effect takes place (i.e. KDF will try to connect to all servers and keep these connections maintained).
In addition to the former request format there are two new fields, min_connected & max_connected mentioned above.

example request:

{
    "userpass": "{{userpass}}",
    "method": "electrum",
    "coin": "DOC",
    "servers": [
        {
            "url": "electrum1.cipig.net:10020"
        },
        {
            "url": "electrum2.cipig.net:10020"
        },
        {
            "url": "electrum3.cipig.net:10020"
        }
    ],
    // any of the following parameters could be omitted and it will be set to the default
    "min_connected": 1, // defaults to 1 when omitted
    "max_connected": 3  // defaults to len(servers) when omitted
}

response:

{
    "result": "success",
    "address": "address",
    "balance": "0",
    "unspendable_balance": "0",
    "coin": "DOC",
    "required_confirmations": 1,
    "requires_notarization": false,
    "mature_confirmations": 100
}

Addresses #791

@rozhkovdmitrii rozhkovdmitrii added the in progress Changes will be made from the author label Sep 11, 2023
@rozhkovdmitrii rozhkovdmitrii self-assigned this Sep 11, 2023
@onur-ozkan onur-ozkan changed the title feat(utxo): prioritize electerum nodes connection feat(utxo): prioritize electrum connections Sep 12, 2023
@shamardy shamardy removed the request for review from onur-ozkan September 12, 2023 08:26
Copy link
Member

@onur-ozkan onur-ozkan left a comment

Choose a reason for hiding this comment

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

Thank you for your effort!

The PR seems to be in its early stages right now. This is not an actual review, I just did a quick review to point out some areas that should not be forgotten.

README.md Outdated Show resolved Hide resolved
mm2src/adex_cli/src/tests/mod.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
mm2src/gossipsub/src/behaviour.rs Outdated Show resolved Hide resolved
mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@shamardy shamardy left a comment

Choose a reason for hiding this comment

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

Since this is still in progress, I left some comments that can help you improve the code.
I will do a more thorough review when this is ready for review!

Please also merge with latest dev and start adding doc comments.

pub struct ElectrumClientImpl {
weak_self: Mutex<Option<Weak<ElectrumClientImpl>>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a very bad design choice, the struct should never reference it self to not create any unstable behavior.

Copy link
Author

Choose a reason for hiding this comment

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

I'm sorry, what unstable behavior can there be if we use Weak reference? The only purpose of this weak_self is to be spawned as an argument of self hosted futures, e.g. server_ping

In the previous implementation, the corresponding code was where it shouldn't be [1, 2]

Copy link
Member

Choose a reason for hiding this comment

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

why not just introduce a new struct ? e.g

struct ElectrumClientImplWeak(Mutex<Option<Weak<ElectrumClientImpl>>>)

mm2src/adex_cli/src/rpc_data.rs Outdated Show resolved Hide resolved
mm2src/coins/utxo/rpc_clients.rs Outdated Show resolved Hide resolved
mm2src/coins/lp_coins.rs Outdated Show resolved Hide resolved
struct ConnMng(Arc<ConnMngImpl>);

impl ConnMng {
async fn suspend_server(self, address: String) -> Result<(), String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is a new implementation, please start using specific error types and MmError

Copy link
Author

Choose a reason for hiding this comment

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

Comment on lines 2751 to 2754
spawner.spawn(async move {
let state = conn_mng.0.guarded.lock().await;
state.connecting.store(false, AtomicOrdering::Relaxed);
})
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please elaborate on this more? ConnectingStateCtx will be considered dropped before connecting is set to false if the lock was held by other operation for sometime.

Copy link
Author

Choose a reason for hiding this comment

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

Certainly should have been more detailed! Thanks for pointing this out! This place is the heart of selective connectivity.

done

Comment on lines 2732 to 2738
let address: String = {
if address.is_some() {
address.map(|internal| internal.to_string())
} else {
guard.active.as_ref().cloned()
}
}?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let address: String = {
if address.is_some() {
address.map(|internal| internal.to_string())
} else {
guard.active.as_ref().cloned()
}
}?;
let address = address.map_or_else(|| guard.active.as_ref().cloned(), |internal| Some(internal.to_string()))?;

mm2src/coins/utxo/rpc_clients.rs Outdated Show resolved Hide resolved
mm2src/coins/utxo/rpc_clients.rs Outdated Show resolved Hide resolved
mm2src/coins/utxo/rpc_clients.rs Outdated Show resolved Hide resolved
this carried over from the usage of these fields in past impl. there is no reason to have these mutexes async. sync mutexs are better for performance.
one mutex is left async though (establishing_connection) to not cpu-block other threads when they are waiting on the same connection to be established.
Copy link
Member

@cipig cipig left a comment

Choose a reason for hiding this comment

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

all issues from my previous comments are fixed

"internet connection lost" case tested too... all connection were reestablished after internet came back

Copy link
Member

@borngraced borngraced left a comment

Choose a reason for hiding this comment

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

Really great work! last review notes from me

mm2src/adex_cli/src/rpc_data.rs Outdated Show resolved Hide resolved
mm2src/coins/utxo.rs Outdated Show resolved Hide resolved
mm2src/mm2_main/src/rpc.rs Outdated Show resolved Hide resolved
and avoid lots of conversions
Copy link
Member

@borngraced borngraced left a comment

Choose a reason for hiding this comment

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

are the changes in adex_cli required? @mariocynicys ... otherwise this looks good to go! 🚀

@mariocynicys
Copy link
Collaborator

are the changes in adex_cli required?

im adding the min & max connected there just in case if we re-use it again the thing works as expected.

borngraced
borngraced previously approved these changes Oct 18, 2024
@shamardy shamardy merged commit f487947 into dev Oct 25, 2024
21 of 25 checks passed
@shamardy shamardy deleted the 1520-prioritize-nodes branch October 25, 2024 14:51
@smk762 smk762 removed the docs label Nov 1, 2024
dimxy added a commit that referenced this pull request Nov 2, 2024
* dev:
  fix(legacy-swap): check for confirmations on recover taker (#2242)
  fix(legacy-swap): remove the need for takers to confirm their payment (#2249)
  refactor(P2P): types and modules (#2256)
  fix(evm): correctly display eth addr in iguana v2 activation result (#2254)
  feat(utxo): prioritize electrum connections (#1966)
  refactor(SwapOps): make all methods async (#2251)
  refactor(SwapOps): make `send_maker_payment` async (#2250)
  remove old p2p implementation (#2248)
dimxy added a commit that referenced this pull request Nov 7, 2024
* dev:
  fix(nft): add token_id field to the tx history primary key, fix balance (#2209)
  feat(cosmos): support IBC types in tx history implementation (#2245)
  fix(hd-wallet): use `CoinBalanceMap` for UTXO and QTUM (#2259)
  fix(tests): add more sepolia endpoints in tests (#2262)
  fix(legacy-swap): check for confirmations on recover taker (#2242)
  fix(legacy-swap): remove the need for takers to confirm their payment (#2249)
  refactor(P2P): types and modules (#2256)
  fix(evm): correctly display eth addr in iguana v2 activation result (#2254)
  feat(utxo): prioritize electrum connections (#1966)
  refactor(SwapOps): make all methods async (#2251)
  refactor(SwapOps): make `send_maker_payment` async (#2250)
  remove old p2p implementation (#2248)
dimxy added a commit that referenced this pull request Nov 11, 2024
* dev:
  fix(foot-shooting): remove leftover code that panics via RPC (#2270)
  refactor(MarketCoinOps): make `wait_for_htlc_tx_spend` async (#2265)
  feat(eth-swap): maker tpu v2 implementation (#2211)
  fix(nft): add token_id field to the tx history primary key, fix balance (#2209)
  feat(cosmos): support IBC types in tx history implementation (#2245)
  fix(hd-wallet): use `CoinBalanceMap` for UTXO and QTUM (#2259)
  fix(tests): add more sepolia endpoints in tests (#2262)
  fix(legacy-swap): check for confirmations on recover taker (#2242)
  fix(legacy-swap): remove the need for takers to confirm their payment (#2249)
  refactor(P2P): types and modules (#2256)
  fix(evm): correctly display eth addr in iguana v2 activation result (#2254)
  feat(utxo): prioritize electrum connections (#1966)
  refactor(SwapOps): make all methods async (#2251)
  refactor(SwapOps): make `send_maker_payment` async (#2250)
  remove old p2p implementation (#2248)
  feat(cosmos-offline-tests): prepare IBC channels inside the container  (#2246)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants