Class BlockNodeClient
Hiero Block Node Client
Implements
Inherited Members
Namespace: Hiero
Assembly: Hiero.dll
Syntax
[Experimental("HIERO001", UrlFormat = "https://github.com/bugbytesinc/Hiero/blob/main/docs/blocknode-client-plan.md")]
public sealed class BlockNodeClient : IAsyncDisposable
Remarks
This client facilitates access to a Hiero Block Node: streaming blocks from the block stream in near real-time, retrieving individual blocks, querying node status, obtaining block contents proofs and state snapshot references, and (for advanced producer scenarios) publishing block streams.
The Block Node service and this client surface are experimental; the API may change in any minor release while the upstream Block Node protocol stabilizes.
Thread safety. An instance is safe for concurrent use and is intended to be shared — a singleton registered with dependency injection is the expected pattern. Each operation builds its own child context, so concurrent calls share no mutable state, and the underlying gRPC channels are pooled in a thread-safe cache.
Configure(Action<IBlockNodeContext>) is the exception. It mutates this instance's own
context in place, which every in-flight operation reads through, and is
meant for setup before the client is used or while it is otherwise idle.
To vary settings on a live client, pass the per-call configure
argument — it applies only to that one call — or take a separately
configured instance with Clone(Action<IBlockNodeContext>?).
Constructors
| Edit this page View SourceBlockNodeClient(Action<IBlockNodeContext>?)
Creates a new instance of a Hiero Block Node Client.
Declaration
public BlockNodeClient(Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| Action<IBlockNodeContext> | configure | Optional configuration method that can set the location of the block node and retry behavior. |
Remarks
Creating a new instance of a
BlockNodeClient
initializes a new instance of a client. It will have a separate cache of GRPC channels to the network and will maintain a separate configuration from other clients. The constructor takes an optional callback method that configures the details on how the client should connect to the block node. See the IBlockNodeContext documentation for configuration details.
BlockNodeClient(Func<Uri, GrpcChannel>, Action<IBlockNodeContext>?)
Creates a new instance of a Hiero Block Node Client with a custom gRPC channel factory.
Declaration
public BlockNodeClient(Func<Uri, GrpcChannel> channelFactory, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| Func<Uri, GrpcChannel> | channelFactory | A custom callback method returning a new channel given the target block node URI. Note, this method is only called once for each unique URI requested by the block node client (which is a function of the current context's URI parameter) |
| Action<IBlockNodeContext> | configure | Optional configuration method that can set the location of the block node and retry behavior. |
Remarks
Creating a new instance of a
BlockNodeClient
initializes a new instance of a client. It will have a separate cache of GRPC channels to the network and will maintain a separate configuration from other clients. The constructor takes an optional callback method that configures the details on how the client should connect to the block node. See the IBlockNodeContext documentation for configuration details.
Methods
| Edit this page View SourceClone(Action<IBlockNodeContext>?)
Creates a new instance of the block node client having a shared base configuration with its parent. Changes to the parent’s configuration will reflect in this instances configuration while changes in this instances configuration will not be reflected in the parent configuration.
Declaration
public BlockNodeClient Clone(Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| Action<IBlockNodeContext> | configure | The callback method receiving the IBlockNodeContext object providing the configuration details of this client instance. Values can be retrieved and set within the context of the method invocation. |
Returns
| Type | Description |
|---|---|
| BlockNodeClient | A new instance of a client object. |
Configure(Action<IBlockNodeContext>)
Updates the configuration of this instance of a block node client through implementation of the supplied IBlockNodeContext callback method.
Declaration
public void Configure(Action<IBlockNodeContext> configure)
Parameters
| Type | Name | Description |
|---|---|---|
| Action<IBlockNodeContext> | configure | The callback method receiving the IBlockNodeContext object providing the configuration details of this client instance. Values can be retrieved and set within the context of the method invocation. |
Remarks
Mutates this client's own context in place. Every operation already in
flight reads its inherited settings through that context, so calling
this concurrently with active work can let a call observe a partially
applied configuration. Configure before use, or while the client is
idle. For a live client, prefer the per-call configure argument
(scoped to that call alone) or Clone(Action<IBlockNodeContext>?) for a separately
configured instance.
CreateBlockStreamPublisherAsync(CancellationToken, Action<IBlockNodeContext>?)
Opens a block stream publish session with the block node — the advanced, producer-side surface used by block-producing tooling, simulators and repeaters.
Declaration
public Task<BlockStreamPublisher> CreateBlockStreamPublisherAsync(CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken | An optional token that can interrupt the session; firing it aborts the underlying stream. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this session. |
Returns
| Type | Description |
|---|---|
| Task<BlockStreamPublisher> | An open publish session. |
Remarks
The returned BlockStreamPublisher exposes the publish protocol's state machine honestly: the caller writes blocks (whole or item-by-item), MUST consume the Events stream to react to skip/resend/behind directives, and awaits cumulative acknowledgements. End the session gracefully with EndStreamAsync(PublishStreamEndCode, ulong, ulong, CancellationToken); disposing without it aborts the call.
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
DisposeAsync()
.NET Asynchronous dispose method.
Declaration
public ValueTask DisposeAsync()
Returns
| Type | Description |
|---|---|
| ValueTask | An Async Task. |
Remarks
Closes any GRPC channels solely owned by this
BlockNodeClient
instance.
GetBlockAsync(ulong, CancellationToken, Action<IBlockNodeContext>?)
Retrieves a single block by block number.
Declaration
public Task<Block?> GetBlockAsync(ulong blockNumber, CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| ulong | blockNumber | The number of the block to retrieve. |
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<Block> |
when the node reports the block as not found. A block that falls outside the node's available range (pruned or not yet produced) raises a BlockNodeException with code NotAvailable instead. |
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the block node rejected the request or communication failed. |
GetBlockContentsProofAsync(GetBlockContentsProofParams, CancellationToken, Action<IBlockNodeContext>?)
Retrieves a proof that a specific item is contained within a specific block: the item, the merkle sibling hashes forming the path to the block root, and the block's aggregate signature.
Declaration
public Task<BlockContentsProof?> GetBlockContentsProofAsync(GetBlockContentsProofParams proofParameters, CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| GetBlockContentsProofParams | proofParameters | Identifies the block and the item of interest; the item may be specified by complete item, item hash, or item index (exactly one). |
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<BlockContentsProof> |
when the node reports the item as not found within the block. |
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | If required arguments are missing. |
| ArgumentOutOfRangeException | If the item specifier is missing or ambiguous. |
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the block node rejected the request or communication failed. |
GetLatestBlockAsync(CancellationToken, Action<IBlockNodeContext>?)
Retrieves the latest block held by the block node.
Declaration
public Task<Block> GetLatestBlockAsync(CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<Block> | The latest block held by the node. |
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the node holds no blocks, rejected the request, or communication failed. |
GetLatestStateSnapshotAsync(CancellationToken, Action<IBlockNodeContext>?)
Retrieves a reference to the latest network state snapshot the block node holds.
Declaration
public Task<StateSnapshot> GetLatestStateSnapshotAsync(CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<StateSnapshot> | A reference from which the snapshot content can be retrieved. |
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the block node rejected the request or communication failed. |
GetServerStatusAsync(CancellationToken, Action<IBlockNodeContext>?)
Retrieves the basic status of the block node: the span of blocks it can currently serve.
Declaration
public Task<BlockNodeStatus> GetServerStatusAsync(CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<BlockNodeStatus> | The node's available block range and state retention mode. |
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If communication with the block node failed. |
GetServerStatusDetailAsync(CancellationToken, Action<IBlockNodeContext>?)
Retrieves the detailed status of the block node: versions, installed plugins, available and stored block ranges, TSS verification bootstrap data and consensus address books.
Declaration
public Task<BlockNodeStatusDetail> GetServerStatusDetailAsync(CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<BlockNodeStatusDetail> | The node's detailed status information. |
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If communication with the block node failed. |
GetStateSnapshotAsync(ulong, CancellationToken, Action<IBlockNodeContext>?)
Retrieves a reference to the network state snapshot as of a given block number.
Declaration
public Task<StateSnapshot> GetStateSnapshotAsync(ulong lastBlockNumber, CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| ulong | lastBlockNumber | The block number the snapshot must reflect. |
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<StateSnapshot> | A reference from which the snapshot content can be retrieved. |
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the block node rejected the request or communication failed. |
GetVerifiedBlockAsync(ulong, CancellationToken, Action<IBlockNodeContext>?)
Retrieves a single block by block number and checks its SHA-384 merkle root against the previous-block root hash of the following block served by the same node, populating VerifiedRootHash on success.
Declaration
public Task<Block?> GetVerifiedBlockAsync(ulong blockNumber, CancellationToken cancellationToken = default, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| ulong | blockNumber | The number of the block to retrieve and verify. |
| CancellationToken | cancellationToken | An optional token that can interrupt the request. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task<Block> | The requested, verified block (with VerifiedRootHash populated), or
when the node reports the block as not found. |
Remarks
Verification recomputes the block's root from its items and confirms it against the following block's previous-block root hash — so this method makes a second block request (the verification anchor) and performs the merkle recomputation, work the plain GetBlockAsync(ulong, CancellationToken, Action<IBlockNodeContext>?) does not. The anchor comes from the same node and is not itself authenticated, so this is an integrity and consistency check, not authentication: the block proof's signature is not checked. The block stream's tip cannot be verified this way because no following block exists yet; there is deliberately no verified-latest counterpart to GetLatestBlockAsync(CancellationToken, Action<IBlockNodeContext>?).
Exceptions
| Type | Condition |
|---|---|
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If verification fails (VerificationFailed — including when the anchor block is not yet available), the block holds content this SDK cannot hash with certainty (Unverifiable), or the block node rejected the request or communication failed. |
SubscribeBlockItemStreamAsync(SubscribeBlockItemStreamParams, Action<IBlockNodeContext>?)
Subscribes to the block node's block stream at the item level, placing each block item into the provided .NET Channel the moment its frame arrives — before the containing block is complete or verified.
Declaration
public Task SubscribeBlockItemStreamAsync(SubscribeBlockItemStreamParams subscribeParameters, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| SubscribeBlockItemStreamParams | subscribeParameters | The details of the subscription: block range, delivery channel and gap policy. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task | Returns only after one of the following conditions occur: the output channel is completed by calling code; the cancellation token provided in the params is signaled; the server ended a bounded stream; or the stream faulted beyond recovery, in which case a BlockNodeException is thrown. |
Remarks
This is the low-latency mode: an item can be delivered a second or more before its block's boundary marker, which matters for consumers reacting to events (e.g. EVM contract outputs) where milliseconds count. In exchange, every safety of the assembled mode is traded away: no buffering, no duplicate dropping, no structural verification — a resent block is delivered again under the same number. See StreamedBlockItem for the reconciliation contract. Gaps in completed block numbers and transport drops are still recovered per GapPolicy, resuming from the last completed block + 1.
Examples
Observe items mid-block, before verification — the low-latency mode for event monitors that reconcile on block completion:
await using var client = new BlockNodeClient(ctx =>
{
ctx.Uri = new Uri(blockNodeEndpoint);
// Only for nodes serving their status API on a separate endpoint;
// when null, status calls share Uri.
ctx.StatusUri = statusEndpoint;
});
Console.WriteLine("Tailing live block items — delivered mid-block, before verification (Ctrl+C to stop)...");
var channel = Channel.CreateBounded<StreamedBlockItem>(256);
var completedBlocks = 0UL;
// Item-level delivery: each item arrives the moment its frame does,
// potentially a second or more before the block's proof — useful when
// reacting to events (e.g. EVM contract outputs) where milliseconds
// matter and provisional data can be reconciled later.
var subscribeTask = client.SubscribeBlockItemStreamAsync(new SubscribeBlockItemStreamParams
{
ItemWriter = channel.Writer,
CancellationToken = cts.Token
});
try
{
await foreach (var streamed in channel.Reader.ReadAllAsync(cts.Token))
{
if (streamed.Item.Kind == BlockItemKind.TransactionOutput || streamed.Item.Kind == BlockItemKind.SignedTransaction)
{
Console.WriteLine($"Block {streamed.BlockNumber} (in progress): {streamed.Item.Kind} observed");
}
if (streamed.IsBlockComplete)
{
Console.WriteLine($"Block {streamed.BlockNumber} complete");
completedBlocks++;
if (maxBlocks > 0 && completedBlocks >= maxBlocks)
{
cts.Cancel();
}
}
}
}
catch (OperationCanceledException)
{
// Ctrl+C or maxBlocks reached.
}
await subscribeTask;
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | If required arguments are missing. |
| ArgumentOutOfRangeException | If the requested block range is inverted. |
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the block stream faulted beyond the configured recovery policy. |
SubscribeBlockStreamAsync(SubscribeBlockStreamParams, Action<IBlockNodeContext>?)
Subscribes to the block node's block stream, assembling item frames into whole blocks and placing each completed block into the provided .NET Channel.
Declaration
public Task SubscribeBlockStreamAsync(SubscribeBlockStreamParams subscribeParameters, Action<IBlockNodeContext>? configure = null)
Parameters
| Type | Name | Description |
|---|---|---|
| SubscribeBlockStreamParams | subscribeParameters | The details of the subscription: block range, delivery channel, verification level and gap policy. |
| Action<IBlockNodeContext> | configure | Optional callback method providing an opportunity to modify the execution configuration for just this method call. |
Returns
| Type | Description |
|---|---|
| Task | Returns only after one of the following conditions occur: the output channel is completed by calling code; the cancellation token provided in the params is signaled; the maximum number of blocks was delivered; the ending block was delivered (or the server ended the bounded stream); or the stream faulted beyond recovery, in which case a BlockNodeException is thrown. |
Remarks
The block stream is forwarded by the node unverified; gaps, resends and malformed blocks are protocol-normal. This method owns those sharp edges: partial blocks superseded by a resend are discarded, duplicates are dropped, each completed block is validated per VerificationLevel, and gaps or transport drops are handled per GapPolicy — by default resubscribing from the last delivered block + 1 using the context's retry settings. Only completed blocks are ever written to the channel.
Examples
Tail live assembled blocks — reads keep pace with the incoming
stream via a Channel, with gaps and reconnects handled
automatically:
await using var client = new BlockNodeClient(ctx =>
{
ctx.Uri = new Uri(blockNodeEndpoint);
// Only for nodes serving their status API on a separate endpoint;
// when null, status calls share Uri.
ctx.StatusUri = statusEndpoint;
});
var status = await client.GetServerStatusAsync();
Console.WriteLine($"Block node serves blocks {status.FirstAvailableBlockNumber}..{status.LastAvailableBlockNumber}");
Console.WriteLine("Tailing live blocks (Ctrl+C to stop)...");
var channel = Channel.CreateBounded<Block>(32);
// Start the subscription in the background. Assembled blocks arrive
// via channel.Writer; gaps and reconnects are handled automatically
// (GapPolicy.Resubscribe) and each block is structurally validated
// (VerificationLevel.Structural) before delivery.
var subscribeTask = client.SubscribeBlockStreamAsync(new SubscribeBlockStreamParams
{
BlockWriter = channel.Writer,
MaxCount = maxBlocks,
CancellationToken = cts.Token
});
// Read blocks as they arrive.
try
{
await foreach (var block in channel.Reader.ReadAllAsync(cts.Token))
{
var transactions = block.Items.Count(i => i.Kind == BlockItemKind.SignedTransaction);
Console.WriteLine($"Block {block.Number} @ {block.Timestamp}: {block.Items.Count} items, {transactions} transactions, proof: {block.Proof?.Kind}");
}
}
catch (OperationCanceledException)
{
// Ctrl+C — fall through to await the subscription's own shutdown.
}
await subscribeTask;
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | If required arguments are missing. |
| ArgumentOutOfRangeException | If the requested block range is inverted, or if ResumeFromCheckpoint is combined with a starting block number, used with a verification level below HashChain, or carries a root hash that is not 48 bytes. |
| InvalidOperationException | If the block node URL configuration is missing. |
| BlockNodeException | If the block stream faulted beyond the configured recovery policy. |