Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2dc3cb0
feat: added the protos for hip-1137
manishdait Mar 11, 2026
1b416c7
feat: added the new classed
manishdait Mar 12, 2026
e92b6f1
feat: added new fields to existing class
manishdait Mar 13, 2026
8157151
feat: added class structure for registeredNodeAdressBook query
manishdait Mar 13, 2026
ba6699e
feat: added e2e test for the registeredNodeTx
manishdait Mar 13, 2026
8f03dc2
chore: change acess modifier
manishdait Mar 23, 2026
39a86c3
chore: fix tx signing
manishdait Mar 30, 2026
7c2eaa0
chore: add initial example
manishdait Mar 30, 2026
cb551a6
chore: added clearServiceEndpoint method
manishdait Mar 30, 2026
52f47a9
chore: completed example
manishdait Mar 31, 2026
e0e2809
chore: updated the proto to use the fix protobuf
manishdait Apr 15, 2026
3a30c6d
feat: updated the hip impl for protobuf changes
manishdait Apr 15, 2026
4f780ce
chore: neat pick
manishdait Apr 15, 2026
0bba294
chore: spotless
manishdait Apr 15, 2026
bbbd65d
feat: added initial registeredNode query
manishdait Apr 22, 2026
d3a7b76
chore: added unit test for initial query
manishdait Apr 22, 2026
52bda95
chore: added docs
manishdait Apr 22, 2026
16d735e
feat: added missing status enum
manishdait Apr 23, 2026
0ea81f3
chore: improve the structure
manishdait Apr 23, 2026
cbd0059
chore: refactor code for parsing json
manishdait Apr 23, 2026
88d67a5
chore: added more test for the create tx
manishdait Apr 27, 2026
f7faf94
chore: fix to proto to include mirror and rpc ep
manishdait Apr 27, 2026
04df3f8
chore: expand test for delete node
manishdait Apr 27, 2026
bf1dc22
chore: added test for the registered address book query
manishdait Apr 29, 2026
48215d0
chore: updated mirrornode version
manishdait Apr 29, 2026
a3f1785
chore: apply spotless
manishdait Apr 29, 2026
aa110b1
chore fix rebase
manishdait Apr 30, 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
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ jobs:

- name: Prepare Hiero Solo
id: solo
uses: hiero-ledger/hiero-solo-action@328bc84c3b00a990a151418144fd682a4eb76ea6 # v0.17
uses: hiero-ledger/hiero-solo-action@4d42a74e8e644a2753f3bb7a2afa429305375b14 # v0.16
with:
installMirrorNode: true
soloVersion: v0.65.0
Expand Down Expand Up @@ -255,8 +255,8 @@ jobs:
installMirrorNode: true
soloVersion: v0.65.0
dualMode: true
hieroVersion: v0.68.0
mirrorNodeVersion: v0.142.0
hieroVersion: v0.73.0
mirrorNodeVersion: v0.153.0

- name: Build SDK
run: ./gradlew assemble
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.hashgraph.sdk.examples;

import com.hedera.hashgraph.sdk.AccountId;
import com.hedera.hashgraph.sdk.BlockNodeApi;
import com.hedera.hashgraph.sdk.BlockNodeServiceEndpoint;
import com.hedera.hashgraph.sdk.Client;
import com.hedera.hashgraph.sdk.NodeUpdateTransaction;
import com.hedera.hashgraph.sdk.PrivateKey;
import com.hedera.hashgraph.sdk.RegisteredNodeCreateTransaction;
import com.hedera.hashgraph.sdk.RegisteredNodeDeleteTransaction;
import com.hedera.hashgraph.sdk.RegisteredNodeUpdateTransaction;
import com.hedera.hashgraph.sdk.TransactionReceipt;
import com.hedera.hashgraph.sdk.TransactionResponse;
import com.hedera.hashgraph.sdk.logger.LogLevel;
import com.hedera.hashgraph.sdk.logger.Logger;
import io.github.cdimascio.dotenv.Dotenv;
import java.util.List;
import java.util.Objects;

