Skip to content

Commit

Permalink
fix: allow random RPCs to work for custom networks (#2144)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Jun 14, 2024
1 parent 941fb4f commit 6b0a5e0
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 30 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"eip712>=0.2.7,<0.3",
"ethpm-types>=0.6.9,<0.7",
"eth_pydantic_types>=0.1.0,<0.2",
"evmchains>=0.0.9,<0.1",
"evmchains>=0.0.10,<0.1",
"evm-trace>=0.1.5,<0.2",
],
entry_points={
Expand Down
22 changes: 20 additions & 2 deletions src/ape_ethereum/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from eth_typing import BlockNumber, HexStr
from eth_utils import add_0x_prefix, is_hex, to_hex
from ethpm_types import EventABI
from evmchains import get_random_rpc
from pydantic.dataclasses import dataclass
from requests import HTTPError
from web3 import HTTPProvider, IPCProvider, Web3
Expand Down Expand Up @@ -1152,14 +1153,15 @@ class EthereumNodeProvider(Web3Provider, ABC):
def uri(self) -> str:
if "url" in self.provider_settings:
raise ConfigError("Unknown provider setting 'url'. Did you mean 'uri'?")

elif "uri" in self.provider_settings:
# Use adhoc, scripted value
return self.provider_settings["uri"]

config = self.config.model_dump().get(self.network.ecosystem.name, None)
if config is None:
if self.network.is_dev:
if rpc := self._get_random_rpc():
return rpc
elif self.network.is_dev:
return DEFAULT_SETTINGS["uri"]

# We have no way of knowing what URL the user wants.
Expand All @@ -1170,6 +1172,9 @@ def uri(self) -> str:

if "url" in network_config:
raise ConfigError("Unknown provider setting 'url'. Did you mean 'uri'?")
elif "uri" not in network_config:
if rpc := self._get_random_rpc():
return rpc

settings_uri = network_config.get("uri", DEFAULT_SETTINGS["uri"])
if _is_url(settings_uri):
Expand All @@ -1178,6 +1183,19 @@ def uri(self) -> str:
# Likely was an IPC Path and will connect that way.
return ""

def _get_random_rpc(self) -> Optional[str]:
if self.network.is_dev:
return None

ecosystem = self.network.ecosystem.name
network = self.network.name

# Use public RPC if available
try:
return get_random_rpc(ecosystem, network)
except KeyError:
return None

@property
def connection_str(self) -> str:
return self.uri or f"{self.ipc_path}"
Expand Down
27 changes: 1 addition & 26 deletions src/ape_node/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,29 +354,4 @@ def build_command(self) -> list[str]:

# NOTE: The default behavior of EthereumNodeBehavior assumes geth.
class Node(EthereumNodeProvider):
@property
def uri(self) -> str:
if "uri" in self.provider_settings:
# If specifying in Python, use no matter what.
return self.provider_settings["uri"]

uri = super().uri
ecosystem = self.network.ecosystem.name
network = self.network.name

# If we didn't find one in config, look for a public RPC.
if not uri or uri == DEFAULT_SETTINGS["uri"]:
# Do not override explicit configuration
if ecosystem in self.config:
# Shape of this is odd. Pydantic model containing dicts
if network_config := self.config[ecosystem].get(network):
if "uri" in network_config:
return network_config["uri"]

# Use public RPC if available
try:
uri = get_random_rpc(ecosystem, network)
except KeyError:
pass

return uri
pass
16 changes: 15 additions & 1 deletion tests/functional/geth/test_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,34 @@ def test_uri_when_configured(geth_provider, project, ethereum):
assert actual_mainnet_uri == expected


def test_uri_non_dev_and_not_configured(ethereum):
def test_uri_non_dev_and_not_configured(mocker, ethereum):
"""
If the URI was not configured and we are not using a dev
network (local or -fork), then it should fail, rather than
use local-host.
"""
network = ethereum.sepolia.model_copy(deep=True)

# NOTE: This may fail if using real network names that evmchains would
# know about.
network.name = "gorillanet"
network.ecosystem.name = "gorillas"

provider = Node.model_construct(network=network, request_header={})

with pytest.raises(ProviderError):
_ = provider.uri

# Show that if an evm-chains _does_ exist, it will use that.
patch = mocker.patch("ape_ethereum.provider.get_random_rpc")

# The following URL is made up (please keep example.com).
expected = "https://gorillas.example.com/v1/rpc"
patch.return_value = "https://gorillas.example.com/v1/rpc"

actual = provider.uri
assert actual == expected


@geth_process_test
def test_repr_connected(geth_provider):
Expand Down

0 comments on commit 6b0a5e0

Please sign in to comment.