Skip to content

feat: Implement registered node lifecycle with create, update, and delete transactions#1970

Draft
danielmarv wants to merge 8 commits intohiero-ledger:mainfrom
danielmarv:block-dis-br
Draft

feat: Implement registered node lifecycle with create, update, and delete transactions#1970
danielmarv wants to merge 8 commits intohiero-ledger:mainfrom
danielmarv:block-dis-br

Conversation

@danielmarv
Copy link
Copy Markdown
Contributor

Description:
This pull request introduces a new abstraction for registered nodes and their service endpoints, expanding the SDK to support richer node and address book modeling. It adds new types and API classes for registered nodes, block/mirror node endpoints, and RPC relay endpoints, and updates the SDK exports accordingly. Additionally, it extends node creation to allow associating registered nodes with consensus nodes.

The most important changes are:

Registered Node and Service Endpoint Abstractions:

  • Added RegisteredNode, RegisteredNodeAddressBook, and RegisteredNodeAddressBookQuery classes for representing and querying registered nodes in the address book. [1] [2] [3]
  • Introduced RegisteredServiceEndpoint as a base type, with concrete subclasses for BlockNodeServiceEndpoint, MirrorNodeServiceEndpoint, and RpcRelayServiceEndpoint, supporting protocol buffer serialization and validation. [1] [2] [3] [4]

Block Node API Support:

  • Added BlockNodeApi enum to enumerate well-known block node APIs, and integrated it into the new block node service endpoint class. [1] [2]

Node Transaction Enhancements:

  • Extended NodeCreateParams and NodeCreateTransaction to support an associated_registered_nodes field, allowing consensus nodes to reference registered nodes. [1] [2] [3]

SDK Exports and Imports:

  • Updated __init__.py and the nodes package to export all new registered node and endpoint types, ensuring they are available to SDK consumers. [1] [2] [3] [4] [5]

These changes lay the groundwork for future features involving registered nodes and service endpoints, and improve the SDK's modeling of network topology.

Related issue(s):

Fixes #1911

Notes for reviewer:

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

Copilot AI review requested due to automatic review settings March 13, 2026 12:45
@danielmarv danielmarv requested review from a team as code owners March 13, 2026 12:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements HIP-1137 registered node lifecycle support in the SDK by introducing registered node models/service endpoints, adding registered-node create/update/delete transactions, and extending consensus node create/update transactions to reference registered nodes.

Changes:

  • Added RegisteredServiceEndpoint hierarchy (block node / mirror node / RPC relay) + BlockNodeApi enum and protobuf round-tripping.
  • Added registered-node transactions (RegisteredNodeCreateTransaction, RegisteredNodeUpdateTransaction, RegisteredNodeDeleteTransaction) and wired deserialization mapping.
  • Extended NodeCreateTransaction / NodeUpdateTransaction and TransactionReceipt to support registered-node associations and receipt IDs, with unit tests.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/test_transaction_receipt.py Adds unit coverage for TransactionReceipt.registered_node_id optional semantics.
tests/unit/registered_service_endpoint_test.py Adds round-trip and validation tests for registered service endpoints.
tests/unit/registered_node_create_transaction_test.py Adds unit tests for registered node create transaction build/schedule/from-bytes behavior.
tests/unit/registered_node_update_transaction_test.py Adds unit tests for registered node update transaction build/schedule/from-bytes behavior and validation.
tests/unit/registered_node_delete_transaction_test.py Adds unit tests for registered node delete transaction build/schedule/from-bytes behavior and validation.
tests/unit/registered_node_test.py Adds unit tests for registered node model immutability and proto round-trip.
tests/unit/registered_node_address_book_test.py Adds unit tests for registered node address book collection behavior/immutability.
tests/unit/registered_node_address_book_query_test.py Adds unit test asserting the deferred query raises NotImplementedError.
tests/unit/node_create_transaction_test.py Extends node create tests to cover associated_registered_nodes field/helpers.
tests/unit/node_update_transaction_test.py Extends node update tests to cover associated_registered_nodes three-state semantics and helpers.
tests/integration/registered_node_transaction_e2e_test.py Adds a skipped E2E skeleton documenting intended lifecycle.
src/hiero_sdk_python/transaction/transaction_receipt.py Adds registered_node_id property and minor formatting/import cleanups.
src/hiero_sdk_python/transaction/transaction.py Registers new registered-node transaction types for Transaction.from_bytes() and formatting tweaks.
src/hiero_sdk_python/nodes/registered_node_create_transaction.py Introduces registered node create transaction implementation.
src/hiero_sdk_python/nodes/registered_node_update_transaction.py Introduces registered node update transaction implementation.
src/hiero_sdk_python/nodes/registered_node_delete_transaction.py Introduces registered node delete transaction implementation.
src/hiero_sdk_python/nodes/node_create_transaction.py Adds associated_registered_nodes field + setters/helpers + proto mapping.
src/hiero_sdk_python/nodes/node_update_transaction.py Adds associated_registered_nodes (wrapper list) + setters/helpers + proto mapping.
src/hiero_sdk_python/nodes/init.py Exports node + registered-node transaction types/params from the nodes package.
src/hiero_sdk_python/address_book/registered_service_endpoint.py Adds base registered endpoint type with validation and proto dispatch.
src/hiero_sdk_python/address_book/block_node_api.py Adds BlockNodeApi enum backed by protobuf values.
src/hiero_sdk_python/address_book/block_node_service_endpoint.py Adds block node endpoint subtype including endpoint_api.
src/hiero_sdk_python/address_book/mirror_node_service_endpoint.py Adds mirror node endpoint subtype.
src/hiero_sdk_python/address_book/rpc_relay_service_endpoint.py Adds RPC relay endpoint subtype.
src/hiero_sdk_python/address_book/registered_node.py Adds immutable registered node model with proto mapping.
src/hiero_sdk_python/address_book/registered_node_address_book.py Adds immutable address-book collection type.
src/hiero_sdk_python/address_book/registered_node_address_book_query.py Adds deferred query skeleton for future mirror node support.
src/hiero_sdk_python/init.py Exports new address-book and registered-node transaction types at the SDK top level.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +146 to +160
def set_associated_registered_nodes(
self, associated_registered_nodes: Optional[List[int]]
) -> "NodeCreateTransaction":
"""
Sets the registered nodes associated with this node create transaction.

Args:
associated_registered_nodes (List[int]):
The registered node IDs associated with this consensus node.

Returns:
NodeCreateTransaction: This transaction instance.
"""
self._require_not_frozen()
self.associated_registered_nodes = list(associated_registered_nodes or [])
Comment on lines +22 to +36
def __post_init__(self) -> None:
"""Normalize the enum value before base validation."""
self.endpoint_api = BlockNodeApi(self.endpoint_api)
super().__post_init__()

