Skip to content

Commit 2205e9e

Browse files
authored
Merge branch 'main' into fix/is-approved-accumulation
2 parents b3fb9cb + 679e4a6 commit 2205e9e

30 files changed

Lines changed: 4427 additions & 3 deletions
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
"""
2+
Demonstrates the full lifecycle of a registered node on the Hedera network.
3+
4+
This example shows how to:
5+
1. Create a registered node with BlockNodeServiceEndpoint and multiple endpoint APIs
6+
2. Query the registered node via the mirror node REST API
7+
3. Update the registered node's description and service endpoints
8+
4. Associate / disassociate the registered node with a consensus node
9+
5. Delete the registered node
10+
11+
NOTE: This is a privileged transaction. Regular developers do not have the required
12+
permissions to manage registered nodes on testnet or mainnet as this operation
13+
requires special authorization.
14+
15+
This example is provided to demonstrate the API for educational purposes or for use
16+
in private network deployments where you have the necessary administrative privileges.
17+
"""
18+
19+
import sys
20+
import time
21+
22+
from dotenv import load_dotenv
23+
24+
from hiero_sdk_python import AccountId, Client, Network, PrivateKey
25+
from hiero_sdk_python.address_book.block_node_api import BlockNodeApi
26+
from hiero_sdk_python.address_book.block_node_service_endpoint import BlockNodeServiceEndpoint
27+
from hiero_sdk_python.address_book.registered_node_address_book_query import RegisteredNodeAddressBookQuery
28+
from hiero_sdk_python.exceptions import PrecheckError
29+
from hiero_sdk_python.nodes.node_update_transaction import NodeUpdateTransaction
30+
from hiero_sdk_python.nodes.registered_node_create_transaction import RegisteredNodeCreateTransaction
31+
from hiero_sdk_python.nodes.registered_node_delete_transaction import RegisteredNodeDeleteTransaction
32+
from hiero_sdk_python.nodes.registered_node_update_transaction import RegisteredNodeUpdateTransaction
33+
from hiero_sdk_python.response_code import ResponseCode
34+
35+
36+
def setup_client():
37+
"""Initialize and set up the client with operator account."""
38+
load_dotenv()
39+
network = Network(network="solo")
40+
client = Client(network)
41+
print(f"Connecting to Hedera {network} network!")
42+
43+
# Account 0.0.2 is a special administrative account with
44+
# elevated privileges for network management operations.
45+
# The private key is intentionally public for local development.
46+
# Note: This setup only works on solo network and will not work on testnet/mainnet.
47+
original_operator_key = PrivateKey.from_string_der(
48+
"302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137"
49+
)
50+
client.set_operator(AccountId(0, 0, 2), original_operator_key)
51+
52+
return client
53+
54+
55+
def registered_node_lifecycle():
56+
"""Demonstrates create, query, update, associate, disassociate, and delete of a registered node."""
57+
client = setup_client()
58+
59+
# Generate an admin key for the registered node
60+
admin_key = PrivateKey.generate_ed25519()
61+
62+
# ── Step 1: Create a registered node ────────────────────────────
63+
print("\n--- Step 1: Creating registered node ---")
64+
65+
block_endpoint = BlockNodeServiceEndpoint(
66+
ip_address=bytes([127, 0, 0, 1]),
67+
port=443,
68+
requires_tls=True,
69+
endpoint_apis=[BlockNodeApi.STATUS, BlockNodeApi.SUBSCRIBE_STREAM],
70+
)
71+
72+
try:
73+
receipt = (
74+
RegisteredNodeCreateTransaction()
75+
.set_admin_key(admin_key.public_key())
76+
.set_description("My Block Node")
77+
.set_service_endpoints([block_endpoint])
78+
.freeze_with(client)
79+
.sign(admin_key)
80+
.execute(client)
81+
)
82+
except (PrecheckError, ValueError) as e:
83+
print(f"Registered node creation failed: {e}")
84+
sys.exit(1)
85+
86+
if receipt.status != ResponseCode.SUCCESS:
87+
print(f"Registered node creation failed: {ResponseCode(receipt.status).name}")
88+
sys.exit(1)
89+
90+
registered_node_id = receipt.registered_node_id
91+
print(f"Registered node created with ID: {registered_node_id}")
92+
93+
# ── Step 2: Query the registered node via mirror node ───────────
94+
print("\n--- Step 2: Querying registered node via mirror node ---")
95+
96+
# Wait for mirror node ingestion
97+
time.sleep(5)
98+
99+
try:
100+
address_book = RegisteredNodeAddressBookQuery().set_registered_node_id(registered_node_id).execute(client)
101+
102+
if len(address_book) > 0:
103+
node = address_book[0]
104+
print(f"Fetched registered node: {node}")
105+
print(f" Description: {node.description}")
106+
print(f" Endpoints: {len(node.service_endpoints)}")
107+
else:
108+
print("No registered nodes returned (mirror may still be ingesting)")
109+
except RuntimeError as e:
110+
print(f"Query failed (mirror node may not support this endpoint): {e}")
111+
112+
# ── Step 3: Update the registered node ──────────────────────────
113+
print("\n--- Step 3: Updating registered node ---")
114+
115+
update_endpoint = BlockNodeServiceEndpoint(
116+
domain_name="block-node.example.com",
117+
port=443,
118+
requires_tls=True,
119+
endpoint_apis=[BlockNodeApi.STATUS],
120+
)
121+
122+
try:
123+
receipt = (
124+
RegisteredNodeUpdateTransaction()
125+
.set_registered_node_id(registered_node_id)
126+
.set_description("My Updated Block Node")
127+
.set_service_endpoints([block_endpoint, update_endpoint])
128+
.freeze_with(client)
129+
.sign(admin_key)
130+
.execute(client)
131+
)
132+
except (PrecheckError, ValueError) as e:
133+
print(f"Registered node update failed: {e}")
134+
sys.exit(1)
135+
136+
if receipt.status != ResponseCode.SUCCESS:
137+
print(f"Registered node update failed: {ResponseCode(receipt.status).name}")
138+
sys.exit(1)
139+
140+
print("Registered node updated successfully")
141+
142+
# ── Step 4: Associate registered node with consensus node ───────
143+
print("\n--- Step 4: Associating registered node with consensus node 0 ---")
144+
145+
try:
146+
receipt = (
147+
NodeUpdateTransaction()
148+
.set_node_id(0)
149+
.add_associated_registered_node(registered_node_id)
150+
.freeze_with(client)
151+
.execute(client)
152+
)
153+
except (PrecheckError, ValueError) as e:
154+
print(f"Association failed: {e}")
155+
sys.exit(1)
156+
157+
if receipt.status != ResponseCode.SUCCESS:
158+
print(f"Association failed: {ResponseCode(receipt.status).name}")
159+
sys.exit(1)
160+
161+
print(f"Registered node {registered_node_id} associated with consensus node 0")
162+
163+
# ── Step 5: Disassociate registered node from consensus node ────
164+
print("\n--- Step 5: Disassociating registered node from consensus node 0 ---")
165+
166+
try:
167+
receipt = (
168+
NodeUpdateTransaction()
169+
.set_node_id(0)
170+
.clear_associated_registered_nodes()
171+
.freeze_with(client)
172+
.execute(client)
173+
)
174+
except (PrecheckError, ValueError) as e:
175+
print(f"Disassociation failed: {e}")
176+
sys.exit(1)
177+
178+
if receipt.status != ResponseCode.SUCCESS:
179+
print(f"Disassociation failed: {ResponseCode(receipt.status).name}")
180+
sys.exit(1)
181+
182+
print(f"Registered node {registered_node_id} disassociated from consensus node 0")
183+
184+
# ── Step 6: Delete the registered node ──────────────────────────
185+
print("\n--- Step 6: Deleting registered node ---")
186+
187+
try:
188+
receipt = (
189+
RegisteredNodeDeleteTransaction()
190+
.set_registered_node_id(registered_node_id)
191+
.freeze_with(client)
192+
.sign(admin_key)
193+
.execute(client)
194+
)
195+
except (PrecheckError, ValueError) as e:
196+
print(f"Registered node deletion failed: {e}")
197+
sys.exit(1)
198+
199+
if receipt.status != ResponseCode.SUCCESS:
200+
print(f"Registered node deletion failed: {ResponseCode(receipt.status).name}")
201+
sys.exit(1)
202+
203+
print("Registered node deleted successfully")
204+
print("\n--- Registered node lifecycle complete! ---")
205+
206+
207+
if __name__ == "__main__":
208+
registered_node_lifecycle()

generate_proto.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from urllib.parse import urlparse
3535

3636

37-
VERSION = "v0.72.1"
37+
VERSION = "v0.73.0"
3838
SOURCES = [
3939
{
4040
"name": "hedera-protobufs",

src/hiero_sdk_python/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@
99
from .account.account_update_transaction import AccountUpdateTransaction
1010

1111
# Address book
12+
from .address_book.block_node_api import BlockNodeApi
13+
from .address_book.block_node_service_endpoint import BlockNodeServiceEndpoint
1214
from .address_book.endpoint import Endpoint
15+
from .address_book.general_service_endpoint import GeneralServiceEndpoint
16+
from .address_book.mirror_node_service_endpoint import MirrorNodeServiceEndpoint
1317
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
21+
from .address_book.registered_service_endpoint import RegisteredServiceEndpoint
22+
from .address_book.rpc_relay_service_endpoint import RpcRelayServiceEndpoint
1423

1524
# Client and Network
1625
from .client.client import Client
@@ -77,6 +86,9 @@
7786
from .nodes.node_create_transaction import NodeCreateTransaction
7887
from .nodes.node_delete_transaction import NodeDeleteTransaction
7988
from .nodes.node_update_transaction import NodeUpdateTransaction
89+
from .nodes.registered_node_create_transaction import RegisteredNodeCreateTransaction
90+
from .nodes.registered_node_delete_transaction import RegisteredNodeDeleteTransaction
91+
from .nodes.registered_node_update_transaction import RegisteredNodeUpdateTransaction
8092

8193
# PRNG
8294
from .prng_transaction import PrngTransaction
@@ -245,8 +257,17 @@
245257
"TokenInfoQuery",
246258
"AccountInfoQuery",
247259
# Address book
260+
"BlockNodeApi",
261+
"BlockNodeServiceEndpoint",
248262
"Endpoint",
263+
"GeneralServiceEndpoint",
264+
"MirrorNodeServiceEndpoint",
249265
"NodeAddress",
266+
"RegisteredNode",
267+
"RegisteredNodeAddressBook",
268+
"RegisteredNodeAddressBookQuery",
269+
"RegisteredServiceEndpoint",
270+
"RpcRelayServiceEndpoint",
250271
# Logger
251272
"Logger",
252273
"LogLevel",
@@ -296,6 +317,9 @@
296317
"NodeCreateTransaction",
297318
"NodeUpdateTransaction",
298319
"NodeDeleteTransaction",
320+
"RegisteredNodeCreateTransaction",
321+
"RegisteredNodeUpdateTransaction",
322+
"RegisteredNodeDeleteTransaction",
299323
# PRNG
300324
"PrngTransaction",
301325
# Custom Fees
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from __future__ import annotations
2+
3+
from enum import IntEnum
4+
5+
6+
class BlockNodeApi(IntEnum):
7+
"""Maps to the BlockNodeApi enum defined inside RegisteredServiceEndpoint.BlockNodeEndpoint."""
8+
9+
OTHER = 0
10+
STATUS = 1
11+
PUBLISH = 2
12+
SUBSCRIBE_STREAM = 3
13+
STATE_PROOF = 4
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from __future__ import annotations
2+
3+
from hiero_sdk_python.address_book.block_node_api import BlockNodeApi
4+
from hiero_sdk_python.address_book.registered_service_endpoint import RegisteredServiceEndpoint
5+
from hiero_sdk_python.hapi.services.registered_service_endpoint_pb2 import (
6+
RegisteredServiceEndpoint as RegisteredServiceEndpointProto,
7+
)
8+
9+
10+
class BlockNodeServiceEndpoint(RegisteredServiceEndpoint):
11+
"""A registered service endpoint for a block node."""
12+
13+
def __init__(
14+
self,
15+
ip_address: bytes | None = None,
16+
domain_name: str | None = None,
17+
port: int = 0,
18+
requires_tls: bool = False,
19+
endpoint_apis: list[BlockNodeApi] | None = None,
20+
) -> None:
21+
super().__init__(ip_address=ip_address, domain_name=domain_name, port=port, requires_tls=requires_tls)
22+
self.endpoint_apis: list[BlockNodeApi] = [BlockNodeApi(api) for api in (endpoint_apis or [])]
23+
24+
def set_endpoint_apis(self, endpoint_apis: list[BlockNodeApi]) -> BlockNodeServiceEndpoint:
25+
"""Set the list of block node endpoint APIs."""
26+
self.endpoint_apis = [BlockNodeApi(api) for api in endpoint_apis]
27+
return self
28+
29+
def _set_endpoint_type(self, proto: RegisteredServiceEndpointProto) -> None:
30+
block_node = proto.block_node
31+
for api in self.endpoint_apis:
32+
block_node.endpoint_api.append(api.value)
33+
34+
@classmethod
35+
def _from_proto_inner(
36+
cls,
37+
proto: RegisteredServiceEndpointProto,
38+
ip_address: bytes | None,
39+
domain_name: str | None,
40+
port: int,
41+
requires_tls: bool,
42+
) -> BlockNodeServiceEndpoint:
43+
apis = [BlockNodeApi(v) for v in proto.block_node.endpoint_api]
44+
return cls(
45+
ip_address=ip_address,
46+
domain_name=domain_name,
47+
port=port,
48+
requires_tls=requires_tls,
49+
endpoint_apis=apis,
50+
)
51+
52+
@classmethod
53+
def _from_dict_inner(cls, type_data: dict, **base_kwargs) -> BlockNodeServiceEndpoint:
54+
"""Build from the ``block_node`` sub-dict of a mirror-node JSON endpoint."""
55+
raw_apis = type_data.get("endpoint_apis") or []
56+
apis = [BlockNodeApi[a.upper()] if isinstance(a, str) else BlockNodeApi(a) for a in raw_apis]
57+
return cls(endpoint_apis=apis, **base_kwargs)

0 commit comments

Comments
 (0)