From b2bdfb79043444749e395650601f720d0d5a3548 Mon Sep 17 00:00:00 2001 From: iamomm-hack <153058027+iamomm-hack@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:43:52 +0530 Subject: [PATCH] fix: support IPv6 enode URLs --- CHANGELOG.md | 5 +++++ src/hive/client.py | 9 +++++---- src/hive/tests/test_client.py | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 src/hive/tests/test_client.py diff --git a/CHANGELOG.md b/CHANGELOG.md index b431e15..39af0e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- `ClientEnode` now parses and emits bracketed IPv6 endpoints, matching the + canonical `enode://` URL form produced by Ethereum clients. + ## [v0.1.0] - 2026-08-31 Initial stable release of the Python Hive Simulator API with: diff --git a/src/hive/client.py b/src/hive/client.py index 7d64662..506a6e4 100644 --- a/src/hive/client.py +++ b/src/hive/client.py @@ -94,14 +94,15 @@ def from_string(cls, enode: str): prefix = "enode://" enode = enode.removeprefix(prefix) - id, ip_port = enode.split("@") + id, ip_port = enode.split("@", maxsplit=1) - ip, port = ip_port.split(":") + ip, port = ip_port.rsplit(":", maxsplit=1) - return cls(id=id, ip=ip_address(ip), port=int(port)) + return cls(id=id, ip=ip_address(ip.strip("[]")), port=int(port)) def __str__(self) -> str: - return f"enode://{self.id}@{self.ip}:{self.port}" + ip = f"[{self.ip}]" if isinstance(self.ip, IPv6Address) else self.ip + return f"enode://{self.id}@{ip}:{self.port}" def to_json(self): return str(self) diff --git a/src/hive/tests/test_client.py b/src/hive/tests/test_client.py new file mode 100644 index 0000000..89ce268 --- /dev/null +++ b/src/hive/tests/test_client.py @@ -0,0 +1,22 @@ +from ipaddress import IPv4Address, IPv6Address + +import pytest + +from hive.client import ClientEnode + + +@pytest.mark.parametrize( + "enode", + [ + ClientEnode(id="ab" * 64, ip=IPv4Address("192.0.2.1"), port=30303), + ClientEnode(id="cd" * 64, ip=IPv6Address("2001:db8::1"), port=30303), + ], +) +def test_client_enode_round_trip(enode: ClientEnode): + assert ClientEnode.from_string(str(enode)) == enode + + +def test_client_enode_formats_ipv6_with_brackets(): + enode = ClientEnode(id="ab" * 64, ip=IPv6Address("2001:db8::1"), port=30303) + + assert str(enode) == f"enode://{'ab' * 64}@[2001:db8::1]:30303"