-
Notifications
You must be signed in to change notification settings - Fork 170
Description
Summary
When using Client.setNetworkFromAddressBook(...) with a custom network, the JavaScript Hiero SDK can end up with an empty network without surfacing any error. In this state:
transaction.freezeWith(client)appears to succeed (no error thrown),- but
await transaction.getTransactionHash()later throws:
Error: transaction must have been frozen before calculating the hash will be stable, try calling 'freeze'
From the user’s perspective, they did call freezeWith(client). The real problem is that the client’s network is {} due to how setNetworkFromAddressBook filters nodes.
Details
In Network.js, setNetworkFromAddressBook currently looks roughly like:
setNetworkFromAddressBook(addressBook) {
/** @type {Record<string, AccountId>} */
const network = {};
const port = this.isTransportSecurity() ? 50212 : 50211;
for (const nodeAddress of addressBook.nodeAddresses) {
for (const endpoint of nodeAddress.addresses) {
// TODO: We hard code ports too much, should fix
if (endpoint.port === port && nodeAddress.accountId != null) {
network[endpoint.toString()] = nodeAddress.accountId;
}
}
}
this.setNetwork(network);
return this;
}If the address book entries use a different port (e.g. port: "1" due to misconfiguration), no nodes are added to the network object. The SDK then:
- Silently sets the network to {}.
- Does not warn or throw when the resulting network is empty.
- Allows
freezeWith(client)to be called; this “succeeds” but doesn’t actually mark the transaction as frozen, because no signed transactions are generated (no nodes to sign for). - Later,
getTransactionHash()checks whether the transaction is frozen and throws the generic “transaction must have been frozen” error.
Example Scenario
const client = Client.forNetwork({}); // will be re-initialized from address book
const nodeAddressBook = await new AddressBookQuery()
.setFileId(FileId.ADDRESS_BOOK)
.execute(client);
client.setNetworkFromAddressBook(nodeAddressBook);
if (ledgerId) {
client.setLedgerId(ledgerId);
}Address book entries exist and look valid at a glance (node IDs, account IDs, etc.), but all endpoints are:
"addresses": [
{ "address": "1.0.0.0", "port": "1" }
]Because these ports do not match 50211 or 50212, no nodes are added, so:
client.network()is{}(but this is not surfaced unless explicitly checked).- On the backend:
transaction.freezeWith(client);
await transaction.getTransactionHash(); // throws- Error observed:
Error: transaction must have been frozen before calculating the hash will be stable, try calling `freeze`Even though freezeWith(client) was called, the transaction never became “frozen” due to the empty network.
Actual behavior
Client.setNetworkFromAddressBook(addressBook)silently sets an empty network if no endpoints match the expected port (50211/50212).freezeWith(client)does not throw or log anything in this state.getTransactionHash()throwstransaction must have been frozen..., which misleads the user into thinking they misusedfreezeWithinstead of pointing them toward the network/address book issue.
Expected behavior
At minimum:
-
Validation when setting the network
If
setNetworkFromAddressBookresults in an empty network (no usable nodes), the SDK should:
- Throw a descriptive error, or
- Log a clear warning and refuse to overwrite the network with
{}.
-
More actionable error messaging
When the network is empty, subsequent operations (e.g.
freezeWith,getTransactionHash) should surface that there are no nodes configured, e.g.:Client network is empty after initializing from address book; no usable nodes were found (check ports/IPs).This would have immediately pointed us toward the misconfigured address book (ports being
"1"instead of50211/50212) and avoided confusion around freezing vs. transaction hash generation.
Proposed improvements
-
In
setNetworkFromAddressBook:After building
network, ifObject.keys(network).length === 0, throw an error such as:setNetworkFromAddressBook: address book contained no nodes matching the expected port (50211/50212); network would be empty. -
Optionally include basic debug info (number of addresses scanned, distinct ports seen, etc.), or provide a helper that can show “what the SDK parsed” from the address book for easier troubleshooting.
-
(Optional / future) Revisit the hard-coded port behavior and the
// TODO: We hard code ports too much, should fixcomment, to support more flexible/custom node ports in the JS SDK.