feat: Implement registered node lifecycle with create, update, and delete transactions#1970
feat: Implement registered node lifecycle with create, update, and delete transactions#1970danielmarv wants to merge 8 commits intohiero-ledger:mainfrom
Conversation
…lete transactions Signed-off-by: Daniel Ntege <[email protected]>
There was a problem hiding this comment.
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
RegisteredServiceEndpointhierarchy (block node / mirror node / RPC relay) +BlockNodeApienum and protobuf round-tripping. - Added registered-node transactions (
RegisteredNodeCreateTransaction,RegisteredNodeUpdateTransaction,RegisteredNodeDeleteTransaction) and wired deserialization mapping. - Extended
NodeCreateTransaction/NodeUpdateTransactionandTransactionReceiptto 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.
| 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 []) |
| 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), |
| 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." | ||
| ) |
WalkthroughThis 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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📋 Issue PlannerBuilt with CodeRabbit's Coding Plans for faster development and fewer bugs. View plan used: ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
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 | 🔵 TrivialConsider adding
add_associated_registered_nodeto frozen state tests.The frozen state test covers
set_associated_registered_nodesbut notadd_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
📒 Files selected for processing (28)
src/hiero_sdk_python/__init__.pysrc/hiero_sdk_python/address_book/block_node_api.pysrc/hiero_sdk_python/address_book/block_node_service_endpoint.pysrc/hiero_sdk_python/address_book/mirror_node_service_endpoint.pysrc/hiero_sdk_python/address_book/registered_node.pysrc/hiero_sdk_python/address_book/registered_node_address_book.pysrc/hiero_sdk_python/address_book/registered_node_address_book_query.pysrc/hiero_sdk_python/address_book/registered_service_endpoint.pysrc/hiero_sdk_python/address_book/rpc_relay_service_endpoint.pysrc/hiero_sdk_python/nodes/__init__.pysrc/hiero_sdk_python/nodes/node_create_transaction.pysrc/hiero_sdk_python/nodes/node_update_transaction.pysrc/hiero_sdk_python/nodes/registered_node_create_transaction.pysrc/hiero_sdk_python/nodes/registered_node_delete_transaction.pysrc/hiero_sdk_python/nodes/registered_node_update_transaction.pysrc/hiero_sdk_python/transaction/transaction.pysrc/hiero_sdk_python/transaction/transaction_receipt.pytests/integration/registered_node_transaction_e2e_test.pytests/unit/node_create_transaction_test.pytests/unit/node_update_transaction_test.pytests/unit/registered_node_address_book_query_test.pytests/unit/registered_node_address_book_test.pytests/unit/registered_node_create_transaction_test.pytests/unit/registered_node_delete_transaction_test.pytests/unit/registered_node_test.pytests/unit/registered_node_update_transaction_test.pytests/unit/registered_service_endpoint_test.pytests/unit/test_transaction_receipt.py
| 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 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check if the file exists and inspect its actual content
find . -name "registered_node_create_transaction.py" -type fRepository: 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 -10Repository: 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 fRepository: hiero-ledger/hiero-sdk-python
Length of output: 55
🏁 Script executed:
# Search for all registered_node imports in the codebase
rg "registered_node" --type pyRepository: 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 fRepository: 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 -5Repository: 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 -20Repository: 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 -30Repository: 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 -20Repository: 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 -40Repository: 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 -40Repository: 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 -50Repository: 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 fRepository: 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 -20Repository: hiero-ledger/hiero-sdk-python
Length of output: 55
🏁 Script executed:
# Read pyproject.toml to see all dependencies
cat pyproject.tomlRepository: 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.tomlRepository: 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 -20Repository: 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 -30Repository: 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 -printRepository: 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>&1Repository: 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 = 54of typecom.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 NodeCreateTransactionBodyin packagecom.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 -->There was a problem hiding this comment.
@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?
There was a problem hiding this comment.
in the generate_proto.py we use v0.72.0-rc.2
so it should be better we use the same
There was a problem hiding this comment.
coderabbit suggest using v0.71.3 as a stable version, will need a confirmation on this.
There was a problem hiding this comment.
but for that it does not have the fields for this hip
There was a problem hiding this comment.
version 0.71.3 was tagged last week, so it should be up to date enough.
There was a problem hiding this comment.
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
| @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) | ||
|
|
There was a problem hiding this comment.
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
| 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 |
There was a problem hiding this comment.
🧹 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.
+ """| 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 |
There was a problem hiding this comment.
🧩 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 -nRepository: 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 -20Repository: 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 -30Repository: 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(tagv0.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 isbytes signedTransactionBytes = 5, which MUST contain a serializedSignedTransaction; the older fields (body,sigs,sigMap,bodyBytes) are deprecated and MUST NOT be set. Also notes a current transaction size limit of 6144 bytes forsignedTransactionBytes. [1]message TransactionBody: common transaction header fields (transactionID,nodeAccountID,transactionFee,transactionValidDuration,memo, etc.), plus a largeoneof dataselecting exactly one transaction type (Crypto/File/Contract/Consensus/Token/Schedule/Ethereum/Node ops, TokenReject/Airdrop/Cancel/Claim, platformStateSignatureTransaction, auxiliary hintTS/history-proof transactions, hook/lambda-related transactions, etc.). It also includesrepeated CustomFeeLimit max_custom_fees = 1001. [1][2]message AtomicBatchTransactionBody: supports atomic batches viarepeated 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(packagecom.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:
- 1: https://github.com/hashgraph/hedera-protobufs/tree/main/services
- 2: https://github.com/hashgraph/hedera-protobufs/blob/main/services/node_update.proto
🏁 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 -10Repository: 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 -10Repository: 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/nullRepository: 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 -30Repository: 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.pyRepository: 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/nullRepository: 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
StateChangechooses what kind of change happened viaoneof 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 includesnode_id_key. (github.com) - The value type is selected via
MapChangeValue.oneof value_choice, which includesnode_valueof typecom.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_update → MapChangeKey.node_id_key + MapChangeValue.node_value. (github.com)
Citations:
- 1: https://github.com/hashgraph/hedera-protobufs/blob/main/services/node_update.proto
- 2: https://github.com/hashgraph/hedera-protobufs/blob/main/block/stream/output/state_changes.proto
- 3: https://github.com/hashgraph/hedera-protobufs/blob/main/block/stream/output/state_changes.proto
- 4: https://github.com/hashgraph/hedera-protobufs/blob/main/block/stream/output/state_changes.proto
- 5: https://github.com/hashgraph/hedera-protobufs/blob/main/block/stream/output/state_changes.proto
🏁 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 -20Repository: 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 -20Repository: 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).
There was a problem hiding this comment.
but we are using the newer verssion
| @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) |
There was a problem hiding this comment.
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
| 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) |
There was a problem hiding this comment.
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.
| 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 | |
| ) |
| 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 |
There was a problem hiding this comment.
🛠️ 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| 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 |
There was a problem hiding this comment.
🧹 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:
- Minimal node (only
registered_node_idset) - Node with
Noneadmin_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 == ()| 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] |
There was a problem hiding this comment.
🧹 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:
- Add individual setter tests asserting the value is set and
selfis returned. - Add a test verifying setters raise an exception after
freeze_with().
| 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, | ||
| ) |
There was a problem hiding this comment.
🧹 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)Signed-off-by: Daniel Ntege <[email protected]>
…tered node and consensus node Signed-off-by: Daniel Ntege <[email protected]>
Signed-off-by: Daniel Ntege <[email protected]>
…o module Signed-off-by: Daniel Ntege <[email protected]>
Signed-off-by: Daniel Ntege <[email protected]>
|
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, |
|
Hi @danielmarv, This pull request has had no commit activity for 10 days. Are you still working on it?
If you're no longer working on this, please comment Reach out on discord or join our office hours if you need assistance. From the Python SDK Team |
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:
RegisteredNode,RegisteredNodeAddressBook, andRegisteredNodeAddressBookQueryclasses for representing and querying registered nodes in the address book. [1] [2] [3]RegisteredServiceEndpointas a base type, with concrete subclasses forBlockNodeServiceEndpoint,MirrorNodeServiceEndpoint, andRpcRelayServiceEndpoint, supporting protocol buffer serialization and validation. [1] [2] [3] [4]Block Node API Support:
BlockNodeApienum to enumerate well-known block node APIs, and integrated it into the new block node service endpoint class. [1] [2]Node Transaction Enhancements:
NodeCreateParamsandNodeCreateTransactionto support anassociated_registered_nodesfield, allowing consensus nodes to reference registered nodes. [1] [2] [3]SDK Exports and Imports:
__init__.pyand thenodespackage 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