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

[Fix #710] Use network_id to identify faucet_url #715

Merged
merged 15 commits into from
Jan 2, 2025

Conversation

ckeshava
Copy link
Collaborator

High Level Overview of Change

  • use network_id to disambiguate between different XRPL chains
  • move the utility function _get_network_id_and_build_version into client.py file for convinience

Context of Change

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update CHANGELOG.md?

  • Yes
  • No, this change does not impact library users
    This change should not modify the experience for external developers

Test Plan

Tests have been modified to accomodate the new API for grabbing the faucet_url

- move the utility function _get_network_id_and_build_version into client.py file for convinience
@ckeshava ckeshava requested review from mvadari and justinr1234 June 12, 2024 23:52
@@ -51,3 +54,24 @@ async def _request_impl(
:meta private:
"""
pass


async def _get_network_id_and_build_version(client: Client) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not automatically run this on every client upon instantiation, instead of having it be a helper function? Then it only needs to be run once, instead of every time a function that uses this is called.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We are not duplicating invocations of this function. Here are two examples where the network_id information persists in the client object.

But I agree, this could be done in a cleaner way.
I can only invoke _get_network_id_and_build_version function after a client has opened the socket-connection. So, I'll need to invoke this in this open() method of all the child classes of Client

let me try it out

Copy link
Collaborator

@mvadari mvadari Jun 28, 2024

Choose a reason for hiding this comment

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

I'm open to the option of doing it lazily - i.e. only if and as needed, but only once. All that would require is checking if the network ID has been instantiated before running the request.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

okay, I've added a check: 23d3eb3

import math
from typing import Any, Dict, Optional, cast

from typing_extensions import Final

from xrpl.asyncio.account import get_next_valid_seq_number
from xrpl.asyncio.clients import Client, XRPLRequestFailureException
from xrpl.asyncio.clients.client import _get_network_id_and_build_version
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any function that gets imported elsewhere shouldn't start with a _.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in ab918dc

Comment on lines 186 to 187
elif network_id < 0:
raise XRPLFaucetException("network_id cannot be negative.")
Copy link
Collaborator

@mvadari mvadari Jul 3, 2024

Choose a reason for hiding this comment

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

Does rippled even allow this scenario?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the config file does not allow negative numbers as input for network_id. But it accepts really large numbers (for eg: 4294967295 which could be negative numbers, if typecast into smaller signed-types)

also, rippled unit tests allow negative numbers - XRPLF/rippled@develop...ckeshava:rippled:negNetworkID

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think xrpl-py needs any specific checks for this, "we don't understand this network ID" is fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated in f06e5c1

Comment on lines 189 to 191
raise XRPLFaucetException(
"Cannot fund an account with a client that is not on the testnet or devnet."
"Cannot create faucet URL without network_id or the faucet_host information."
)
Copy link
Collaborator

@mvadari mvadari Jul 3, 2024

Choose a reason for hiding this comment

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

Thoughts on throwing this error in the main generate_faucet_wallet function instead of here? e.g.

if network_id is None and faucet_host is None:
  raise XRPLFaucetException(...)

Otherwise, this error currently executes on an unknown devnet as well, which may have a network ID.

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 don't object to that idea, but how is that different from the current implementation?

At line 189 (the last line of the get_faucet_url method), we have established that faucet_host == None. You are correct, an unknown devnet might have a networkID, but how can I determine the host_url from a networkID (say 1345 ?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if network_id is None and faucet_host is None:
  raise XRPLFaucetException(...)

The said devnet will have a networkID, hence this condition needs to be:

if faucet_host is None and networkID >= 1025:
  raise XRPLFaucetException(...)

The current implementation is equivalent to the above if-condition

Copy link
Collaborator

Choose a reason for hiding this comment

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

The difference comes from typing and failing fast. IMO it makes more sense for get_faucet_url to not accept None as a valid network ID.

You can't determine the host URL from an unknown Devnet, but the error message should be different.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we cannot expect all networks to have a valid NetworkID isn't it?
If we force get_faucet_url to accept valid networkIDs only, then some of the processing logic shifts into generate_faucet_wallet method.

def get_faucet_url(network_id: int) -> str:
    if network_id == 1:
        return _TEST_FAUCET_URL
    elif network_id == 2:
        return _DEV_FAUCET_URL
    elif network_id == 0:
        raise XRPLFaucetException("Cannot create faucet with a client on mainnet.")
    elif network_id < 0:
        raise XRPLFaucetException("network_id cannot be negative.")

    # unreachable code
    return ""

async def generate_faucet_wallet(
    client: Client,
    wallet: Optional[Wallet] = None,
    debug: bool = False,
    faucet_host: Optional[str] = None,
    usage_context: Optional[str] = None,
    user_agent: Optional[str] = "xrpl-py",
) -> Wallet:
    if not client.network_id:
        await get_network_id_and_build_version(client)

    if client.network_id is None and faucet_host is None:
        raise XRPLFaucetException(
            "Cannot create faucet URL without network_id or the faucet_host information"
        )

    if faucet_host is not None:
        faucet_url = process_faucet_host_url(faucet_host)
    else:
        faucet_url = get_faucet_url(client.network_id)
        
    # remaining code in the function ...

Copy link
Collaborator

Choose a reason for hiding this comment

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

That flow makes more sense to me, personally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

alrighty

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated sometime before f06e5c1

Comment on lines 176 to 177
if faucet_host is not None:
return process_faucet_host_url(faucet_host)
Copy link
Collaborator

@mvadari mvadari Jul 3, 2024

Choose a reason for hiding this comment

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

Upon further thought, I think it might make more sense to bring this code out of this function and have get_faucet_url be entirely based on the network_id.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, I put down a snippet in a comment below

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated somewhere prior to f06e5c1

self.assertEqual(get_faucet_url(json_client_url, custom_host), expected_faucet)
self.assertEqual(get_faucet_url(ws_client_url, custom_host), expected_faucet)

def test_get_faucet_wallet_test(self):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

these test cases are already covered in the below class TestProcessFaucetHostURL

@ckeshava ckeshava requested a review from mvadari July 3, 2024 20:24
raise XRPLFaucetException("Unable to parse the specified network_id.")

# this line is unreachable. Custom devnets must specify a faucet_host input
return ""
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should raise an error here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in f06e5c1

Comment on lines 172 to 177
if "sidechain-net2" in url: # sidechain issuing chain devnet
raise XRPLFaucetException(
"Cannot fund an account on an issuing chain. Accounts must be created via "
"the bridge."
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This piece still needs to be included. The network ID is 262.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

thanks. fixed in f06e5c1

Comment on lines 169 to 180
return process_faucet_host_url(faucet_host)
if "altnet" in url or "testnet" in url: # testnet
if network_id == 1:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this if-else be replaced with a dictionary? It'll be easier to maintain that way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated in f06e5c1

CHANGELOG.md Outdated Show resolved Hide resolved
if "altnet" in url or "testnet" in url: # testnet
return _TEST_FAUCET_URL
if "sidechain-net2" in url: # sidechain issuing chain devnet
map_network_id_to_url: Dict[int, str] = {1: _TEST_FAUCET_URL, 2: _DEV_FAUCET_URL}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This object should be outside of the function, since it's not dependent on the data inside. And the name should be in uppercase.

@@ -17,6 +17,7 @@
_DEV_FAUCET_URL: Final[str] = "https://faucet.devnet.rippletest.net/accounts"

_TIMEOUT_SECONDS: Final[int] = 40
_MAP_NETWORK_ID_TO_URL: Dict[int, str] = {1: _TEST_FAUCET_URL, 2: _DEV_FAUCET_URL}
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:

Suggested change
_MAP_NETWORK_ID_TO_URL: Dict[int, str] = {1: _TEST_FAUCET_URL, 2: _DEV_FAUCET_URL}
_NETWORK_ID_URL_MAP: Dict[int, str] = {1: _TEST_FAUCET_URL, 2: _DEV_FAUCET_URL}

@ckeshava ckeshava requested a review from khancode July 17, 2024 20:55
@mvadari mvadari linked an issue Sep 17, 2024 that may be closed by this pull request
Copy link
Contributor

coderabbitai bot commented Jan 2, 2025

Warning

Rate limit exceeded

@ckeshava has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 16cc117 and 2c91e53.

📒 Files selected for processing (5)
  • CHANGELOG.md (1 hunks)
  • tests/unit/asyn/wallet/test_wallet.py (2 hunks)
  • xrpl/asyncio/clients/client.py (2 hunks)
  • xrpl/asyncio/transaction/main.py (2 hunks)
  • xrpl/asyncio/wallet/wallet_generation.py (3 hunks)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ckeshava ckeshava merged commit 8c6b489 into XRPLF:main Jan 2, 2025
22 checks passed
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.

Use Network ID to determine the correct faucet to use
3 participants