Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 13 additions & 5 deletions .github/workflows/zxc-build-library.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,23 @@ jobs:
uses: hiero-ledger/hiero-solo-action@fbca3e7a99ce9aa8a250563a81187abe115e0dad # v0.16.0
with:
installMirrorNode: true
hieroVersion: v0.65.0
hieroVersion: v0.68.1-rc.1
dualMode: true


- name: Start CTest suite (Debug)
run: ${{ steps.cgroup.outputs.exec }} ctest -j 6 -C Debug --test-dir build/${{ matrix.preset }}-debug --output-on-failure
# - name: Start CTest suite (Debug)
# run: ${{ steps.cgroup.outputs.exec }} ctest -j 6 -C Debug --test-dir build/${{ matrix.preset }}-debug -E NodeUpdateTransactionIntegrationTests --output-on-failure

- name: Start CTest suite (Release)
# - name: Start CTest suite (Release)
# if: github.event.pull_request.merged == true
# run: ${{ steps.cgroup.outputs.exec }} ctest -j 6 -C Debug --test-dir build/${{ matrix.preset }}-release -E NodeUpdateTransactionIntegrationTests --output-on-failure

- name: Run Node Update Integration Tests (Debug)
run: ${{ steps.cgroup.outputs.exec }} ctest -C Debug --test-dir build/${{ matrix.preset }}-debug -R NodeUpdateTransactionIntegrationTests --output-on-failure

- name: Run Node Update Integration Tests (Release)
if: github.event.pull_request.merged == true
run: ${{ steps.cgroup.outputs.exec }} ctest -j 6 -C Debug --test-dir build/${{ matrix.preset }}-release --output-on-failure
run: ${{ steps.cgroup.outputs.exec }} ctest -C Debug --test-dir build/${{ matrix.preset }}-release -R NodeUpdateTransactionIntegrationTests --output-on-failure

- name: Compute Short SHA
id: sha
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,7 @@ src/proto/*.proto
/proto/event
/proto/sdk
/proto/platform
/proto/block

### Ignore Generated Package Files
package
Expand Down
7 changes: 4 additions & 3 deletions HieroApi.cmake
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
set(HAPI_VERSION_TAG "v0.62.0" CACHE STRING "Use the configured version tag for the Hiero API protobufs")
set(HAPI_COMMIT_HASH "e7db7cd74e1709ca719d1fcc9119aa062e82930f" CACHE STRING "Use the configured commit hash for the Hiero API protobufs (overrides version tag if provided)")
set(HAPI_VERSION_TAG "v0.68.1-rc.1" CACHE STRING "Use the configured version tag for the Hiero API protobufs")
set(HAPI_COMMIT_HASH "fadd38a6b2badec02bee35272f03fe8fafadea00" CACHE STRING "Use the configured commit hash for the Hiero API protobufs (overrides version tag if provided)")

if (HAPI_VERSION_TAG STREQUAL "")
set(HAPI_VERSION_TAG "v0.62.0")
set(HAPI_VERSION_TAG "v0.68.1-rc.1")
endif ()

# Use commit hash if provided, otherwise use version tag
Expand Down Expand Up @@ -61,6 +61,7 @@ file(INSTALL ${PROJECT_SOURCE_DIR}/proto/service-external-proto/sdk/ DESTINATION

file(INSTALL ${hproto_SOURCE_DIR}/hapi/hedera-protobuf-java-api/src/main/proto/platform/ DESTINATION ${PROTO_SRC}/platform)
file(INSTALL ${hproto_SOURCE_DIR}/hapi/hedera-protobuf-java-api/src/main/proto/services/ DESTINATION ${PROTO_SRC}/services)
file(INSTALL ${hproto_SOURCE_DIR}/hapi/hedera-protobuf-java-api/src/main/proto/block/ DESTINATION ${PROTO_SRC}/block)

add_subdirectory(proto)

Expand Down
4 changes: 4 additions & 0 deletions proto/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Begin Protobuf Definitions
set(PROTO_FILES
block/stream/chain_of_trust_proof.proto
transaction_list.proto

mirror/consensus_service.proto
Expand Down Expand Up @@ -56,6 +57,9 @@ set(PROTO_FILES
services/get_account_details.proto
services/get_by_key.proto
services/get_by_solidity_id.proto
services/hook_dispatch.proto
services/hook_types.proto
services/lambda_sstore.proto
services/network_get_execution_time.proto
services/network_get_version_info.proto
services/network_service.proto
Expand Down
8 changes: 8 additions & 0 deletions src/sdk/main/include/Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,14 @@ class Client
*/
Client& setNetworkFromAddressBook(const NodeAddressBook& addressBook);