public class RegisteredNodeLifeCycleExample {
/*
* See .env.sample in the examples folder root for how to specify values below
* or set environment variables with the same names.
*/

/**
* Operator's account ID.
* Used to sign and pay for operations on Hedera.
*/
private static final AccountId OPERATOR_ID =
AccountId.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_ID")));

/**
* Operator's private key.
*/
private static final PrivateKey OPERATOR_KEY =
PrivateKey.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_KEY")));

/**
* HEDERA_NETWORK defaults to testnet if not specified in dotenv file.
* Network can be: localhost, testnet, previewnet or mainnet.
*/
private static final String HEDERA_NETWORK = Dotenv.load().get("HEDERA_NETWORK", "testnet");

/**
* SDK_LOG_LEVEL defaults to SILENT if not specified in dotenv file.
* Log levels can be: TRACE, DEBUG, INFO, WARN, ERROR, SILENT.
* <p>
* Important pre-requisite: set simple logger log level to same level as the SDK_LOG_LEVEL,
* for example via VM options: -Dorg.slf4j.simpleLogger.log.org.hiero=trace
*/
private static final String SDK_LOG_LEVEL = Dotenv.load().get("SDK_LOG_LEVEL", "SILENT");

public static void main(String[] args) throws Exception {

Check warning on line 55 in examples/src/main/java/com/hedera/hashgraph/sdk/examples/RegisteredNodeLifeCycleExample.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/src/main/java/com/hedera/hashgraph/sdk/examples/RegisteredNodeLifeCycleExample.java#L55

Method RegisteredNodeLifeCycleExample::main has 63 lines of code (limit is 50)
System.out.println("Registered Node Lifecycle Example Start!");

/*
* Step 0:
* Create and configure the SDK Client.
*/
Client client = ClientHelper.forName(HEDERA_NETWORK);
// All generated transactions will be paid by this account and signed by this key.
client.setOperator(OPERATOR_ID, OPERATOR_KEY);
// Attach logger to the SDK Client.
client.setLogger(new Logger(LogLevel.valueOf(SDK_LOG_LEVEL)));

/*
* Step 1:
* Generate an admin key pair and configure a BlockNodeServiceEndpoint
* for use in the RegisterNodeTransaction.
*/
PrivateKey adminKey = PrivateKey.generateED25519();
BlockNodeServiceEndpoint initialEndpoint = new BlockNodeServiceEndpoint()
.setIpAddress(new byte[] {127, 0, 0, 1})
.setPort(443)
.setRequiresTls(true)
.addEndpointApi(BlockNodeApi.SUBSCRIBE_STREAM);

/*
* Step 2:
* Create Registered Node.
*/
RegisteredNodeCreateTransaction registeredNodeCreateTx = new RegisteredNodeCreateTransaction()
.setDescription("My Block Node")
.setAdminKey(adminKey)
.addServiceEndpoint(initialEndpoint)
.freezeWith(client)
.sign(adminKey);

System.out.println("Creating Registered Node...");
TransactionResponse registeredNodeCreateTxResponse = registeredNodeCreateTx.execute(client);
TransactionReceipt registeredNodeCreateTxReceipt = registeredNodeCreateTxResponse.getReceipt(client);

if (registeredNodeCreateTxReceipt.registeredNodeId <= 0) {
throw new Exception("RegisteredNodeCreate transaction receipt was missing registeredNodeId. (Fail)");

Check warning on line 96 in examples/src/main/java/com/hedera/hashgraph/sdk/examples/RegisteredNodeLifeCycleExample.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/src/main/java/com/hedera/hashgraph/sdk/examples/RegisteredNodeLifeCycleExample.java#L96

Avoid throwing raw exception types.
}

long registeredNodeId = registeredNodeCreateTxReceipt.registeredNodeId;

/*
* Step 3:
* Execute a RegisteredNodeAddressBookQuery to verify the newly created
* registered node appears in the RegisteredNodeAddressBook.
*/
System.out.println("Skipping registered node address book query because mirror node API is not available...");

/*
* Step 4:
* Update the RegisteredNode with new Block Node endpoint.
*/
BlockNodeServiceEndpoint updateEndpoint = new BlockNodeServiceEndpoint()
.setDomainName("block-node.example.com")
.setPort(443)
.setRequiresTls(true)
.addEndpointApi(BlockNodeApi.STATUS);

RegisteredNodeUpdateTransaction registeredNodeUpdateTx = new RegisteredNodeUpdateTransaction()
.setRegisteredNodeId(registeredNodeId)
.setDescription("My Updated Block Node")
.setServiceEndpoints(List.of(initialEndpoint, updateEndpoint))
.freezeWith(client)
.sign(adminKey);

System.out.println("Updating Registered Node...");
TransactionResponse registeredNodeUpdateTxResponse = registeredNodeUpdateTx.execute(client);
registeredNodeUpdateTxResponse.getReceipt(client);

/*
* Step 5:
* Add the registeredNodeId as associatedRegisteredNodes to a Node.
*/

NodeUpdateTransaction associateTx = new NodeUpdateTransaction()
.setNodeId(0)
.addAssociatedRegisteredNode(registeredNodeId)
.freezeWith(client);

System.out.println("Associating registered node " + registeredNodeId + " with consensus node...");
TransactionResponse associateTxResponse = associateTx.execute(client);
associateTxResponse.getReceipt(client);

/*
* Step 6:
* Remove the registeredNodeId as associatedRegisteredNodes from a Node.
*/

NodeUpdateTransaction disassociateTx = new NodeUpdateTransaction()
.setNodeId(0)
.setAssociatedRegisteredNodes(List.of()) // Empty list clear associated registeredNode
.freezeWith(client);

System.out.println("Disassociating registered node " + registeredNodeId + " with consensus node...");
disassociateTx.execute(client);
associateTxResponse.getReceipt(client);

/*
* Step 7:
* Delete the Registered Node.
*/
System.out.println("Deleting Registered Node...");
new RegisteredNodeDeleteTransaction()
.setRegisteredNodeId(registeredNodeCreateTxReceipt.registeredNodeId)
.freezeWith(client)
.sign(adminKey)
.execute(client)
.getReceipt(client);

client.close();

System.out.println("Registered Node Lifecycle Example Complete!");
}
}
56 changes: 56 additions & 0 deletions sdk/src/main/java/com/hedera/hashgraph/sdk/BlockNodeApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.hashgraph.sdk;

