Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
09e6e3a
feat: add BlockNodeApi implementation
Dosik13 Mar 26, 2026
6e7f0c1
feat: add RegisteredServiceEndpoint implementation
Dosik13 Mar 26, 2026
730ec00
feat: add BlockNode, MirrorNode and RpcRelay endpoints
Dosik13 Mar 26, 2026
bb2979c
test: add unit tests for service endpoint structs
Dosik13 Mar 26, 2026
afe327b
feat: add RegisteredNodeCreateTransaction implementation
Dosik13 Mar 26, 2026
bd15498
feat: add registeredNodeId field in TransactionReceipt
Dosik13 Mar 26, 2026
0593e27
test: add RegisteredNodeCreateTransaction integration tests
Dosik13 Mar 26, 2026
5c32024
test: add RegisteredNodeCreateTransaction unit tests
Dosik13 Mar 26, 2026
9a16a1a
feat: add RegisteredNodeDeleteTransaction implementation
Dosik13 Mar 26, 2026
8d29242
test: add RegisteredNodeDeleteTransaction integration tests
Dosik13 Mar 26, 2026
1c9cd68
test: add RegisteredNodeDeleteTransaction unit tests
Dosik13 Mar 26, 2026
64119e6
feat: add RegisteredNodeUpdateTransaction implementation
Dosik13 May 7, 2026
6834a4b
test: add RegisteredNodeUpdateTransaction integration tests
Dosik13 Mar 26, 2026
d9ba478
test: add RegisteredNodeUpdateTransaction unit tests
Dosik13 Mar 26, 2026
8881ead
feat: add associatedRegisteredNodes field in NodeCreateTransaction
Dosik13 Mar 26, 2026
b02fe79
test: add e2e test for associatedRegisteredNodes field for NodeCreate…
Dosik13 Mar 26, 2026
5dacb0f
test: add unit test for associatedRegisteredNodes field for NodeCreat…
Dosik13 Mar 26, 2026
42ac275
feat: add associatedRegisteredNodes field in NodeUpdateTransaction
Dosik13 Mar 26, 2026
5b0f1a8
test: add e2e test for associatedRegisteredNodes field for NodeUpdate…
Dosik13 Mar 26, 2026
941b8db
test: add unit test for associatedRegisteredNodes field for NodeUpdat…
Dosik13 Mar 26, 2026
b12e682
test: fix transaction receipt/record query unit tests
Dosik13 Mar 26, 2026
79c0fd4
docs: update RegisteredNodeTransaction structs docstrings
Dosik13 Mar 26, 2026
d2b546c
chore: fix codacy errors
Dosik13 Mar 27, 2026
95d36ba
fix: lint error
Dosik13 Mar 27, 2026
1227a73
feat: add chaining setters to registered service endpoint concrete types
Dosik13 Mar 31, 2026
c9b6411
refactor: change endpointApi to endpointApis in BlockNodeServiceEndpoint
Dosik13 Apr 16, 2026
087159d
test: fix tests
Dosik13 Apr 16, 2026
9084e7c
feat: implement GeneralServiceEndpoint
Dosik13 Apr 22, 2026
dd445b5
feat: add GeneralServiceEndpoint in the _RegisteredServiceEndpointFro…
Dosik13 Apr 22, 2026
57b8b7a
test: add GeneralServiceEndpoint tests
Dosik13 Apr 22, 2026
2ef40f6
feat: add RegisteredNodeAddressBookQuery
Dosik13 Apr 22, 2026
a4971b0
test: add tests for RegisteredNodeAddressBookQuery
Dosik13 Apr 22, 2026
b2658f3
test: add query checks for RegisteredNodeTransactions
Dosik13 Apr 22, 2026
916eb8e
feat: add example
Dosik13 Apr 29, 2026
fc27d7f
chore: fix codacy issues and codecov
Dosik13 May 7, 2026
988d28c
fix: expose RegisteredNodeId on TransactionReceipt as *uint64
Dosik13 May 7, 2026
17eb029
refactor: error on unknown block node API strings
Dosik13 May 7, 2026
3e4c205
chore: reduce codacy
Dosik13 May 7, 2026
73086a3
Merge branch 'main' into feat/add-hip1137
Dosik13 May 7, 2026
4d2d652
fix: add authorized account to run the registered node example
Dosik13 May 8, 2026
cef1b48
Merge branch 'main' into feat/add-hip1137
Dosik13 May 8, 2026
7409b24
chore: fix codacy issues in example
Dosik13 May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 256 additions & 0 deletions examples/registered_node/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
package main

import (
"fmt"
"net"
"os"
"time"

hiero "github.com/hiero-ledger/hiero-sdk-go/v2/sdk"
)