/**
* Update the address book from the network immediately.
* This method is typically called when a node returns INVALID_NODE_ACCOUNT.
* The update is done synchronously to ensure the network has the latest nodes
* before retrying with another node.
*/
void updateAddressBook();

/**
* Set the consensus network with which this Client should communicate.
*
Expand Down
6 changes: 5 additions & 1 deletion src/sdk/main/include/Executable.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,11 @@ class Executable
/**
* The call was successful but an operation did not complete.
*/
RETRY
RETRY,
/**
* The node account is invalid; retry with another node and update address book.
*/
RETRY_WITH_ANOTHER_NODE
};

/**
Expand Down
7 changes: 6 additions & 1 deletion src/sdk/main/include/Status.h
Original file line number Diff line number Diff line change
Expand Up @@ -1855,7 +1855,12 @@ enum class Status
/**
* An NFT transfers list referenced a token type other than NON_FUNGIBLE_UNIQUE.
*/
NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE
NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE,

/**
* The node account has a zero balance.
*/
NODE_ACCOUNT_HAS_ZERO_BALANCE,
};

/**
Expand Down
47 changes: 45 additions & 2 deletions src/sdk/main/src/Client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,36 @@ Client& Client::setNetworkFromAddressBook(const NodeAddressBook& addressBook)
return *this;
}

//-----
void Client::updateAddressBook()
{
try
{
mImpl->mLogger.trace("Updating address book after INVALID_NODE_ACCOUNT response");
std::cout << "Updating address book after INVALID_NODE_ACCOUNT response" << std::endl;

// Get the address book - do NOT hold the mutex during query execution
// as execute() will call other Client methods that also need the mutex
const NodeAddressBook addressBook = AddressBookQuery().setFileId(FileId::ADDRESS_BOOK).execute(*this);

std::cout << "Node addresses in AB: " << std::endl;
for (const auto& nodeAddress : addressBook.getNodeAddresses())
{
std::cout << "Node address: " << nodeAddress.toString() << std::endl;
}

// Only acquire the mutex for the actual update
std::unique_lock lock(mImpl->mMutex);
setNetworkFromAddressBookInternal(addressBook);

mImpl->mLogger.trace("Address book successfully updated");
}
catch (const std::exception& exception)
{
mImpl->mLogger.warn(std::string("Failed to update address book: ") + exception.what());
}
}

//-----
Client& Client::setNetwork(const std::unordered_map<std::string, AccountId>& networkMap)
{
Expand Down Expand Up @@ -1073,11 +1103,24 @@ std::shared_ptr<internal::MirrorNetwork> Client::getClientMirrorNetwork() const
//-----
void Client::setNetworkFromAddressBookInternal(const NodeAddressBook& addressBook)
{
mImpl->mNetwork->setNetwork(internal::Network::getNetworkFromAddressBook(
std::cout << "setNetworkFromAddressBookInternal: Getting network from address book..." << std::endl;

auto networkMap = internal::Network::getNetworkFromAddressBook(
addressBook,
mImpl->mNetwork->isTransportSecurity() == internal::TLSBehavior::REQUIRE
? internal::BaseNodeAddress::PORT_NODE_TLS
: internal::BaseNodeAddress::PORT_NODE_PLAIN));
: internal::BaseNodeAddress::PORT_NODE_PLAIN);

std::cout << "setNetworkFromAddressBookInternal: Got " << networkMap.size() << " entries, setting network..." << std::endl;

for (const auto& [address, accountId] : networkMap)
{
std::cout << " Network entry: " << address << " -> " << accountId.toString() << std::endl;
}

mImpl->mNetwork->setNetwork(networkMap);

std::cout << "setNetworkFromAddressBookInternal: Done!" << std::endl;
}

//-----
Expand Down
20 changes: 20 additions & 0 deletions src/sdk/main/src/Executable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,24 @@ SdkResponseType Executable<SdkRequestType, ProtoRequestType, ProtoResponseType,
nodeResponses.clear();
[[fallthrough]];
}
case ExecutionStatus::RETRY_WITH_ANOTHER_NODE:
{
std::cout << "Received INVALID_NODE_ACCOUNT; updating addressbook and marking node as unhealthy, nodeAccountId: " << node->getAccountId().toString() << " during attempt #" << attempt << std::endl;
mLogger.trace("Received INVALID_NODE_ACCOUNT; updating addressbook and marking node as unhealthy, nodeAccountId: " +
node->getAccountId().toString() + " during attempt #" + std::to_string(attempt));

// Mark this node as unhealthy
node->increaseBackoff();

// Update address book synchronously before continuing with other nodes
// This ensures we have the latest valid nodes before retrying
// Using const_cast here because we need to call a non-const method on the client
// This is safe because we're only updating internal state that's protected by mutex
const_cast<Client&>(client).updateAddressBook();

// Continue with other nodes (now with updated address book)
continue;
}
// Response isn't ready yet from the network
case ExecutionStatus::RETRY:
{
Expand Down Expand Up @@ -437,6 +455,8 @@ Executable<SdkRequestType, ProtoRequestType, ProtoResponseType, SdkResponseType>
case Status::PLATFORM_NOT_ACTIVE:
case Status::BUSY:
return ExecutionStatus::SERVER_ERROR;
case Status::INVALID_NODE_ACCOUNT:
return ExecutionStatus::RETRY_WITH_ANOTHER_NODE;
case Status::OK:
return ExecutionStatus::SUCCESS;
// Let derived class handle this status, assume request error
Expand Down
5 changes: 4 additions & 1 deletion src/sdk/main/src/NodeUpdateTransaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,10 @@ aproto::NodeUpdateTransactionBody* NodeUpdateTransaction::build() const
body->mutable_service_endpoint()->AddAllocated(e.toProtobuf().release());
}

body->mutable_gossip_ca_certificate()->set_value(internal::Utilities::byteVectorToString(mGossipCaCertificate));
if (!mGossipCaCertificate.empty())
{
body->mutable_gossip_ca_certificate()->set_value(internal::Utilities::byteVectorToString(mGossipCaCertificate));
}

if (mGrpcCertificateHash.has_value())
{
Expand Down
11 changes: 7 additions & 4 deletions src/sdk/main/src/Status.cc
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,8 @@ const std::unordered_map<proto::ResponseCodeEnum, Status> gProtobufResponseCodeT
Status::AIRDROP_CONTAINS_MULTIPLE_SENDERS_FOR_A_TOKEN },
{ proto::ResponseCodeEnum::GRPC_WEB_PROXY_NOT_SUPPORTED, Status::GRPC_WEB_PROXY_NOT_SUPPORTED },
{ proto::ResponseCodeEnum::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE,
Status::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE }
Status::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE },
{ proto::ResponseCodeEnum::NODE_ACCOUNT_HAS_ZERO_BALANCE, Status::NODE_ACCOUNT_HAS_ZERO_BALANCE },
};

//-----
Expand Down Expand Up @@ -764,10 +765,11 @@ const std::unordered_map<Status, proto::ResponseCodeEnum> gStatusToProtobufRespo
{ Status::CREATING_SYSTEM_ENTITIES, proto::ResponseCodeEnum::CREATING_SYSTEM_ENTITIES },
{ Status::THROTTLE_GROUP_LCM_OVERFLOW, proto::ResponseCodeEnum::THROTTLE_GROUP_LCM_OVERFLOW },
{ Status::AIRDROP_CONTAINS_MULTIPLE_SENDERS_FOR_A_TOKEN,
proto::ResponseCodeEnum::AIRDROP_CONTAINS_MULTIPLE_SENDERS_FOR_A_TOKEN },
proto::ResponseCodeEnum::AIRDROP_CONTAINS_MULTIPLE_SENDERS_FOR_A_TOKEN },
{ Status::GRPC_WEB_PROXY_NOT_SUPPORTED, proto::ResponseCodeEnum::GRPC_WEB_PROXY_NOT_SUPPORTED },
{ Status::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE,
proto::ResponseCodeEnum::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE }
proto::ResponseCodeEnum::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE },
{ Status::NODE_ACCOUNT_HAS_ZERO_BALANCE, proto::ResponseCodeEnum::NODE_ACCOUNT_HAS_ZERO_BALANCE },
};

//-----
Expand Down Expand Up @@ -1125,7 +1127,8 @@ const std::unordered_map<Status, std::string> gStatusToString = {
"AIRDROP_CONTAINS_MULTIPLE_SENDERS_FOR_A_TOKEN" },
{ Status::GRPC_WEB_PROXY_NOT_SUPPORTED, "GRPC_WEB_PROXY_NOT_SUPPORTED" },
{ Status::NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE,
"NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE" }
"NFT_TRANSFERS_ONLY_ALLOWED_FOR_NON_FUNGIBLE_UNIQUE" },
{ Status::NODE_ACCOUNT_HAS_ZERO_BALANCE, "NODE_ACCOUNT_HAS_ZERO_BALANCE" },
};

} // namespace Hiero
Loading
Loading