-
Notifications
You must be signed in to change notification settings - Fork 287
Implements HIP-1137 registered node support in the Python SDK. #2248
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
Merged
Merged
Changes from all commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
209bf5c
feat: update protobufs to v0.73.0 for HIP-1137
danielmarv 48e9322
feat: add HIP-1137 registered service endpoint models
danielmarv a7a0b37
Merge branch 'hiero-ledger:main' into hip-1137-br
danielmarv 6c58126
feat: add HIP-1137 registered node transactions
danielmarv 9ab77ab
Merge branch 'hip-1137-br' of https://github.com/danielmarv/hiero-sdk…
danielmarv cd2de39
feat: support associated registered nodes on node transactions
danielmarv 0d76050
feat: add HIP-1137 registered node address book models
danielmarv 87b9d90
test: harden HIP-1137 validation and status handling
danielmarv 550ffdb
test: add HIP-1137 integration coverage and docs
danielmarv 5fdbc50
test: add HIP-1137 integration coverage and docs
danielmarv 2b9e6d3
test: add HIP-1137 integration coverage and docs
danielmarv b15ebd9
test: add HIP-1137 integration coverage and docs
danielmarv 2025c27
Merge branch 'main' into hip-1137-br
danielmarv dca3d40
test: add HIP-1137 integration coverage and docs
danielmarv f47ed8b
Merge branch 'hip-1137-br' of https://github.com/danielmarv/hiero-sdk…
danielmarv 54c5f31
test: add HIP-1137 integration coverage and docs
danielmarv 1e86243
test: add HIP-1137 integration coverage and docs
danielmarv 3b6ad9a
test: add HIP-1137 integration coverage and docs
danielmarv 83cd770
Merge branch 'main' into hip-1137-br
danielmarv 24212f4
Merge branch 'main' into hip-1137-br
danielmarv 2bf5796
Merge branch 'main' into hip-1137-br
danielmarv 7340544
feat: refactor registered node transactions and validation logic for …
danielmarv 77aa5f8
Merge branch 'hip-1137-br' of https://github.com/danielmarv/hiero-sdk…
danielmarv c928195
feat: refactor registered node transactions and validation logic for …
danielmarv 0be6eec
feat: refactor registered node transactions and validation logic for …
danielmarv d4f0fa0
feat: refactor registered node transactions and validation logic for …
danielmarv 2345b42
Merge branch 'main' into hip-1137-br
danielmarv bb587da
Merge branch 'main' into hip-1137-br
danielmarv b267997
Merge branch 'main' into hip-1137-br
danielmarv b922cc9
test: Enhance NodeCreate and NodeUpdate tests for associated register…
danielmarv 58ccff7
feat: Enhance registered node lifecycle example and add query functio…
danielmarv acfcf6f
Merge branch 'main' into hip-1137-br
danielmarv f100f5f
Merge branch 'main' into hip-1137-br
danielmarv c0dba0e
feat: Implement from_dict methods for service endpoints and enhance t…
danielmarv b69107b
Merge branch 'hip-1137-br' of https://github.com/danielmarv/hiero-sdk…
danielmarv c231de7
Merge branch 'main' into hip-1137-br
danielmarv dced5fd
feat: Implement from_dict methods for service endpoints and enhance t…
danielmarv 7144c0e
feat: Implement from_dict methods for service endpoints and enhance t…
danielmarv 9aa777a
Merge branch 'main' into hip-1137-br
danielmarv 1206a86
feat: Implement from_dict methods for service endpoints and enhance t…
danielmarv 51287f5
feat: Implement from_dict methods for service endpoints and enhance t…
danielmarv 3bc52f8
Merge branch 'main' into hip-1137-br
danielmarv ad2a72e
feat: Add setter methods for service endpoint attributes and enhance …
danielmarv fb7f464
Merge branch 'main' into hip-1137-br
danielmarv ad9ffe2
feat: Add setter methods for service endpoint attributes and enhance …
danielmarv 32d308e
Merge branch 'hip-1137-br' of https://github.com/danielmarv/hiero-sdk…
danielmarv fc186b9
feat: Add setter methods for service endpoint attributes and enhance …
danielmarv bcfae47
feat: Add setter methods for service endpoint attributes and enhance …
danielmarv 0fbd578
feat: Add setter methods for service endpoint attributes and enhance …
danielmarv 366d56f
feat: Add setter methods for service endpoint attributes and enhance …
danielmarv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| """ | ||
| Demonstrates the full lifecycle of a registered node on the Hedera network. | ||
|
|
||
| This example shows how to: | ||
| 1. Create a registered node with BlockNodeServiceEndpoint and multiple endpoint APIs | ||
| 2. Query the registered node via the mirror node REST API | ||
| 3. Update the registered node's description and service endpoints | ||
| 4. Associate / disassociate the registered node with a consensus node | ||
| 5. Delete the registered node | ||
|
|
||
| NOTE: This is a privileged transaction. Regular developers do not have the required | ||
| permissions to manage registered nodes on testnet or mainnet as this operation | ||
| requires special authorization. | ||
|
|
||
| This example is provided to demonstrate the API for educational purposes or for use | ||
| in private network deployments where you have the necessary administrative privileges. | ||
| """ | ||
|
|
||
| import sys | ||
| import time | ||
|
|
||
| from dotenv import load_dotenv | ||
|
|
||
| from hiero_sdk_python import AccountId, Client, Network, PrivateKey | ||
| from hiero_sdk_python.address_book.block_node_api import BlockNodeApi | ||
| from hiero_sdk_python.address_book.block_node_service_endpoint import BlockNodeServiceEndpoint | ||
| from hiero_sdk_python.address_book.registered_node_address_book_query import RegisteredNodeAddressBookQuery | ||
| from hiero_sdk_python.exceptions import PrecheckError | ||
| from hiero_sdk_python.nodes.node_update_transaction import NodeUpdateTransaction | ||
| from hiero_sdk_python.nodes.registered_node_create_transaction import RegisteredNodeCreateTransaction | ||
| from hiero_sdk_python.nodes.registered_node_delete_transaction import RegisteredNodeDeleteTransaction | ||
| from hiero_sdk_python.nodes.registered_node_update_transaction import RegisteredNodeUpdateTransaction | ||
| from hiero_sdk_python.response_code import ResponseCode | ||
|
|
||
|
|
||
| def setup_client(): | ||
| """Initialize and set up the client with operator account.""" | ||
| load_dotenv() | ||
| network = Network(network="solo") | ||
| client = Client(network) | ||
| print(f"Connecting to Hedera {network} network!") | ||
|
|
||
| # Account 0.0.2 is a special administrative account with | ||
| # elevated privileges for network management operations. | ||
| # The private key is intentionally public for local development. | ||
| # Note: This setup only works on solo network and will not work on testnet/mainnet. | ||
| original_operator_key = PrivateKey.from_string_der( | ||
| "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137" | ||
| ) | ||
| client.set_operator(AccountId(0, 0, 2), original_operator_key) | ||
|
|
||
| return client | ||
|
|
||
|
|
||
| def registered_node_lifecycle(): | ||
| """Demonstrates create, query, update, associate, disassociate, and delete of a registered node.""" | ||
| client = setup_client() | ||
|
|
||
| # Generate an admin key for the registered node | ||
| admin_key = PrivateKey.generate_ed25519() | ||
|
|
||
| # ── Step 1: Create a registered node ──────────────────────────── | ||
| print("\n--- Step 1: Creating registered node ---") | ||
|
|
||
| block_endpoint = BlockNodeServiceEndpoint( | ||
| ip_address=bytes([127, 0, 0, 1]), | ||
| port=443, | ||
| requires_tls=True, | ||
| endpoint_apis=[BlockNodeApi.STATUS, BlockNodeApi.SUBSCRIBE_STREAM], | ||
| ) | ||
|
|
||
| try: | ||
| receipt = ( | ||
| RegisteredNodeCreateTransaction() | ||
| .set_admin_key(admin_key.public_key()) | ||
| .set_description("My Block Node") | ||
| .set_service_endpoints([block_endpoint]) | ||
| .freeze_with(client) | ||
| .sign(admin_key) | ||
| .execute(client) | ||
| ) | ||
| except (PrecheckError, ValueError) as e: | ||
| print(f"Registered node creation failed: {e}") | ||
| sys.exit(1) | ||
|
|
||
| if receipt.status != ResponseCode.SUCCESS: | ||
| print(f"Registered node creation failed: {ResponseCode(receipt.status).name}") | ||
| sys.exit(1) | ||
|
|
||
| registered_node_id = receipt.registered_node_id | ||
| print(f"Registered node created with ID: {registered_node_id}") | ||
|
|
||
| # ── Step 2: Query the registered node via mirror node ─────────── | ||
| print("\n--- Step 2: Querying registered node via mirror node ---") | ||
|
|
||
| # Wait for mirror node ingestion | ||
| time.sleep(5) | ||
|
|
||
| try: | ||
| address_book = RegisteredNodeAddressBookQuery().set_registered_node_id(registered_node_id).execute(client) | ||
|
|
||
| if len(address_book) > 0: | ||
| node = address_book[0] | ||
| print(f"Fetched registered node: {node}") | ||
| print(f" Description: {node.description}") | ||
| print(f" Endpoints: {len(node.service_endpoints)}") | ||
| else: | ||
| print("No registered nodes returned (mirror may still be ingesting)") | ||
| except RuntimeError as e: | ||
| print(f"Query failed (mirror node may not support this endpoint): {e}") | ||
|
|
||
| # ── Step 3: Update the registered node ────────────────────────── | ||
| print("\n--- Step 3: Updating registered node ---") | ||
|
|
||
| update_endpoint = BlockNodeServiceEndpoint( | ||
| domain_name="block-node.example.com", | ||
| port=443, | ||
| requires_tls=True, | ||
| endpoint_apis=[BlockNodeApi.STATUS], | ||
| ) | ||
|
|
||
| try: | ||
| receipt = ( | ||
| RegisteredNodeUpdateTransaction() | ||
| .set_registered_node_id(registered_node_id) | ||
| .set_description("My Updated Block Node") | ||
| .set_service_endpoints([block_endpoint, update_endpoint]) | ||
| .freeze_with(client) | ||
| .sign(admin_key) | ||
| .execute(client) | ||
| ) | ||
| except (PrecheckError, ValueError) as e: | ||
| print(f"Registered node update failed: {e}") | ||
| sys.exit(1) | ||
|
|
||
| if receipt.status != ResponseCode.SUCCESS: | ||
| print(f"Registered node update failed: {ResponseCode(receipt.status).name}") | ||
| sys.exit(1) | ||
|
|
||
| print("Registered node updated successfully") | ||
|
|
||
| # ── Step 4: Associate registered node with consensus node ─────── | ||
| print("\n--- Step 4: Associating registered node with consensus node 0 ---") | ||
|
|
||
| try: | ||
| receipt = ( | ||
| NodeUpdateTransaction() | ||
| .set_node_id(0) | ||
| .add_associated_registered_node(registered_node_id) | ||
| .freeze_with(client) | ||
| .execute(client) | ||
| ) | ||
| except (PrecheckError, ValueError) as e: | ||
| print(f"Association failed: {e}") | ||
| sys.exit(1) | ||
|
|
||
| if receipt.status != ResponseCode.SUCCESS: | ||
| print(f"Association failed: {ResponseCode(receipt.status).name}") | ||
| sys.exit(1) | ||
|
|
||
| print(f"Registered node {registered_node_id} associated with consensus node 0") | ||
|
|
||
| # ── Step 5: Disassociate registered node from consensus node ──── | ||
| print("\n--- Step 5: Disassociating registered node from consensus node 0 ---") | ||
|
|
||
| try: | ||
| receipt = ( | ||
| NodeUpdateTransaction() | ||
| .set_node_id(0) | ||
| .clear_associated_registered_nodes() | ||
| .freeze_with(client) | ||
| .execute(client) | ||
| ) | ||
| except (PrecheckError, ValueError) as e: | ||
| print(f"Disassociation failed: {e}") | ||
| sys.exit(1) | ||
|
|
||
| if receipt.status != ResponseCode.SUCCESS: | ||
| print(f"Disassociation failed: {ResponseCode(receipt.status).name}") | ||
| sys.exit(1) | ||
|
|
||
| print(f"Registered node {registered_node_id} disassociated from consensus node 0") | ||
|
|
||
| # ── Step 6: Delete the registered node ────────────────────────── | ||
| print("\n--- Step 6: Deleting registered node ---") | ||
|
|
||
| try: | ||
| receipt = ( | ||
| RegisteredNodeDeleteTransaction() | ||
| .set_registered_node_id(registered_node_id) | ||
| .freeze_with(client) | ||
| .sign(admin_key) | ||
| .execute(client) | ||
| ) | ||
| except (PrecheckError, ValueError) as e: | ||
| print(f"Registered node deletion failed: {e}") | ||
| sys.exit(1) | ||
|
|
||
| if receipt.status != ResponseCode.SUCCESS: | ||
| print(f"Registered node deletion failed: {ResponseCode(receipt.status).name}") | ||
| sys.exit(1) | ||
|
|
||
| print("Registered node deleted successfully") | ||
| print("\n--- Registered node lifecycle complete! ---") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| registered_node_lifecycle() | ||
|
aceppaluni marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from enum import IntEnum | ||
|
|
||
|
|
||
| class BlockNodeApi(IntEnum): | ||
| """Maps to the BlockNodeApi enum defined inside RegisteredServiceEndpoint.BlockNodeEndpoint.""" | ||
|
|
||
| OTHER = 0 | ||
| STATUS = 1 | ||
| PUBLISH = 2 | ||
| SUBSCRIBE_STREAM = 3 | ||
| STATE_PROOF = 4 |
57 changes: 57 additions & 0 deletions
57
src/hiero_sdk_python/address_book/block_node_service_endpoint.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from hiero_sdk_python.address_book.block_node_api import BlockNodeApi | ||
| from hiero_sdk_python.address_book.registered_service_endpoint import RegisteredServiceEndpoint | ||
| from hiero_sdk_python.hapi.services.registered_service_endpoint_pb2 import ( | ||
| RegisteredServiceEndpoint as RegisteredServiceEndpointProto, | ||
| ) | ||
|
|
||
|
|
||
| class BlockNodeServiceEndpoint(RegisteredServiceEndpoint): | ||
| """A registered service endpoint for a block node.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| ip_address: bytes | None = None, | ||
| domain_name: str | None = None, | ||
| port: int = 0, | ||
| requires_tls: bool = False, | ||
| endpoint_apis: list[BlockNodeApi] | None = None, | ||
| ) -> None: | ||
| super().__init__(ip_address=ip_address, domain_name=domain_name, port=port, requires_tls=requires_tls) | ||
| self.endpoint_apis: list[BlockNodeApi] = [BlockNodeApi(api) for api in (endpoint_apis or [])] | ||
|
|
||
| def set_endpoint_apis(self, endpoint_apis: list[BlockNodeApi]) -> BlockNodeServiceEndpoint: | ||
| """Set the list of block node endpoint APIs.""" | ||
| self.endpoint_apis = [BlockNodeApi(api) for api in endpoint_apis] | ||
| return self | ||
|
|
||
| def _set_endpoint_type(self, proto: RegisteredServiceEndpointProto) -> None: | ||
| block_node = proto.block_node | ||
| for api in self.endpoint_apis: | ||
| block_node.endpoint_api.append(api.value) | ||
|
|
||
| @classmethod | ||
| def _from_proto_inner( | ||
| cls, | ||
| proto: RegisteredServiceEndpointProto, | ||
| ip_address: bytes | None, | ||
| domain_name: str | None, | ||
| port: int, | ||
| requires_tls: bool, | ||
| ) -> BlockNodeServiceEndpoint: | ||
| apis = [BlockNodeApi(v) for v in proto.block_node.endpoint_api] | ||
| return cls( | ||
| ip_address=ip_address, | ||
| domain_name=domain_name, | ||
| port=port, | ||
| requires_tls=requires_tls, | ||
| endpoint_apis=apis, | ||
| ) | ||
|
aceppaluni marked this conversation as resolved.
|
||
|
|
||
| @classmethod | ||
| def _from_dict_inner(cls, type_data: dict, **base_kwargs) -> BlockNodeServiceEndpoint: | ||
| """Build from the ``block_node`` sub-dict of a mirror-node JSON endpoint.""" | ||
| raw_apis = type_data.get("endpoint_apis") or [] | ||
| apis = [BlockNodeApi[a.upper()] if isinstance(a, str) else BlockNodeApi(a) for a in raw_apis] | ||
| return cls(endpoint_apis=apis, **base_kwargs) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.