// Registered Node Lifecycle
//
// Walks through the full lifecycle of a registered node:
// 1. Generate an admin key.
// 2. Create a registered node with a block node service endpoint.
// 3. Query the RegisteredNodeAddressBook to confirm it appears.
// 4. Update the registered node with a new description and a second endpoint.
// 5. Associate the registered node with an existing consensus node.
// 6. Delete the registered node.
//
// Environment:
//
// HEDERA_NETWORK — e.g. "previewnet", "testnet", or "mainnet"
// GENESIS_OPERATOR_ID — optional override (defaults to "0.0.2")
// GENESIS_OPERATOR_KEY — optional override (defaults to the genesis Ed25519 key)
// CONSENSUS_NODE_ID — numeric consensus node ID to associate (optional; step 5 is skipped if unset)
// CONSENSUS_NODE_ADMIN_KEY — DER-encoded admin key for that consensus node (optional; required if CONSENSUS_NODE_ID is set)
func main() {
client := setupClient()

// Step 1 — generate the registered node admin key.
adminKey, err := hiero.PrivateKeyGenerateEd25519()
if err != nil {
panic(fmt.Sprintf("%v : error generating admin key", err))
}
fmt.Printf("Generated registered-node admin key: %s\n", adminKey.PublicKey().String())

// Steps 2-4 — create a new registered node.
registeredNodeId := createRegisteredNode(client, adminKey)
fmt.Printf("Created registered node with id: %d\n", registeredNodeId)

// Wait for the mirror node to ingest the new entry.
time.Sleep(time.Second * 5)

// Step 5 — query the address book and confirm the new node is present.
verifyInAddressBook(client, registeredNodeId)

// Steps 6-7 — update the registered node with a new description and both endpoints.
updateRegisteredNode(client, adminKey, registeredNodeId)

// Step 8 — associate with an existing consensus node (optional).
associateWithConsensusNode(client, registeredNodeId)

// Step 9 — delete the registered node.
deleteRegisteredNode(client, adminKey, registeredNodeId)

if err := client.Close(); err != nil {
panic(fmt.Sprintf("%v : error closing client", err))
}
}

// setupClient builds a Hiero client from HEDERA_NETWORK and configures the
// operator. The operator defaults to account 0.0.2 + genesis Ed25519 key (the
// standard local hedera-services bootstrap, also what the HIP-1137 e2e tests
// use); override via GENESIS_OPERATOR_ID / GENESIS_OPERATOR_KEY if your
// network has a different system account.
func setupClient() *hiero.Client {
client, err := hiero.ClientForName(os.Getenv("HEDERA_NETWORK"))
if err != nil {
panic(fmt.Sprintf("%v : error creating client", err))
}

operatorIDStr := os.Getenv("GENESIS_OPERATOR_ID")
if operatorIDStr == "" {
operatorIDStr = "0.0.2"
}
operatorAccountID, err := hiero.AccountIDFromString(operatorIDStr)
if err != nil {
panic(fmt.Sprintf("%v : error parsing operator ID", err))
}

operatorKeyStr := os.Getenv("GENESIS_OPERATOR_KEY")
if operatorKeyStr == "" {
operatorKeyStr = "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137"
}
operatorKey, err := hiero.PrivateKeyFromString(operatorKeyStr)
if err != nil {
panic(fmt.Sprintf("%v : error parsing operator key", err))
}

client.SetOperator(operatorAccountID, operatorKey)
return client
}

// createRegisteredNode submits a RegisteredNodeCreateTransaction with a single
// block-node endpoint and returns the assigned registeredNodeId.
func createRegisteredNode(client *hiero.Client, adminKey hiero.PrivateKey) uint64 {
primaryEndpoint := &hiero.BlockNodeServiceEndpoint{}
primaryEndpoint.
SetIPAddress(net.IPv4(192, 168, 1, 1).To4()).
SetPort(50211).
SetRequiresTls(true).
SetEndpointApis([]hiero.BlockNodeApi{
hiero.BlockNodeApiSubscribeStream,
hiero.BlockNodeApiStatus,
})

createTx, err := hiero.NewRegisteredNodeCreateTransaction().
SetAdminKey(adminKey).
SetDescription("My Block Node").
SetServiceEndpoints([]hiero.RegisteredServiceEndpoint{primaryEndpoint}).
FreezeWith(client)
if err != nil {
panic(fmt.Sprintf("%v : error freezing create tx", err))
}

createResp, err := createTx.Sign(adminKey).Execute(client)
if err != nil {
panic(fmt.Sprintf("%v : error executing create tx", err))
}

createReceipt, err := createResp.SetValidateStatus(true).GetReceipt(client)
if err != nil {
panic(fmt.Sprintf("%v : error fetching create receipt", err))
}

if createReceipt.RegisteredNodeId == nil || *createReceipt.RegisteredNodeId == 0 {
panic("expected a non-zero registeredNodeId on the receipt")
}
return *createReceipt.RegisteredNodeId
}

// verifyInAddressBook queries the mirror node and panics if the given
// registered node id is missing.
func verifyInAddressBook(client *hiero.Client, registeredNodeId uint64) {
book, err := hiero.NewRegisteredNodeAddressBookQuery().
SetRegisteredNodeId(registeredNodeId).
Execute(client)
if err != nil {
panic(fmt.Sprintf("%v : error executing address book query", err))
}

for _, n := range book.RegisteredNodes {
if n.RegisteredNodeID == registeredNodeId {
fmt.Printf("Address book returned registered node: id=%d description=%q\n",
n.RegisteredNodeID, n.Description)
return
}
}
panic("registered node was not found in the address book")
}