import com.hedera.hashgraph.sdk.proto.RegisteredServiceEndpoint.BlockNodeEndpoint;

/**
* An enumeration of well-known block node endpoint APIs.
*/
public enum BlockNodeApi {
/**
* Any other API type associated with a block node.
*/
OTHER(BlockNodeEndpoint.BlockNodeApi.OTHER),

/**
* The Block Node Status API.
*/
STATUS(BlockNodeEndpoint.BlockNodeApi.STATUS),

/**
* The Block Node Publish API.
*/
PUBLISH(BlockNodeEndpoint.BlockNodeApi.PUBLISH),

/**
* The Block Node Subscribe Stream API.
*/
SUBSCRIBE_STREAM(BlockNodeEndpoint.BlockNodeApi.SUBSCRIBE_STREAM),

/**
* The Block Node State Proof API.
*/
STATE_PROOF(BlockNodeEndpoint.BlockNodeApi.STATE_PROOF);

final BlockNodeEndpoint.BlockNodeApi code;

BlockNodeApi(BlockNodeEndpoint.BlockNodeApi code) {
this.code = code;
}

static BlockNodeApi valueOf(BlockNodeEndpoint.BlockNodeApi code) {
return switch (code) {
case OTHER -> OTHER;
case STATUS -> STATUS;
case PUBLISH -> PUBLISH;
case SUBSCRIBE_STREAM -> SUBSCRIBE_STREAM;
case STATE_PROOF -> STATE_PROOF;
default -> throw new IllegalArgumentException("Unhandled BlockNodeApi code");
};
}

@Override
public String toString() {
return code.name();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.hashgraph.sdk;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.protobuf.ByteString;
import com.hedera.hashgraph.sdk.proto.RegisteredServiceEndpoint;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* Represent a Registered Block Node
*/
public class BlockNodeServiceEndpoint extends RegisteredServiceEndpointBase<BlockNodeServiceEndpoint> {
/**
* An indicator of what API this endpoint supports.
*/
private List<BlockNodeApi> endpointApis = new ArrayList<>();

/**
* Constructor.
*/
public BlockNodeServiceEndpoint() {}

/**
* Returns the list of APIs supported by this endpoint.
*
* @return the list of supported block node APIs
*/
public List<BlockNodeApi> getEndpointApis() {
return endpointApis;
}

/**
* Sets the list of APIs supported by this endpoint.
*
* @param endpointApis the list of APIs to support; must not be null
* @return {@code this}
*/
public BlockNodeServiceEndpoint setEndpointApis(List<BlockNodeApi> endpointApis) {
Objects.requireNonNull(endpointApis, "endpointApis must not be null");
this.endpointApis = new ArrayList<>(endpointApis);
return this;
}

/**
* Adds a single API to the list of supported APIs for this endpoint.
*
* @param endpointApi the API to add; must not be null
* @return {@code this}
*/
public BlockNodeServiceEndpoint addEndpointApi(BlockNodeApi endpointApi) {
Objects.requireNonNull(endpointApi, "endpointApi must not be null");
this.endpointApis.add(endpointApi);
return this;
}

/**
* Create a BlockNodeServiceEndpoint object from protobuf
*
* @param serviceEndpoint the protobuf object
* @return the new instance of BlockNodeServiceEndpoint
*/
static BlockNodeServiceEndpoint fromProtobuf(
com.hedera.hashgraph.sdk.proto.RegisteredServiceEndpoint serviceEndpoint) {
Objects.requireNonNull(serviceEndpoint, "serviceEndpoint must not be null");

var blockNodeEndpoint = new BlockNodeServiceEndpoint()
.setPort(serviceEndpoint.getPort())
.setRequiresTls(serviceEndpoint.getRequiresTls());

if (serviceEndpoint.hasBlockNode()) {
for (var apiProto : serviceEndpoint.getBlockNode().getEndpointApiList()) {
blockNodeEndpoint.addEndpointApi(BlockNodeApi.valueOf(apiProto));
}
}

if (serviceEndpoint.hasIpAddress()) {
blockNodeEndpoint.setIpAddress(serviceEndpoint.getIpAddress().toByteArray());
}

if (serviceEndpoint.hasDomainName()) {
blockNodeEndpoint.setDomainName(serviceEndpoint.getDomainName());
}

return blockNodeEndpoint;
}

@Override
com.hedera.hashgraph.sdk.proto.RegisteredServiceEndpoint toProtobuf() {
if (ipAddress == null && domainName == null) {
throw new IllegalArgumentException(
"RegisterServiceEndpoint must define either an ipAddress or domainName");
}

var blockNodeBuilder = RegisteredServiceEndpoint.BlockNodeEndpoint.newBuilder()
.addAllEndpointApi(endpointApis.stream().map(api -> api.code).toList());

var registeredServiceEndpoint = com.hedera.hashgraph.sdk.proto.RegisteredServiceEndpoint.newBuilder()
.setPort(port)
.setRequiresTls(requiresTls)
.setBlockNode(blockNodeBuilder);

if (ipAddress != null) {
registeredServiceEndpoint.setIpAddress(ByteString.copyFrom(this.ipAddress));
}

if (domainName != null) {
registeredServiceEndpoint.setDomainName(this.domainName);
}

return registeredServiceEndpoint.build();
}

/**
* Parses BlockNodeServiceEndpoint from the type-specific JSON object the MirrorNode.
*
* @param json the json containing block node specific data
* @return {@code this}
*/
static BlockNodeServiceEndpoint fromJson(JsonObject json) {
Objects.requireNonNull(json, "json must not be null");

List<String> apis = new ArrayList<>();
if (json.has("endpoint_apis")) {
for (JsonElement api : json.getAsJsonArray("endpoint_apis")) {
apis.add(api.getAsString());
}
}

return new BlockNodeServiceEndpoint()
.setEndpointApis(
apis.stream().map(a -> BlockNodeApi.valueOf(a)).collect(Collectors.toUnmodifiableList()));
}

@Override
public String toString() {
return toStringHelper().add("endpointApis", endpointApis).toString();
}
}
Loading
Loading