def _endpoint_type_proto(self) -> dict[str, RegisteredServiceEndpointProto.BlockNodeEndpoint]:
"""Return the protobuf block-node subtype."""
return {"block_node": RegisteredServiceEndpointProto.BlockNodeEndpoint(endpoint_api=int(self.endpoint_api))}

@classmethod
def _from_proto(cls, proto: RegisteredServiceEndpointProto) -> BlockNodeServiceEndpoint:
"""Build a block node endpoint from protobuf."""
return cls(
endpoint_api=BlockNodeApi(proto.block_node.endpoint_api),
**cls._base_kwargs_from_proto(proto),
Comment on lines +9 to +16
class RegisteredNodeAddressBookQuery:
"""Query contract for registered nodes once mirror support exists."""

def execute(self, client: Client) -> RegisteredNodeAddressBook:
"""Execute the query once the mirror node API exists."""
raise NotImplementedError(
"RegisteredNodeAddressBookQuery is not available until the mirror node API is defined."
)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 13, 2026

Walkthrough

This PR implements HIP-1137 support by introducing registered node transaction types (create, update, delete), a service endpoint hierarchy for block node/mirror node/RPC relay endpoints, immutable read-model types (RegisteredNode, RegisteredNodeAddressBook), and extends existing node transactions to support associating registered nodes. New public APIs are wired through package exports and transaction infrastructure.

Changes

Cohort / File(s) Summary
Public API Exports
src/hiero_sdk_python/__init__.py, src/hiero_sdk_python/nodes/__init__.py
Expanded public API to expose new RegisteredNode, RegisteredServiceEndpoint variants, and RegisteredNode transaction classes; added transaction type re-exports via nodes package.
Service Endpoint Hierarchy
src/hiero_sdk_python/address_book/registered_service_endpoint.py, src/hiero_sdk_python/address_book/block_node_service_endpoint.py, src/hiero_sdk_python/address_book/mirror_node_service_endpoint.py, src/hiero_sdk_python/address_book/rpc_relay_service_endpoint.py
Introduced abstract RegisteredServiceEndpoint base class with address/port/TLS validation and protobuf dispatch mechanism; three concrete subclasses for block node, mirror node, and RPC relay endpoints with endpoint-specific proto serialization.
Block Node API Enum
src/hiero_sdk_python/address_book/block_node_api.py
Added BlockNodeApi IntEnum mapping OTHER, STATUS, PUBLISH, SUBSCRIBE_STREAM, STATE_PROOF to protobuf BlockNodeEndpoint values.
Registered Node Read Models
src/hiero_sdk_python/address_book/registered_node.py, src/hiero_sdk_python/address_book/registered_node_address_book.py, src/hiero_sdk_python/address_book/registered_node_address_book_query.py
Introduced immutable RegisteredNode dataclass with protobuf round-trip support; RegisteredNodeAddressBook collection type with iteration/indexing; RegisteredNodeAddressBookQuery skeleton (raises NotImplementedError pending mirror node API).
Registered Node Transactions
src/hiero_sdk_python/nodes/registered_node_create_transaction.py, src/hiero_sdk_python/nodes/registered_node_update_transaction.py, src/hiero_sdk_python/nodes/registered_node_delete_transaction.py
Added three transaction types with params dataclasses, setter chains, service endpoint validation (1–50 endpoints), protobuf body construction, transaction body assembly, gRPC method routing, and round-trip deserialization support.
Extensions to Existing Node Transactions
src/hiero_sdk_python/nodes/node_create_transaction.py, src/hiero_sdk_python/nodes/node_update_transaction.py
Added associated_registered_nodes field to NodeCreateParams and NodeCreateTransaction; added associated_registered_nodes field, setter, adder, and clearer to NodeUpdateTransaction; updated proto body building to include associated node list serialization.
Core Transaction Infrastructure
src/hiero_sdk_python/transaction/transaction.py, src/hiero_sdk_python/transaction/transaction_receipt.py
Registered new transaction types (registeredNodeCreate, registeredNodeUpdate, registeredNodeDelete, atomic_batch) in _get_transaction_class factory; added registered_node_id property to TransactionReceipt with optional normalization.
Tests
tests/integration/registered_node_transaction_e2e_test.py, tests/unit/registered_node_*_test.py, tests/unit/registered_service_endpoint_test.py, tests/unit/node_*_transaction_test.py, tests/unit/test_transaction_receipt.py
Comprehensive unit and integration test coverage for new transaction classes, service endpoints, read models, address book, and transaction receipt extensions; validates proto round-trips, validation logic, endpoint capacity constraints, and immutability.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: implementing registered node lifecycle with create, update, and delete transactions, which is the primary focus of the PR.
Description check ✅ Passed The description is well-structured and directly related to the changeset, explaining the registered node abstractions, service endpoints, and node transaction enhancements being introduced.
Linked Issues check ✅ Passed The PR addresses all key requirements from issue #1911: implements three registered-node transaction classes, RegisteredServiceEndpoint hierarchy with BlockNodeApi enum, read-model types, updates to TransactionReceipt and node transactions, and SDK infrastructure wiring.
Out of Scope Changes check ✅ Passed All changes align with the scope defined in issue #1911; no out-of-scope modifications detected. Changes focus on registered node support, service endpoints, and related SDK updates as specified.
Docstring Coverage ✅ Passed Docstring coverage is 98.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #1911

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unit/node_create_transaction_test.py (1)

296-309: 🧹 Nitpick | 🔵 Trivial

Consider adding add_associated_registered_node to frozen state tests.

The frozen state test covers set_associated_registered_nodes but not add_associated_registered_node. Both setters should be tested for immutability after freeze.

🧪 Suggested test addition
     test_cases = [
         ("set_account_id", node_params["account_id"]),
         ("set_description", node_params["description"]),
         ("set_gossip_endpoints", node_params["gossip_endpoints"]),
         ("set_service_endpoints", node_params["service_endpoints"]),
         (
             "set_associated_registered_nodes",
             node_params["associated_registered_nodes"],
         ),
+        ("add_associated_registered_node", 99),
         ("set_gossip_ca_certificate", node_params["gossip_ca_certificate"]),

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 032af632-4372-4540-95ab-eb5f8ea28fb3

📥 Commits

Reviewing files that changed from the base of the PR and between 86a18f9 and 3af31c3.

📒 Files selected for processing (28)
  • src/hiero_sdk_python/__init__.py
  • src/hiero_sdk_python/address_book/block_node_api.py
  • src/hiero_sdk_python/address_book/block_node_service_endpoint.py
  • src/hiero_sdk_python/address_book/mirror_node_service_endpoint.py
  • src/hiero_sdk_python/address_book/registered_node.py
  • src/hiero_sdk_python/address_book/registered_node_address_book.py
  • src/hiero_sdk_python/address_book/registered_node_address_book_query.py
  • src/hiero_sdk_python/address_book/registered_service_endpoint.py
  • src/hiero_sdk_python/address_book/rpc_relay_service_endpoint.py
  • src/hiero_sdk_python/nodes/__init__.py
  • src/hiero_sdk_python/nodes/node_create_transaction.py
  • src/hiero_sdk_python/nodes/node_update_transaction.py
  • src/hiero_sdk_python/nodes/registered_node_create_transaction.py
  • src/hiero_sdk_python/nodes/registered_node_delete_transaction.py
  • src/hiero_sdk_python/nodes/registered_node_update_transaction.py
  • src/hiero_sdk_python/transaction/transaction.py
  • src/hiero_sdk_python/transaction/transaction_receipt.py
  • tests/integration/registered_node_transaction_e2e_test.py
  • tests/unit/node_create_transaction_test.py
  • tests/unit/node_update_transaction_test.py
  • tests/unit/registered_node_address_book_query_test.py
  • tests/unit/registered_node_address_book_test.py
  • tests/unit/registered_node_create_transaction_test.py
  • tests/unit/registered_node_delete_transaction_test.py
  • tests/unit/registered_node_test.py
  • tests/unit/registered_node_update_transaction_test.py
  • tests/unit/registered_service_endpoint_test.py
  • tests/unit/test_transaction_receipt.py

Comment on lines +13 to +19
from hiero_sdk_python.hapi.services.registered_node_create_pb2 import (
RegisteredNodeCreateTransactionBody,
)
from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import (
SchedulableTransactionBody,
)
from hiero_sdk_python.hapi.services.transaction_pb2 import TransactionBody
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and inspect its actual content
find . -name "registered_node_create_transaction.py" -type f

Repository: hiero-ledger/hiero-sdk-python

Length of output: 139


🏁 Script executed:

# Search for protobuf version pin or configuration
find . -type f \( -name "*.py" -o -name "*.txt" -o -name "*.toml" -o -name "*.cfg" -o -name "*.json" \) | head -20 | xargs grep -l "0\.66\.0\|hedera-protobufs\|protobuf.*version" 2>/dev/null | head -10

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check what registered_node_create related files exist in hapi
find . -path "*/hapi/*" -name "*registered*node*" -type f

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Search for all registered_node imports in the codebase
rg "registered_node" --type py

Repository: hiero-ledger/hiero-sdk-python

Length of output: 27981


🏁 Script executed:

# Read the specific file to see lines 13-19 and 85-104
cat -n src/hiero_sdk_python/nodes/registered_node_create_transaction.py | sed -n '1,105p'

Repository: hiero-ledger/hiero-sdk-python

Length of output: 5273


🏁 Script executed:

# Check if registered_node_create_pb2 exists in hapi
find . -path "*/hapi/services*" -name "*registered_node*pb2*" -type f

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check protobuf version or pinning information
find . -type f \( -name "*.txt" -o -name "*.toml" -o -name "*.cfg" -o -name "setup.py" \) | xargs grep -l "0\.66\|hedera-protobufs\|hapi" 2>/dev/null | head -5

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check what's in the hapi directory structure
find . -path "*/hapi/services*" -type d | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# List all pb2 files in hapi/services
find . -path "*/hapi/services*" -name "*pb2.py" -type f | head -30

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check requirements or setup files for hedera-protobufs version
find . -name "requirements*.txt" -o -name "setup.py" -o -name "pyproject.toml" | xargs cat 2>/dev/null | grep -i "hedera-protobuf\|hapi\|protobuf" | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 100


🏁 Script executed:

# Check for git submodules
cat .gitmodules 2>/dev/null || echo "No .gitmodules file"

Repository: hiero-ledger/hiero-sdk-python

Length of output: 92


🏁 Script executed:

# Check if hapi is mentioned as a dependency anywhere
rg "hapi|hedera.protobufs|hedera-protobufs" --type py --type toml --type txt | head -40

Repository: hiero-ledger/hiero-sdk-python

Length of output: 4913


🏁 Script executed:

# Look for setup.py, pyproject.toml, or package metadata
ls -la | grep -E "setup|pyproject|package"

Repository: hiero-ledger/hiero-sdk-python

Length of output: 138


🏁 Script executed:

# Check all imports from hapi in the codebase to understand how it's used
rg "from hiero_sdk_python.hapi" -A 1 | head -40

Repository: hiero-ledger/hiero-sdk-python

Length of output: 2902


🏁 Script executed:

# List the actual directory structure of hapi
ls -la src/hiero_sdk_python/hapi/

Repository: hiero-ledger/hiero-sdk-python

Length of output: 146


🏁 Script executed:

# List contents of hapi/services
ls -la src/hiero_sdk_python/hapi/services/ | head -50

Repository: hiero-ledger/hiero-sdk-python

Length of output: 155


🏁 Script executed:

# Check if registered_node files exist anywhere
find src/hiero_sdk_python/hapi -name "*registered*" -type f

Repository: hiero-ledger/hiero-sdk-python

Length of output: 133


🏁 Script executed:

# Check transaction_pb2 to see what transaction body fields exist
rg "registeredNode|nodeCreate" src/hiero_sdk_python/hapi/services/transaction_pb2.py 2>/dev/null | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Read pyproject.toml to see all dependencies
cat pyproject.toml

Repository: hiero-ledger/hiero-sdk-python

Length of output: 2760


🏁 Script executed:

# Check if there's a hedera-protobufs dependency specification
rg "hedera.*protobuf|hapi" pyproject.toml

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check git log or comments that might indicate which version of hapi is expected
git log --oneline --all | grep -i "protobuf\|hapi\|registered" | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 774


🏁 Script executed:

# Check if hapi is generated or built - look for setup/build scripts
find . -name "setup.py" -o -name "Makefile" -o -name "*.sh" | xargs cat 2>/dev/null | grep -i "hapi\|protobuf\|generate" | head -30

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check if there's a proto directory or build configuration
find . -path "./.*" -prune -o -name "*.proto" -type f -print

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Look for pyproject.toml build configuration details
cat pyproject.toml | grep -A 20 "build-system"

Repository: hiero-ledger/hiero-sdk-python

Length of output: 503


🏁 Script executed:

# Check the actual imports in the file being reviewed - see if they work
python3 -c "from hiero_sdk_python.nodes.registered_node_create_transaction import RegisteredNodeCreateTransaction" 2>&1

Repository: hiero-ledger/hiero-sdk-python

Length of output: 202


🌐 Web query:

hedera-protobufs v0.66.0 registered_node_create services transaction body

💡 Result:

In hedera-protobufs v0.66.0, the “node create” transaction is not named registered_node_create in protobuf; it is:

  • gRPC service method: AddressBookService.createNode(Transaction) -> TransactionResponse [1]
  • TransactionBody oneof field: nodeCreate = 54 of type com.hedera.hapi.node.addressbook.NodeCreateTransactionBody [2]

NodeCreateTransactionBody (from services/node_create.proto) has these fields (with ordinals): account_id (1), description (2), gossip_endpoint (3), service_endpoint (4), gossip_ca_certificate (5), grpc_certificate_hash (6), admin_key (7), decline_reward (8), grpc_proxy_endpoint (9) [3].

Sources: [1] [2] [3]


🌐 Web query:

hedera-protobufs registered_node_create latest version

💡 Result:

The latest hashgraph/hedera-protobufs version tag on GitHub is v0.73.0-pectra-preview-alpha.3 (tagged Mar 11, 2026). [1]

If you want the latest non-preview/stable tag (i.e., not -alpha/-rc), it is v0.71.3 (tagged Mar 9, 2026). [1]

Sources
[1] GitHub tags for hashgraph/hedera-protobufs (shows latest tags and dates) https://github.com/hashgraph/hedera-protobufs/tags


🌐 Web query:

hedera-protobufs registered_node_create_pb2 protobuf schema

💡 Result:

In hashgraph/hedera-protobufs, the protobuf schema that defines the “node create” transaction body (what Python generators typically emit as a *_pb2.py module) is:

  • services/node_create.proto
  • Defines message NodeCreateTransactionBody in package com.hedera.hapi.node.addressbook. [1]

Schema (abridged to the actual fields):

message NodeCreateTransactionBody {
  proto.AccountID account_id = 1;
  string description = 2;
  repeated proto.ServiceEndpoint gossip_endpoint = 3;
  repeated proto.ServiceEndpoint service_endpoint = 4;
  bytes gossip_ca_certificate = 5;
  bytes grpc_certificate_hash = 6;
  proto.Key admin_key = 7;
  bool decline_reward = 8;
  proto.ServiceEndpoint grpc_proxy_endpoint = 9;
}
```[1]

---

🌐 Web query:

`hedera-protobufs "registered_node_create.proto"`

💡 Result:

`registered_node_create.proto` does **not** appear to exist in the `hashgraph/hedera-protobufs` repository (at least on the `main` branch).

If you’re looking for the protobufs related to *creating a node* and the *state representation of a node*, they’re here instead:

- **Node create transaction body:** `services/node_create.proto` (package `com.hedera.hapi.node.addressbook`) [1]  
- **Node state definition (address book):** `services/state/addressbook/node.proto` (package `com.hedera.hapi.node.state.addressbook`) [2]

**Sources:**  
[1] `hashgraph/hedera-protobufs` – `services/node_create.proto` [1]  
[2] `hashgraph/hedera-protobufs` – `services/state/addressbook/node.proto` [2]

---

🌐 Web query:

`hedera-protobufs "registered_node" node lifecycle v0.71 v0.72 v0.73`

💡 Result:

In `hashgraph/hedera-protobufs`, the **v0.71 / v0.72 / v0.73** identifiers you mentioned *do exist as protobuf repo tags* (e.g., `v0.71.3`, `v0.72.0-rc.2`, and multiple `v0.73.0-*` preview tags). [1]

However, in the **v0.71.3** protobuf set:
- The node “lifecycle” is represented via **Node transactions** (`node_create.proto`, `node_update.proto`, `node_delete.proto`, plus `node_stake_update.proto`) rather than a `registered_node` message. [2]
- Related “lifecycle / transition” state tracking appears in **`services/state/roster/roster_state.proto`** (e.g., `transplant_in_progress` indicates the network is still processing node create/update/delete as part of a roster transplant). [3]
- I did **not** find any protobuf message/type literally named **`registered_node`** in the areas of the v0.71.3 tag I inspected (platform state + roster state + services index). [2][3]

If you meant **Consensus Node release v0.71+** (node software), Hedera’s release notes show **Consensus Node v0.71** mainnet update scheduled for **March 18, 2026**, but that’s separate from whether a protobuf message is named `registered_node`. [4]

**Sources:**  
[1] hedera-protobufs tags (shows v0.71.3 / v0.72.0-rc.2 / v0.73.0-*) ([github.com](https://github.com/hashgraph/hedera-protobufs/tags))  
[2] v0.71.3 `services/` directory listing (shows node_create/update/delete protos) ([github.com](https://github.com/hashgraph/hedera-protobufs/tree/v0.71.3/services))  
[3] v0.71.3 `roster_state.proto` (mentions node create/update/delete processing + `transplant_in_progress`) ([github.com](https://github.com/hashgraph/hedera-protobufs/blob/v0.71.3/services/state/roster/roster_state.proto))  
[4] Hedera docs: Consensus Node Release v0.71 (mainnet update date) ([docs.hedera.com](https://docs.hedera.com/hedera/networks/release-notes/services?utm_source=openai))

Citations:

- 1: https://github.com/hashgraph/hedera-protobufs/tags
- 2: https://github.com/hashgraph/hedera-protobufs/tree/v0.71.3/services
- 3: https://github.com/hashgraph/hedera-protobufs/blob/v0.71.3/services/state/roster/roster_state.proto
- 4: https://docs.hedera.com/hedera/networks/release-notes/services?utm_source=openai

---

🌐 Web query:

`hedera-protobufs "registeredNode" TransactionBody oneof`

💡 Result:

In the **Hedera protobufs**, `TransactionBody` has a `oneof` (often exposed in generated code as a `data`/`Data` union) for the *transaction type*. For node/address-book related transactions, the `oneof` alternatives are **`nodeCreate`**, **`nodeUpdate`**, **`nodeDelete`**, and **`nodeStakeUpdate`**—**not** `registeredNode`. The generated APIs expose these as fields/getters like `GetNodeCreate()`, `GetNodeUpdate()`, etc. [1]

So if you’re looking for where to set a “node registration” operation inside `TransactionBody`, it’s done by choosing the appropriate `oneof` variant (typically `nodeCreate`), not `registeredNode`. Hedera’s docs also describe that the transaction body contains *one of* the operation types (transaction kinds). [2]

**Sources:**  
[1] `TransactionBody` generated API showing `GetNodeCreate/GetNodeUpdate/GetNodeDelete/GetNodeStakeUpdate` (Go package docs) ([pkg.go.dev](https://pkg.go.dev/github.com/cordialsys/hedera-protobufs-go/services))  
[2] Hedera docs: transaction body contains one of the operation types ([docs.hedera.com](https://docs.hedera.com/hedera/core-concepts/transactions-and-queries/transaction-properties?utm_source=openai))

Citations:

- 1: https://pkg.go.dev/github.com/cordialsys/hedera-protobufs-go/services
- 2: https://docs.hedera.com/hedera/core-concepts/transactions-and-queries/transaction-properties?utm_source=openai

---

🌐 Web query:

`hedera-protobufs main branch registered_node_create registeredNode latest`

💡 Result:

In the **`hashgraph/hedera-protobufs` `main` branch**, there is **no** protobuf message/file named **`registered_node_create`** nor a message/type named **`RegisteredNode`** (at least not under the current `services/` and `services/state/` trees).

What *does* exist (and appears to be the “latest” node-create equivalent in `main`) is:

- **`services/node_create.proto` → `message NodeCreateTransactionBody`** with fields:  
  1. `account_id`  
  2. `description`  
  3. `gossip_endpoint`  
  4. `service_endpoint`  
  5. `gossip_ca_certificate`  
  6. `grpc_certificate_hash`  
  7. `admin_key`  
  8. `decline_reward`  
  9. `grpc_proxy_endpoint`  [1]

And the corresponding address book state type is:

- **`services/state/addressbook/node.proto` → `message Node`** (state representation of a node; includes `node_id`, `account_id`, `description`, and endpoint/cert-related fields) [2]

**Sources:**  
[1] `services/node_create.proto` (main) — `NodeCreateTransactionBody` fields and semantics  
[2] `services/state/addressbook/node.proto` (main) — `Node` state message

---



</details>

**The protobuf imports reference modules and fields that do not exist in the pinned v0.66.0 schema.**

This file imports `registered_node_create_pb2`, writes to the `registeredNodeCreate` oneof field in `TransactionBody`, and calls the `createRegisteredNode` gRPC method. However, the canonical hedera-protobufs v0.66.0 sources do not expose:

- `services/registered_node_create.proto` or `registered_node_create_pb2` module
- `registeredNodeCreate` field in `TransactionBody` (only `nodeCreate` exists)
- `createRegisteredNode` method on `AddressBookService` (only `createNode` exists)

The correct imports for v0.66.0 should be from `node_create_pb2`, the oneof field should be `nodeCreate`, and the gRPC method should be `createNode`. 

If this PR intentionally targets a newer protobuf revision that introduces `registered_node_create.proto`, the hedera-protobufs version pinning and regenerated `hapi` stubs must be updated in the same change. Otherwise, the code will fail at import/runtime with the current v0.66.0 schema.

Same issue applies to lines 85–104 (build methods) and the related `RegisteredNodeUpdateTransaction` and `RegisteredNodeDeleteTransaction` classes.

<!-- fingerprinting:phantom:medusa:grasshopper -->

<!-- This is an auto-generated comment by CodeRabbit -->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@manishdait @exploreriii why can’t we use v0.73.0-pectra-preview-alpha.3 or a newer version for review instead of v0.66.0?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the generate_proto.py we use v0.72.0-rc.2
so it should be better we use the same

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coderabbit suggest using v0.71.3 as a stable version, will need a confirmation on this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but for that it does not have the fields for this hip

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

version 0.71.3 was tagged last week, so it should be up to date enough.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree that the 0.71.3 is the latest stable release, but this hip is the newest one and currently if we want to add this or work on this the protobuf files related to this are found early release like the 0.72.0-rc.2 or 0.73.0-pectra-preview-alpha

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to 0.72.0-rc.2

Comment on lines +23 to +30
@dataclass
class RegisteredNodeCreateParams:
"""Fields supported by a registered node create transaction."""

admin_key: PublicKey | None = None
description: str | None = None
service_endpoints: list[RegisteredServiceEndpoint] = field(default_factory=list)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The create transaction surface is missing required fields from the linked contract.

admin_key stays nullable all the way through _build_proto_body(), and node_account_id is absent from the params object, public API, serialization, and _from_protobuf(). That lets callers serialize a create transaction without the required admin key and gives them no way to set or round-trip the registered node’s associated account. This is distinct from the inherited Transaction.node_account_id routing field.

Also applies to: 35-67, 76-83, 106-118

Comment on lines +17 to +23
class RegisteredNodeDeleteTransaction(Transaction):
"""Delete an existing registered node."""

def __init__(self, registered_node_id: int | None = None) -> None:
"""Initialize a registered node delete transaction."""
super().__init__()
self.registered_node_id = registered_node_id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider documenting privileged operation requirements.

Per coding guidelines for the nodes module, privileged operations should have clear documentation about network/permission requirements. The docstring could mention that this operation requires admin key or governance authorization.

📝 Suggested docstring enhancement
 class RegisteredNodeDeleteTransaction(Transaction):
-    """Delete an existing registered node."""
+    """Delete an existing registered node.
+
+    This is a privileged administrative operation that requires either admin_key
+    authorization or governance authorization to execute successfully.
+    """

Comment on lines +15 to +21
from hiero_sdk_python.hapi.services.registered_node_update_pb2 import (
RegisteredNodeUpdateTransactionBody,
)
from hiero_sdk_python.hapi.services.schedulable_transaction_body_pb2 import (
SchedulableTransactionBody,
)
from hiero_sdk_python.hapi.services.transaction_pb2 import TransactionBody
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, read the file to understand what it's doing
head -n 120 src/hiero_sdk_python/nodes/registered_node_update_transaction.py | cat -n

Repository: hiero-ledger/hiero-sdk-python

Length of output: 6129


🏁 Script executed:

# Check if the hapi directory has registered_node_update_pb2
find . -name "*registered_node_update*" -type f 2>/dev/null | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 195


🏁 Script executed:

# Check what proto files exist in hapi/services
ls -la src/hiero_sdk_python/hapi/services/ | grep -E "\.py$" | head -30

Repository: hiero-ledger/hiero-sdk-python

Length of output: 155


🌐 Web query:

site:github.com/hashgraph/hedera-protobufs "v0.66.0" registered_node_update.proto

💡 Result:

In the hashgraph/hedera-protobufs v0.66.0 tag, there is no file named registered_node_update.proto in the services/ directory; the node-related protobufs present there include node_create.proto, node_delete.proto, node_stake_update.proto, and node_update.proto. [1]

If you meant the node-update transaction body, it’s defined in:

  • services/node_update.proto (tag v0.66.0) [2]

Sources:
[1] services/ directory listing at tag v0.66.0 https://github.com/hashgraph/hedera-protobufs/tree/v0.66.0/services
[2] services/node_update.proto at tag v0.66.0 https://github.com/hashgraph/hedera-protobufs/blob/v0.66.0/services/node_update.proto


🌐 Web query:

site:github.com/hashgraph/hedera-protobufs/blob/v0.66.0 services/transaction.proto

💡 Result:

In services/transaction.proto at tag v0.66.0, Hedera defines:

  • message Transaction: now a (mostly legacy) wrapper whose only supported field is bytes signedTransactionBytes = 5, which MUST contain a serialized SignedTransaction; the older fields (body, sigs, sigMap, bodyBytes) are deprecated and MUST NOT be set. Also notes a current transaction size limit of 6144 bytes for signedTransactionBytes. [1]
  • message TransactionBody: common transaction header fields (transactionID, nodeAccountID, transactionFee, transactionValidDuration, memo, etc.), plus a large oneof data selecting exactly one transaction type (Crypto/File/Contract/Consensus/Token/Schedule/Ethereum/Node ops, TokenReject/Airdrop/Cancel/Claim, platform StateSignatureTransaction, auxiliary hintTS/history-proof transactions, hook/lambda-related transactions, etc.). It also includes repeated CustomFeeLimit max_custom_fees = 1001. [1][2]
  • message AtomicBatchTransactionBody: supports atomic batches via repeated bytes transactions = 1;. [2]

Sources: transaction.proto raw at v0.66.0. [1][2]


🌐 Web query:

site:github.com/hashgraph/hedera-protobufs registered_node_update.proto

💡 Result:

registered_node_update.proto does not exist in hashgraph/hedera-protobufs (at least on the main branch).

The node-update protobuf in that repo is instead:

  • services/node_update.proto (package com.hedera.hapi.node.addressbook) [2]

You can also see node_update.proto listed in the services/ directory alongside node_create.proto, node_delete.proto, etc. [1]

Sources: [1] (github.com) [2] (github.com)

Citations:


🏁 Script executed:

# Check if hapi stubs directory exists and what it contains
find . -type d -name "hapi" 2>/dev/null | head -5
find . -type f -name "*_pb2.py" 2>/dev/null | grep registered | head -10

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check the schema pin in the codebase
grep -r "v0.66.0" . --include="*.py" --include="*.md" --include="*.txt" 2>/dev/null | head -10
grep -r "hedera-protobufs" . --include="*.py" --include="*.md" 2>/dev/null | grep -i version | head -10

Repository: hiero-ledger/hiero-sdk-python

Length of output: 125


🏁 Script executed:

# Check if hapi stubs are actually present in the repository
find . -path "*/hapi/services/*_pb2.py" -type f 2>/dev/null | wc -l
find . -path "*/hapi/services/*registered*" -type f 2>/dev/null

Repository: hiero-ledger/hiero-sdk-python

Length of output: 74


🏁 Script executed:

# Check what transaction types are in the TransactionBody oneof at the pinned version
# by looking at how other transactions use _to_proto
grep -r "\.CopyFrom(" src/hiero_sdk_python/ --include="*.py" -B 2 | grep -E "(CopyFrom|def build_transaction_body)" | head -30

Repository: hiero-ledger/hiero-sdk-python

Length of output: 4242


🏁 Script executed:

# Look at how other node transactions are structured
head -n 25 src/hiero_sdk_python/nodes/node_update_transaction.py

Repository: hiero-ledger/hiero-sdk-python

Length of output: 920


🏁 Script executed:

# Check if RegisteredNodeUpdateTransaction appears in __init__ or is actually exported
grep -r "RegisteredNodeUpdateTransaction" src/hiero_sdk_python/ --include="*.py" | grep -v "test" | grep -v ".pyc"

Repository: hiero-ledger/hiero-sdk-python

Length of output: 1953


🏁 Script executed:

# Check if registered_node_update_pb2 is actually generated/vendored
find . -name "registered_node_update_pb2.py" -o -name "registered_node_update_pb2.pyi" 2>/dev/null

Repository: hiero-ledger/hiero-sdk-python

Length of output: 55


🏁 Script executed:

# Check the actual TransactionBody oneof definition in the generated stubs by looking at node_update
grep -A 5 "def _build_proto_body" src/hiero_sdk_python/nodes/node_update_transaction.py | head -15
grep -A 2 "transaction_body\." src/hiero_sdk_python/nodes/node_update_transaction.py | grep "CopyFrom"

Repository: hiero-ledger/hiero-sdk-python

Length of output: 373


🌐 Web query:

hedera-protobufs "registeredNodeUpdate" oneof

💡 Result:

In the current hashgraph/hedera-protobufs main branch, I can’t find any oneof (or any field/message) named registeredNodeUpdate in the .proto sources I checked (including services/node_update.proto and the address book state protos). (github.com)

If what you’re looking for is “how node (address book) updates show up” in Hedera block stream state changes, they’re modeled via state_changes.proto like this:

  • A StateChange chooses what kind of change happened via oneof change_operation (e.g., map_update, map_delete, etc.). (github.com)
  • For virtual-map updates, the key type is selected via MapChangeKey.oneof key_choice, which includes node_id_key. (github.com)
  • The value type is selected via MapChangeValue.oneof value_choice, which includes node_value of type com.hedera.hapi.node.state.addressbook.Node. (github.com)

So, if you expected something like registeredNodeUpdate as a oneof arm, the closest equivalent in today’s protos is typically:
StateChange.map_updateMapChangeKey.node_id_key + MapChangeValue.node_value. (github.com)

Citations:


🏁 Script executed:

# Check if there's documentation or comments about registeredNodeUpdate vs nodeUpdate
grep -r "registeredNodeUpdate\|registered.*node.*update" . --include="*.py" --include="*.md" -i | grep -v ".pyc" | grep -v "test" | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 2372


🏁 Script executed:

# Verify: does registered_node_update_pb2.py exist anywhere in the generated stubs?
ls -la src/hiero_sdk_python/hapi/services/ 2>&1 | head -20

Repository: hiero-ledger/hiero-sdk-python

Length of output: 155


🏁 Script executed:

# Check the actual gRPC service definition - what methods exist?
grep -r "updateNode\|updateRegisteredNode" src/hiero_sdk_python/ --include="*.py" | grep -v test | grep -v ".pyc"

Repository: hiero-ledger/hiero-sdk-python

Length of output: 353


The protobuf schema for RegisteredNodeUpdateTransaction does not exist in any version of hedera-protobufs, including v0.66.0.

The file attempts to import registered_node_update_pb2 (line 15), which does not exist in the repository or in hashgraph/hedera-protobufs at any tag. Additionally, it references oneofs (registeredNodeUpdate in TransactionBody and SchedulableTransactionBody) and a gRPC method (updateRegisteredNode on AddressBookService) that do not exist in the canonical v0.66.0 schema or any version thereof.

For comparison, NodeUpdateTransaction correctly imports node_update_pb2 and uses the nodeUpdate oneof with the updateNode gRPC method, which all exist in v0.66.0. The protobuf stubs must be regenerated from the canonical schema sources, and this transaction class (exported as a public API in __init__.py) must either align with an actual protobuf definition or be removed if the feature is not yet available upstream.

As per coding guidelines: Flag any SDK attribute that references a proto field name that does not exist in the schema (lines 15–21, 96–115).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we are using the newer verssion

Comment on lines +25 to +32
@dataclass
class RegisteredNodeUpdateParams:
"""Fields supported by a registered node update transaction."""

registered_node_id: int | None = None
admin_key: PublicKey | None = None
description: str | None = None
service_endpoints: list[RegisteredServiceEndpoint] = field(default_factory=list)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

node_account_id cannot be updated through this API.

The linked issue calls out node_account_id as an updateable field, but this class omits it from the params object, setter surface, _build_proto_body(), and _from_protobuf(). Right now callers cannot express or round-trip that part of the update contract, and the base Transaction.node_account_id submission field is not a substitute.

Also applies to: 38-77, 86-94, 117-130

Comment on lines +147 to +158
def test_from_bytes_restores_registered_node_create_transaction(mock_client, registered_node_create_params):
"""Transaction.from_bytes should restore the registered-node create type."""
transaction = RegisteredNodeCreateTransaction(registered_node_create_params)
transaction.freeze_with(mock_client)

restored = Transaction.from_bytes(transaction.to_bytes())

assert isinstance(restored, RegisteredNodeCreateTransaction)
assert restored.description == registered_node_create_params.description
assert restored.admin_key == registered_node_create_params.admin_key
assert len(restored.service_endpoints) == 1
assert isinstance(restored.service_endpoints[0], RegisteredServiceEndpoint)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Assert the concrete endpoint subtype in the round-trip test.

Line 158 only proves that deserialization returned some RegisteredServiceEndpoint. This still passes if BlockNodeServiceEndpoint is downgraded to the base type or loses endpoint_api, which is exactly the regression this PR’s new hierarchy needs to guard against.

Suggested test hardening
     assert isinstance(restored, RegisteredNodeCreateTransaction)
     assert restored.description == registered_node_create_params.description
     assert restored.admin_key == registered_node_create_params.admin_key
     assert len(restored.service_endpoints) == 1
     assert isinstance(restored.service_endpoints[0], RegisteredServiceEndpoint)
+    assert isinstance(restored.service_endpoints[0], BlockNodeServiceEndpoint)
+    assert restored.service_endpoints[0].endpoint_api == BlockNodeApi.PUBLISH
+    assert (
+        restored.service_endpoints[0].domain_name
+        == registered_node_create_params.service_endpoints[0].domain_name
+    )
+    assert restored.service_endpoints[0].port == registered_node_create_params.service_endpoints[0].port
+    assert (
+        restored.service_endpoints[0].requires_tls
+        == registered_node_create_params.service_endpoints[0].requires_tls
+    )

As per coding guidelines, tests/unit/**/*: 'Assert return types where relevant' and 'Unit tests should be extensive - test even if we don't expect users to use it currently.'

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_from_bytes_restores_registered_node_create_transaction(mock_client, registered_node_create_params):
"""Transaction.from_bytes should restore the registered-node create type."""
transaction = RegisteredNodeCreateTransaction(registered_node_create_params)
transaction.freeze_with(mock_client)
restored = Transaction.from_bytes(transaction.to_bytes())
assert isinstance(restored, RegisteredNodeCreateTransaction)
assert restored.description == registered_node_create_params.description
assert restored.admin_key == registered_node_create_params.admin_key
assert len(restored.service_endpoints) == 1
assert isinstance(restored.service_endpoints[0], RegisteredServiceEndpoint)
def test_from_bytes_restores_registered_node_create_transaction(mock_client, registered_node_create_params):
"""Transaction.from_bytes should restore the registered-node create type."""
transaction = RegisteredNodeCreateTransaction(registered_node_create_params)
transaction.freeze_with(mock_client)
restored = Transaction.from_bytes(transaction.to_bytes())
assert isinstance(restored, RegisteredNodeCreateTransaction)
assert restored.description == registered_node_create_params.description
assert restored.admin_key == registered_node_create_params.admin_key
assert len(restored.service_endpoints) == 1
assert isinstance(restored.service_endpoints[0], RegisteredServiceEndpoint)
assert isinstance(restored.service_endpoints[0], BlockNodeServiceEndpoint)
assert restored.service_endpoints[0].endpoint_api == BlockNodeApi.PUBLISH
assert (
restored.service_endpoints[0].domain_name
== registered_node_create_params.service_endpoints[0].domain_name
)
assert restored.service_endpoints[0].port == registered_node_create_params.service_endpoints[0].port
assert (
restored.service_endpoints[0].requires_tls
== registered_node_create_params.service_endpoints[0].requires_tls
)

Comment on lines +18 to +29
def test_constructor_with_parameter():
"""The constructor should populate the registered node id."""
transaction = RegisteredNodeDeleteTransaction(registered_node_id=9)

assert transaction.registered_node_id == 9


def test_constructor_default_values():
"""Default construction should produce an unset id."""
transaction = RegisteredNodeDeleteTransaction()

assert transaction.registered_node_id is None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add fluent setter return-self assertion.

Per coding guidelines PRIORITY 1, fluent setters must be tested to return self. Add a test verifying that set_registered_node_id supports method chaining:

🧪 Suggested test case
+def test_set_registered_node_id_returns_self():
+    """Fluent setters should return self for method chaining."""
+    transaction = RegisteredNodeDeleteTransaction()
+
+    result = transaction.set_registered_node_id(9)
+
+    assert result is transaction

Comment on lines +20 to +40
def test_registered_node_roundtrip():
"""Registered nodes should round-trip through protobuf."""
admin_key = PrivateKey.generate_ed25519().public_key()
endpoint = BlockNodeServiceEndpoint(
domain_name="block.example.com",
port=443,
requires_tls=True,
endpoint_api=BlockNodeApi.STATUS,
)
node = RegisteredNode(
registered_node_id=12,
admin_key=admin_key,
description="mirrorable block node",
service_endpoints=(endpoint,),
)

proto = node._to_proto()
roundtrip = RegisteredNode._from_proto(proto)

assert isinstance(proto, RegisteredNodeProto)
assert roundtrip == node
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Strong roundtrip test with room for edge case coverage.

The roundtrip test correctly validates protobuf serialization with a populated RegisteredNode. Consider adding coverage for:

  1. Minimal node (only registered_node_id set)
  2. Node with None admin_key to verify optional field handling
🧪 Suggested additional test
+def test_registered_node_minimal_roundtrip():
+    """Registered nodes with minimal fields should round-trip correctly."""
+    node = RegisteredNode(registered_node_id=1)
+
+    proto = node._to_proto()
+    roundtrip = RegisteredNode._from_proto(proto)
+
+    assert roundtrip.registered_node_id == 1
+    assert roundtrip.admin_key is None
+    assert roundtrip.description is None
+    assert roundtrip.service_endpoints == ()

Comment on lines +90 to +102
def test_add_service_endpoint():
"""Service endpoints should be appendable through the fluent helper."""
transaction = RegisteredNodeUpdateTransaction()
endpoint = BlockNodeServiceEndpoint(
domain_name="block.example.com",
port=443,
endpoint_api=BlockNodeApi.STATUS,
)

result = transaction.add_service_endpoint(endpoint)

assert result is transaction
assert transaction.service_endpoints == [endpoint]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add tests for additional setter methods and frozen state rejection.

The test file covers add_service_endpoint but is missing tests for other fluent setters (set_registered_node_id, set_admin_key, set_description, set_service_endpoints) and verification that setters raise an exception when the transaction is frozen.

For completeness and to protect against breaking changes:

  1. Add individual setter tests asserting the value is set and self is returned.
  2. Add a test verifying setters raise an exception after freeze_with().

Comment on lines +60 to +80
def test_registered_service_endpoint_requires_exactly_one_address():
"""Endpoints must use either an IP address or a domain name."""
with pytest.raises(ValueError, match="Exactly one of ip_address or domain_name must be set."):
MirrorNodeServiceEndpoint(
ip_address=b"\x7f\x00\x00\x01",
domain_name="mirror.example.com",
port=5600,
)

with pytest.raises(ValueError, match="Exactly one of ip_address or domain_name must be set."):
RpcRelayServiceEndpoint(port=7545)


def test_registered_service_endpoint_validates_port_range():
"""Endpoints should reject invalid port values."""
with pytest.raises(ValueError, match="port must be between 0 and 65535."):
BlockNodeServiceEndpoint(
domain_name="block.example.com",
port=70000,
endpoint_api=BlockNodeApi.STATUS,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding port boundary tests.

The port validation tests cover an invalid value (70000) but don't verify boundary conditions. Consider adding tests for edge cases:

🧪 Suggested boundary tests
def test_registered_service_endpoint_port_boundaries():
    """Endpoints should accept valid port boundary values."""
    # Port 0 is valid (often means "any available port")
    endpoint_zero = MirrorNodeServiceEndpoint(domain_name="test.com", port=0)
    assert endpoint_zero.port == 0

    # Port 65535 is valid (max TCP/UDP port)
    endpoint_max = MirrorNodeServiceEndpoint(domain_name="test.com", port=65535)
    assert endpoint_max.port == 65535

    # Negative port should fail
    with pytest.raises(ValueError, match="port must be between 0 and 65535."):
        MirrorNodeServiceEndpoint(domain_name="test.com", port=-1)

@danielmarv danielmarv marked this pull request as draft March 13, 2026 13:17
Signed-off-by: Daniel Ntege <[email protected]>
@github-actions
Copy link
Copy Markdown

Hello, this is the OfficeHourBot.

This is a reminder that the Hiero Python SDK Office Hours are scheduled in approximately 4 hours (14:00 UTC).

This session provides an opportunity to ask questions regarding this Pull Request.

Details:

Disclaimer: This is an automated reminder. Please verify the schedule here for any changes.

From,
The Python SDK Team

@github-actions
Copy link
Copy Markdown

Hi @danielmarv,

This pull request has had no commit activity for 10 days. Are you still working on it?
To keep the PR active, you can:

  • Push a new commit.
  • Comment /working on the linked issue (not this PR).

If you're no longer working on this, please comment /unassign on the linked issue to release it for others. Otherwise, this PR may be closed due to inactivity.

Reach out on discord or join our office hours if you need assistance.

From the Python SDK Team

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Advanced]: Implement HIP-1137 — Block Node discoverability via on-chain registry

5 participants