// updateRegisteredNode submits a RegisteredNodeUpdateTransaction with a new
// description and adds a second (domain-name) endpoint alongside the original.
func updateRegisteredNode(client *hiero.Client, adminKey hiero.PrivateKey, registeredNodeId uint64) {
primaryEndpoint := &hiero.BlockNodeServiceEndpoint{}
primaryEndpoint.
SetIPAddress(net.IPv4(192, 168, 1, 1).To4()).
SetPort(50211).
SetRequiresTls(true).
SetEndpointApis([]hiero.BlockNodeApi{
hiero.BlockNodeApiSubscribeStream,
hiero.BlockNodeApiStatus,
})

secondaryEndpoint := &hiero.BlockNodeServiceEndpoint{}
secondaryEndpoint.
SetDomainName("block.example.com").
SetPort(50212).
SetRequiresTls(true).
SetEndpointApis([]hiero.BlockNodeApi{hiero.BlockNodeApiStatus})

updateTx, err := hiero.NewRegisteredNodeUpdateTransaction().
SetRegisteredNodeId(registeredNodeId).
SetDescription("My Updated Block Node").
SetServiceEndpoints([]hiero.RegisteredServiceEndpoint{primaryEndpoint, secondaryEndpoint}).
FreezeWith(client)
if err != nil {
panic(fmt.Sprintf("%v : error freezing update tx", err))
}

updateResp, err := updateTx.Sign(adminKey).Execute(client)
if err != nil {
panic(fmt.Sprintf("%v : error executing update tx", err))
}

updateReceipt, err := updateResp.SetValidateStatus(true).GetReceipt(client)
if err != nil {
panic(fmt.Sprintf("%v : error fetching update receipt", err))
}
fmt.Printf("Update receipt status: %s\n", updateReceipt.Status)
}

// associateWithConsensusNode runs step 8 of the lifecycle. Skipped when
// CONSENSUS_NODE_ID is unset, since most operators don't hold a consensus
// node's admin key.
func associateWithConsensusNode(client *hiero.Client, registeredNodeId uint64) {
consensusNodeIDStr := os.Getenv("CONSENSUS_NODE_ID")
if consensusNodeIDStr == "" {
fmt.Println("CONSENSUS_NODE_ID not set — skipping consensus-node association step")
return
}

var consensusNodeID uint64
if _, err := fmt.Sscanf(consensusNodeIDStr, "%d", &consensusNodeID); err != nil {
panic(fmt.Sprintf("%v : error parsing CONSENSUS_NODE_ID", err))
}

consensusAdminKey, err := hiero.PrivateKeyFromString(os.Getenv("CONSENSUS_NODE_ADMIN_KEY"))
if err != nil {
panic(fmt.Sprintf("%v : error parsing CONSENSUS_NODE_ADMIN_KEY", err))
}

nodeUpdateTx, err := hiero.NewNodeUpdateTransaction().
SetNodeID(consensusNodeID).
AddAssociatedRegisteredNode(registeredNodeId).
FreezeWith(client)
if err != nil {
panic(fmt.Sprintf("%v : error freezing node update tx", err))
}

nodeUpdateResp, err := nodeUpdateTx.Sign(consensusAdminKey).Execute(client)
if err != nil {
panic(fmt.Sprintf("%v : error executing node update tx", err))
}

nodeUpdateReceipt, err := nodeUpdateResp.SetValidateStatus(true).GetReceipt(client)
if err != nil {
panic(fmt.Sprintf("%v : error fetching node update receipt", err))
}
fmt.Printf("Consensus node %d updated with associated registered node %d: %s\n",
consensusNodeID, registeredNodeId, nodeUpdateReceipt.Status)
}

// deleteRegisteredNode submits a RegisteredNodeDeleteTransaction and waits for
// the receipt.
func deleteRegisteredNode(client *hiero.Client, adminKey hiero.PrivateKey, registeredNodeId uint64) {
deleteTx, err := hiero.NewRegisteredNodeDeleteTransaction().
SetRegisteredNodeId(registeredNodeId).
FreezeWith(client)
if err != nil {
panic(fmt.Sprintf("%v : error freezing delete tx", err))
}

deleteResp, err := deleteTx.Sign(adminKey).Execute(client)
if err != nil {
panic(fmt.Sprintf("%v : error executing delete tx", err))
}

deleteReceipt, err := deleteResp.SetValidateStatus(true).GetReceipt(client)
if err != nil {
panic(fmt.Sprintf("%v : error fetching delete receipt", err))
}
fmt.Printf("Delete receipt status: %s\n", deleteReceipt.Status)
}
75 changes: 47 additions & 28 deletions proto/services/transaction_receipt.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading