Skip to content

Commit 0d76050

Browse files
committed
feat: add HIP-1137 registered node address book models
Signed-off-by: Ntege Daniel <danientege785@gmail.com>
1 parent cd2de39 commit 0d76050

5 files changed

Lines changed: 438 additions & 0 deletions

File tree

src/hiero_sdk_python/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
from .address_book.general_service_endpoint import GeneralServiceEndpoint
1616
from .address_book.mirror_node_service_endpoint import MirrorNodeServiceEndpoint
1717
from .address_book.node_address import NodeAddress
18+
from .address_book.registered_node import RegisteredNode
19+
from .address_book.registered_node_address_book import RegisteredNodeAddressBook
20+
from .address_book.registered_node_address_book_query import RegisteredNodeAddressBookQuery
1821
from .address_book.registered_service_endpoint import RegisteredServiceEndpoint
1922
from .address_book.rpc_relay_service_endpoint import RpcRelayServiceEndpoint
2023

@@ -247,6 +250,9 @@
247250
# Address book
248251
"Endpoint",
249252
"NodeAddress",
253+
"RegisteredNode",
254+
"RegisteredNodeAddressBook",
255+
"RegisteredNodeAddressBookQuery",
250256
# Logger
251257
"Logger",
252258
"LogLevel",
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Read-side model for a HIP-1137 registered node."""
2+
3+
from __future__ import annotations
4+
5+
from hiero_sdk_python.address_book.registered_service_endpoint import RegisteredServiceEndpoint
6+
from hiero_sdk_python.crypto.public_key import PublicKey
7+
from hiero_sdk_python.hapi.services.state.addressbook.registered_node_pb2 import (
8+
RegisteredNode as RegisteredNodeProto,
9+
)
10+
11+
12+
class RegisteredNode:
13+
"""Immutable model representing a registered node from network/mirror state."""
14+
15+
def __init__(
16+
self,
17+
registered_node_id: int,
18+
admin_key: PublicKey | None = None,
19+
description: str | None = None,
20+
service_endpoints: tuple[RegisteredServiceEndpoint, ...] = (),
21+
) -> None:
22+
if not isinstance(registered_node_id, int) or isinstance(registered_node_id, bool):
23+
raise ValueError("registered_node_id must be a positive integer")
24+
if registered_node_id <= 0:
25+
raise ValueError("registered_node_id must be a positive integer")
26+
27+
self._registered_node_id = registered_node_id
28+
self._admin_key = admin_key
29+
self._description = description
30+
self._service_endpoints = tuple(service_endpoints)
31+
32+
@property
33+
def registered_node_id(self) -> int:
34+
return self._registered_node_id
35+
36+
@property
37+
def admin_key(self) -> PublicKey | None:
38+
return self._admin_key
39+
40+
@property
41+
def description(self) -> str | None:
42+
return self._description
43+
44+
@property
45+
def service_endpoints(self) -> tuple[RegisteredServiceEndpoint, ...]:
46+
return self._service_endpoints
47+
48+
@classmethod
49+
def _from_proto(cls, proto: RegisteredNodeProto) -> RegisteredNode:
50+
"""Create a RegisteredNode from a protobuf RegisteredNode state message."""
51+
admin_key: PublicKey | None = None
52+
if proto.HasField("admin_key"):
53+
admin_key = PublicKey._from_proto(proto.admin_key)
54+
55+
description: str | None = proto.description if proto.description else None
56+
57+
endpoints = tuple(RegisteredServiceEndpoint._from_proto(ep) for ep in proto.service_endpoint)
58+
59+
return cls(
60+
registered_node_id=proto.registered_node_id,
61+
admin_key=admin_key,
62+
description=description,
63+
service_endpoints=endpoints,
64+
)
65+
66+
def _to_proto(self) -> RegisteredNodeProto:
67+
"""Convert this RegisteredNode to a protobuf RegisteredNode."""
68+
proto = RegisteredNodeProto(
69+
registered_node_id=self._registered_node_id,
70+
)
71+
if self._admin_key is not None:
72+
proto.admin_key.CopyFrom(self._admin_key._to_proto())
73+
if self._description is not None:
74+
proto.description = self._description
75+
for ep in self._service_endpoints:
76+
proto.service_endpoint.append(ep._to_proto())
77+
return proto
78+
79+
def __repr__(self) -> str:
80+
return f"RegisteredNode(registered_node_id={self._registered_node_id}, description={self._description!r})"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Read-side container for a collection of HIP-1137 registered nodes."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Iterator
6+
7+
from hiero_sdk_python.address_book.registered_node import RegisteredNode
8+
9+
10+
class RegisteredNodeAddressBook:
11+
"""Immutable container of :class:`RegisteredNode` entries.
12+
13+
No protobuf ``RegisteredNodeAddressBook`` message exists in the current
14+
protobufs, so this is a pure SDK-side convenience wrapper that mirrors
15+
the role of the legacy ``NodeAddressBook`` model.
16+
"""
17+
18+
def __init__(self, nodes: tuple[RegisteredNode, ...] = ()) -> None:
19+
self._nodes = tuple(nodes)
20+
21+
@property
22+
def nodes(self) -> tuple[RegisteredNode, ...]:
23+
return self._nodes
24+
25+
def __len__(self) -> int:
26+
return len(self._nodes)
27+
28+
def __iter__(self) -> Iterator[RegisteredNode]:
29+
return iter(self._nodes)
30+
31+
def __getitem__(self, index: int) -> RegisteredNode:
32+
return self._nodes[index]
33+
34+
def __repr__(self) -> str:
35+
return f"RegisteredNodeAddressBook(nodes={len(self._nodes)})"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Skeleton query for fetching the registered-node address book (HIP-1137)."""
2+
3+
from __future__ import annotations
4+
5+
from hiero_sdk_python.address_book.registered_node_address_book import RegisteredNodeAddressBook
6+
7+
8+
class RegisteredNodeAddressBookQuery:
9+
"""Query to retrieve the registered-node address book from the network.
10+
11+
.. warning::
12+
13+
Query execution is **not yet available**. The mirror-node API endpoint
14+
required by this query has not been implemented in this SDK release.
15+
Calling :meth:`execute` will raise :class:`NotImplementedError`.
16+
"""
17+
18+
def __init__(self) -> None:
19+
self._max_registered_node_count: int | None = None
20+
21+
def set_max_registered_node_count(self, count: int) -> RegisteredNodeAddressBookQuery:
22+
"""Limit the number of registered nodes returned.
23+
24+
Args:
25+
count: Maximum number of nodes. Must be positive.
26+
27+
Returns:
28+
This query instance for method chaining.
29+
"""
30+
if not isinstance(count, int) or isinstance(count, bool) or count <= 0:
31+
raise ValueError("count must be a positive integer")
32+
self._max_registered_node_count = count
33+
return self
34+
35+
def execute(self, client: object = None) -> RegisteredNodeAddressBook:
36+
"""Execute the query against the network.
37+
38+
Raises:
39+
NotImplementedError: Always. Mirror-node API support for
40+
registered-node queries is not yet available in this SDK.
41+
"""
42+
raise NotImplementedError(
43+
"RegisteredNodeAddressBookQuery requires HIP-1137 mirror node API support, "
44+
"which is not yet available in this SDK."
45+
)

0 commit comments

Comments
 (0)