Repository: hiero-ledger/hiero-consensus-node
Pull request #19429: chore: Use depth 3 subtrees for calculating blockHash
Author: login: Neeharika-Sompalli
Created: 2025-05-28T23:54:22Z | Merged: 2025-05-30T21:55:41Z

Fixes #19330 

- Based on latest HIP updates, changed to use depth 3 subtree and 8 leaves to calculate blockHash using (previous block hash, start of block state hash, consensus headers tree hash, input item tree hash, output item tree hash, state changes tree hash, trace data tree hash, and <NULL>).
The new format is as described in the picture
<img width="1395" alt="image" src="https://github.com/user-attachments/assets/3f7709ec-092f-4d22-966e-fec875d8e373" />

- Updated the `BlockStreamInfo` to contain all the hashes except stateChanges, so we can reconstruct the blockHash on restart or reconnect
- Added new protobufs for `TraceData` as described in the HIP

Linked issue #19330: Use depth 3 subtrees for block consensus headers, state changes, and trace data
The initial block Merkle tree was defined by four depth 2 subtrees (previous block hash, input item tree hash, output item tree hash, and start of block state hash).

We now want to specify the block Merkle tree by eight depth 3 subtrees (previous block hash, start of block state hash, consensus headers tree hash, input item tree hash, output item tree hash, state changes tree hash, trace data tree hash, and `<NULL>`).

Diff:
diff --git a/hapi/hapi/src/main/java/module-info.java b/hapi/hapi/src/main/java/module-info.java
index 792382de5116..e00a1dcbfeb6 100644
--- a/hapi/hapi/src/main/java/module-info.java
+++ b/hapi/hapi/src/main/java/module-info.java
@@ -78,6 +78,8 @@
     exports com.hedera.hapi.platform.event.legacy;
     exports com.hedera.hapi.node.state.entity;
     exports org.hiero.block.api.codec;
+    exports com.hedera.hapi.block.stream.trace;
+    exports com.hedera.hapi.block.stream.trace.protoc;
 
     // for testing against Google protobuf
     opens com.hedera.hapi.block.stream.input.protoc;
diff --git a/hapi/hedera-protobuf-java-api/src/main/java/module-info.java b/hapi/hedera-protobuf-java-api/src/main/java/module-info.java
index 2563ce7aa673..7c0b2176faea 100644
--- a/hapi/hedera-protobuf-java-api/src/main/java/module-info.java
+++ b/hapi/hedera-protobuf-java-api/src/main/java/module-info.java
@@ -3,6 +3,7 @@
     exports org.hiero.block.api.protoc;
     exports com.hedera.hapi.block.stream.input.protoc;
     exports com.hedera.hapi.block.stream.output.protoc;
+    exports com.hedera.hapi.block.stream.trace.protoc;
     exports com.hedera.hapi.block.stream.protoc;
     exports com.hedera.hapi.node.state.tss.legacy;
     exports com.hedera.hapi.platform.event.legacy;
diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/block_item.proto b/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/block_item.proto
index 11e25b3efccb..735ebe454069 100644
--- a/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/block_item.proto
+++ b/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/block_item.proto
@@ -37,6 +37,7 @@ import "block/stream/output/block_header.proto";
 import "block/stream/output/state_changes.proto";
 import "block/stream/output/transaction_output.proto";
 import "block/stream/output/transaction_result.proto";
+import "block/stream/trace/trace_data.proto";
 
 /**
  * A single item within a block stream.
@@ -238,6 +239,14 @@ message BlockItem {
          * </ul>
          */
         RecordFileItem record_file = 10;
+
+        /**
+         * A trace data.
+         * <p>
+         * Any informational trace data MAY be described by
+         * stream items of this type.<br/>
+         */
+        com.hedera.hapi.block.stream.trace.TraceData trace_data = 16;
     }
 }
 
diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/trace/smart_contract_service.proto b/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/trace/smart_contract_service.proto
new file mode 100644
index 000000000000..ad534ef747ed
--- /dev/null
+++ b/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/trace/smart_contract_service.proto
@@ -0,0 +1,159 @@
+/**
+ * # Smart Contract Service
+ * Block stream messages that report the trace data of transactions handled
+ * by the `smart contract` service.
+ *
+ * ### Keywords
+ * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ * document are to be interpreted as described in
+ * [RFC2119](https://www.ietf.org/rfc/rfc2119) and clarified in
+ * [RFC8174](https://www.ietf.org/rfc/rfc8174).
+ */
+syntax = "proto3";
+
+package com.hedera.hapi.block.stream.trace;
+
+// SPDX-License-Identifier: Apache-2.0
+option java_package = "com.hedera.hapi.block.stream.trace.protoc";
+// <<<pbj.java_package = "com.hedera.hapi.block.stream.trace">>> This comment is special code for setting PBJ Compiler java package
+option java_multiple_files = true;
+
+import "services/basic_types.proto";
+import "services/contract_types.proto";
+import "streams/sidecar_file.proto";
+import "streams/contract_action.proto";
+import "google/protobuf/wrappers.proto";
+
+/**
+ * Usage of a contract's storage slots in an EVM transaction.<br/>
+ */
+message ContractSlotUsage {
+  /**
+   * The contract using the storage slots.
+   */
+  proto.ContractID contract_id = 1;
+
+  /**
+   * The keys of storage slots that were written to in the EVM transaction.
+   * These keys are 256-bit words, left-padded with zeros if necessary.<br/>
+   * Interested consumers can get the written values from the state changes
+   * block item of the transactional unit containing this trace data.
+   */
+  repeated bytes written_slot_keys = 2;
+
+  /**
+   * The storage slots that were read in this EVM execution. They may or
+   * may not have associated slot writes.
+   */
+  repeated SlotRead slot_reads = 3;
+}
+
+/**
+ * A read slot in a contract's storage, as used in an EVM transaction.<br/>
+ */
+message SlotRead {
+  oneof identifier {
+    /**
+    * If this slot was also written, the index in the ContractSlotUsage#written_slot_keys
+    * list above.
+    */
+    int32 index = 1;
+
+    /**
+    * If this slot was only read, its key; may be left-padded with zeros to form a
+    * 256-bit word.
+    */
+    bytes key = 2;
+  }
+
+  /**
+  * The storage value in this slot, may be left-padded with zeros to form a 256-bit word.
+  */
+  bytes read_value = 3;
+}
+
+/**
+ * The initcode of a contract, possibly deduplicated if the runtime part
+ * is already externalized in state changes.
+ */
+message ContractInitcode {
+  /**
+   * The contract created from the initcode.
+   */
+  proto.ContractID contract_id = 1;
+
+  /**
+   * The bytecode that precedes and deploys the runtime bytecode of a contract.
+   */
+  bytes deploy_bytecode = 2;
+
+  /**
+   * If not externalized in state, the bytecode that defines a deployed
+   * contracts logic.
+   */
+  google.protobuf.BytesValue runtime_bytecode = 3;
+
+  /**
+   * The bytecode that follows the runtime bytecode in a contracts deployment.
+   */
+  bytes metadata_bytecode = 4;
+}
+
+/**
+ * A EVM transaction log; c.f. eth_getTransactionReceipt.<br/>
+ * Stream consumers may compute bloom values from topics and data if desired.
+ */
+message EVMTransactionLog {
+  /**
+   * The contract emitting the log.
+   */
+  proto.ContractID contract_id = 1;
+
+  /**
+   * The logged data.
+   */
+  bytes data = 2;
+
+  /**
+   * The log's topics; may be left-padded with zeros to form 256-bit words.
+   */
+  repeated bytes topics = 3;
+}
+
+/**
+ * EVM trace data, including:
+ * 1. Initcode used for any internal contract creations.
+ * 2. Contract actions executed during the transaction (i.e., the call trace)
+ * 3. Contract slot usages, including reads and writes.
+ * 4. Events logged during the transaction.
+ * 5. Full error message, if any, produced by the contract call.
+ */
+message EVMTraceData {
+  /**
+   * The initcode of child contracts created during execution.
+   */
+  repeated ContractInitcode initcodes = 1;
+
+  /**
+   * All contract actions executed during the transaction.
+   */
+  repeated proto.ContractAction contract_actions = 2;
+
+  /**
+   * Contract slot usages in the transaction, by contract id.
+   */
+  repeated ContractSlotUsage contract_slot_usages = 3;
+
+  /**
+   * Log events produced during the transaction, by contract id.
+   */
+  repeated EVMTransactionLog logs = 4;
+
+  /**
+   * Additional details of any error message in the EVM transaction result.
+   * <p>
+   * This SHALL be unset if the contract call succeeded.
+   */
+  string error_details = 5;
+}
\ No newline at end of file
diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/trace/trace_data.proto b/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/trace/trace_data.proto
new file mode 100644
index 000000000000..a1d48ab2dadf
--- /dev/null
+++ b/hapi/hedera-protobuf-java-api/src/main/proto/block/stream/trace/trace_data.proto
@@ -0,0 +1,38 @@
+/**
+ * # Trace Data
+ * A trace data item includes information that can be considered
+ * trace or debugging in nature, but with material value to at
+ * least some stream consumers. This includes EVM execution call
+ * traces; storage access and updates; and logs.
+ *
+ * ### Keywords
+ * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ * document are to be interpreted as described in
+ * [RFC2119](https://www.ietf.org/rfc/rfc2119) and clarified in
+ * [RFC8174](https://www.ietf.org/rfc/rfc8174).
+ */
+syntax = "proto3";
+
+package com.hedera.hapi.block.stream.trace;
+
+// SPDX-License-Identifier: Apache-2.0
+option java_package = "com.hedera.hapi.block.stream.trace.protoc";
+// <<<pbj.java_package = "com.hedera.hapi.block.stream.trace">>> This comment is special code for setting PBJ Compiler java package
+option java_multiple_files = true;
+
+import "block/stream/trace/smart_contract_service.proto";
+
+/**
+ * A Trace Data.
+ *
+ * Trace data SHALL provide non-essential information needed to give an
+ * enriched view of a transaction's handling to stream consumers.<br/>
+ *
+ * There are no REQUIRED fields of this message.
+ */
+message TraceData {
+  oneof data {
+    EVMTraceData evm_trace_data = 1;
+  }
+}
\ No newline at end of file
diff --git a/hapi/hedera-protobuf-java-api/src/main/proto/services/state/blockstream/block_stream_info.proto b/hapi/hedera-protobuf-java-api/src/main/proto/services/state/blockstream/block_stream_info.proto
index 43ac05d0ff1f..2c3fffb65955 100644
--- a/hapi/hedera-protobuf-java-api/src/main/proto/services/state/blockstream/block_stream_info.proto
+++ b/hapi/hedera-protobuf-java-api/src/main/proto/services/state/blockstream/block_stream_info.proto
@@ -99,7 +99,7 @@ message BlockStreamInfo {
      * This SHALL count the number of output block items that _precede_
      * the state change that updates this singleton.
      */
-    uint32 num_preceding_output_items = 7;
+    uint32 num_preceding_state_changes_items = 7;
 
     /**
      * A concatenation of SHA2-384 hash values.<br/>
@@ -108,7 +108,7 @@ message BlockStreamInfo {
      * The subtree containing these hashes SHALL be constructed from all "output"
      * `BlockItem`s in this block that _precede_ the update to this singleton.
      */
-    repeated bytes rightmost_preceding_output_tree_hashes = 8;
+    repeated bytes rightmost_preceding_state_changes_tree_hashes = 8;
 
     /**
      * A block-end consensus time stamp.
@@ -148,4 +148,22 @@ message BlockStreamInfo {
      * at which a user transaction was handled.
      */
     proto.Timestamp last_handle_time = 13;
+
+    /**
+    * A SHA2-384 hash value.<br/>
+    * This is the hash of the "consensus headers" subtree for this block.
+    */
+    bytes consensus_header_tree_root_hash = 14;
+
+    /**
+    * A SHA2-384 hash value.<br/>
+    * This is the hash of the "trace data" subtree for this block.
+    */
+    bytes trace_data_tree_root_hash = 15;
+
+    /**
+   * A SHA2-384 hash value.<br/>
+   * This is the hash of the "output" subtree for this block.
+   */
+    bytes output_tree_root_hash = 16;
 }
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java
index 0353e23bcea8..36753d1e45a6 100644
--- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java
@@ -9,6 +9,7 @@
 import static com.hedera.hapi.util.HapiUtils.functionOf;
 import static com.hedera.node.app.blocks.BlockStreamManager.ZERO_BLOCK_HASH;
 import static com.hedera.node.app.blocks.impl.BlockImplUtils.combine;
+import static com.hedera.node.app.blocks.impl.BlockStreamManagerImpl.NULL_HASH;
 import static com.hedera.node.app.blocks.impl.ConcurrentStreamingTreeHasher.rootHashFrom;
 import static com.hedera.node.app.blocks.schemas.V0560BlockStreamSchema.BLOCK_STREAM_INFO_KEY;
 import static com.hedera.node.app.hapi.utils.CommonUtils.noThrowSha384HashOf;
@@ -1217,41 +1218,47 @@ private Bytes startBlockHashFrom(@NonNull final State state) {
                         blockStreamInfo.blockNumber() - 1,
                         blockStreamInfo.blockNumber() - 1);
         requireNonNull(prevBlockHash);
-        final var leftParent = combine(prevBlockHash, blockStreamInfo.inputTreeRootHash());
-        // The fourth ingredient, the output tree root hash, is not directly in the BlockStreamInfo, but
-        // we can recompute it based on the tree hash information and the fact the last output item in
+
+        // The fourth ingredient, the state changes tree root hash, is not directly in the BlockStreamInfo, but
+        // we can recompute it based on the tree hash information and the fact the last state changes item in
         // the block was devoted to putting the BlockStreamInfo itself into the state
-        final var outputTreeRootHash = outputTreeRootHashFrom(blockStreamInfo);
-        final var rightParent = combine(outputTreeRootHash, blockStreamInfo.startOfBlockStateHash());
+        final var stateChangesHash = stateChangesTreeRootHashFrom(blockStreamInfo);
+
+        final var level1A = combine(prevBlockHash, blockStreamInfo.startOfBlockStateHash());
+        final var level1B = combine(blockStreamInfo.consensusHeaderTreeRootHash(), blockStreamInfo.inputTreeRootHash());
+        final var level1C = combine(blockStreamInfo.outputTreeRootHash(), stateChangesHash);
+        final var level1D = combine(blockStreamInfo.traceDataTreeRootHash(), NULL_HASH);
+        final var leftParent = combine(level1A, level1B);
+        final var rightParent = combine(level1C, level1D);
         return combine(leftParent, rightParent);
     }
 
     /**
-     * Given a {@link BlockStreamInfo} context, computes the output tree root hash that must have been
-     * computed at the end of the block that the context describes, assuming the final output block item
+     * Given a {@link BlockStreamInfo} context, computes the state changes tree root hash that must have been
+     * computed at the end of the block that the context describes, assuming the final state change block item
      * was the state change that put the context into the state.
      *
-     * @param blockStreamInfo the context to use
+     * @param info the context to use
      * @return the inferred output tree root hash
      */
-    private @NonNull Bytes outputTreeRootHashFrom(@NonNull final BlockStreamInfo blockStreamInfo) {
+    private @NonNull Bytes stateChangesTreeRootHashFrom(@NonNull final BlockStreamInfo info) {
         // This was the last state change in the block
         final var blockStreamInfoChange = StateChange.newBuilder()
                 .stateId(STATE_ID_BLOCK_STREAM_INFO.protoOrdinal())
                 .singletonUpdate(SingletonUpdateChange.newBuilder()
-                        .blockStreamInfoValue(blockStreamInfo)
+                        .blockStreamInfoValue(info)
                         .build())
                 .build();
         // And this was the last output block item
         final var lastStateChanges = BlockItem.newBuilder()
-                .stateChanges(new StateChanges(blockStreamInfo.blockEndTime(), List.of(blockStreamInfoChange)))
+                .stateChanges(new StateChanges(info.blockEndTime(), List.of(blockStreamInfoChange)))
                 .build();
         // So we can combine this last leaf's has with the size and rightmost hashes
-        // store from the pending output tree to recompute its final root hash
-        final var penultimateOutputTreeStatus = new StreamingTreeHasher.Status(
-                blockStreamInfo.numPrecedingOutputItems(), blockStreamInfo.rightmostPrecedingOutputTreeHashes());
+        // store from the pending state changes tree to recompute its final root hash
+        final var penultimateStateChangesTreeStatus = new StreamingTreeHasher.Status(
+                info.numPrecedingStateChangesItems(), info.rightmostPrecedingStateChangesTreeHashes());
         final var lastLeafHash = noThrowSha384HashOf(BlockItem.PROTOBUF.toBytes(lastStateChanges));
-        return rootHashFrom(penultimateOutputTreeStatus, lastLeafHash);
+        return rootHashFrom(penultimateStateChangesTreeStatus, lastLeafHash);
     }
 
     private void logConfiguration() {
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImpl.java
index c2e12645a21b..ebad3c932d5c 100644
--- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImpl.java
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImpl.java
@@ -88,6 +88,7 @@
 @Singleton
 public class BlockStreamManagerImpl implements BlockStreamManager {
     private static final Logger log = LogManager.getLogger(BlockStreamManagerImpl.class);
+    public static final Bytes NULL_HASH = Bytes.wrap(new byte[HASH_SIZE]);
 
     private final int roundsPerBlock;
     private final Duration blockPeriod;
@@ -128,8 +129,13 @@ public class BlockStreamManagerImpl implements BlockStreamManager {
     private Instant blockTimestamp;
     private Instant consensusTimeLastRound;
     private BlockItemWriter writer;
+    // stream hashers
     private StreamingTreeHasher inputTreeHasher;
     private StreamingTreeHasher outputTreeHasher;
+    private StreamingTreeHasher consensusHeaderHasher;
+    private StreamingTreeHasher stateChangesHasher;
+    private StreamingTreeHasher traceDataHasher;
+
     private BlockStreamManagerTask worker;
     private final boolean hintsEnabled;
 
@@ -273,8 +279,13 @@ public void startRound(@NonNull final Round round, @NonNull final State state) {
             runningHashManager.startBlock(blockStreamInfo);
 
             lifecycle.onOpenBlock(state);
+
             inputTreeHasher = new ConcurrentStreamingTreeHasher(executor, hashCombineBatchSize);
             outputTreeHasher = new ConcurrentStreamingTreeHasher(executor, hashCombineBatchSize);
+            consensusHeaderHasher = new ConcurrentStreamingTreeHasher(executor, hashCombineBatchSize);
+            stateChangesHasher = new ConcurrentStreamingTreeHasher(executor, hashCombineBatchSize);
+            traceDataHasher = new ConcurrentStreamingTreeHasher(executor, hashCombineBatchSize);
+
             blockNumber = blockStreamInfo.blockNumber() + 1;
             if (hintsEnabled && !hasCheckedForPendingBlocks) {
                 final var hasBeenFrozen = requireNonNull(state.getReadableStates(PlatformStateService.NAME)
@@ -378,7 +389,11 @@ public boolean endRound(@NonNull final State state, final long roundNum) {
             worker.addItem(boundaryStateChangeListener.flushChanges());
             worker.sync();
 
+            final var consensusHeaderHash = consensusHeaderHasher.rootHash().join();
             final var inputHash = inputTreeHasher.rootHash().join();
+            final var traceDataHash = traceDataHasher.rootHash().join();
+            final var outputHash = outputTreeHasher.rootHash().join();
+
             // This block's starting state hash is the end state hash of the last non-empty round
             final var blockStartStateHash = requireNonNull(endRoundStateHashes.get(lastRoundOfPrevBlock))
                     .join();
@@ -388,7 +403,7 @@ public boolean endRound(@NonNull final State state, final long roundNum) {
             }
             // And update the last non-empty round number to this round
             lastRoundOfPrevBlock = roundNum;
-            final var outputTreeStatus = outputTreeHasher.status();
+            final var stateChangesTreeStatus = stateChangesHasher.status();
 
             // Put this block hash context in state via the block stream info
             final var writableState = state.getWritableStates(BlockStreamService.NAME);
@@ -401,22 +416,34 @@ public boolean endRound(@NonNull final State state, final long roundNum) {
                     blockHashManager.blockHashes(),
                     inputHash,
                     blockStartStateHash,
-                    outputTreeStatus.numLeaves(),
-                    outputTreeStatus.rightmostHashes(),
+                    stateChangesTreeStatus.numLeaves(),
+                    stateChangesTreeStatus.rightmostHashes(),
                     boundaryTimestamp,
                     pendingWork != POST_UPGRADE_WORK,
                     version,
                     asTimestamp(lastIntervalProcessTime),
-                    asTimestamp(lastHandleTime)));
+                    asTimestamp(lastHandleTime),
+                    consensusHeaderHash,
+                    traceDataHash,
+                    outputHash));
             ((CommittableWritableStates) writableState).commit();
 
             worker.addItem(boundaryStateChangeListener.flushChanges());
             worker.sync();
 
-            final var outputHash = outputTreeHasher.rootHash().join();
-            final var leftParent = combine(lastBlockHash, inputHash);
-            final var rightParent = combine(outputHash, blockStartStateHash);
+            final var stateChangesHash = stateChangesHasher.rootHash().join();
+
+            // compute level 1 hashes
+            final var level1A = combine(lastBlockHash, blockStartStateHash); // [0,1]
+            final var level1B = combine(consensusHeaderHash, inputHash); // [2,3]
+            final var level1C = combine(outputHash, stateChangesHash); // [4,5]
+            final var level1D = combine(traceDataHash, NULL_HASH); // [6,7]
+            // compute level 2 hashes
+            final var leftParent = combine(level1A, level1B); // [0..3]
+            final var rightParent = combine(level1C, level1D); // [4..7]
+            // compute the block hash
             final var blockHash = combine(leftParent, rightParent);
+
             final var pendingProof = BlockProof.newBuilder()
                     .block(blockNumber)
                     .previousBlockRootHash(lastBlockHash)
@@ -427,7 +454,8 @@ public boolean endRound(@NonNull final State state, final long roundNum) {
                     blockHash,
                     pendingProof,
                     writer,
-                    new MerkleSiblingHash(false, inputHash),
+                    new MerkleSiblingHash(false, blockStartStateHash),
+                    new MerkleSiblingHash(false, level1B),
                     new MerkleSiblingHash(false, rightParent)));
 
             if (streamToBlockNodes) {
@@ -540,7 +568,7 @@ private synchronized void finishProofWithSignature(
             impliesIndirectProof = true;
         }
         if (blockNumber == Long.MIN_VALUE) {
-            log.info("Ignoring signature on already proven block hash '{}'", blockHash);
+            log.debug("Ignoring signature on already proven block hash '{}'", blockHash);
             return;
         }
         // Write proofs for all pending blocks up to and including the signed block number
@@ -669,7 +697,8 @@ protected boolean onExecute() {
                         TRANSACTION_OUTPUT,
                         STATE_CHANGES,
                         ROUND_HEADER,
-                        BLOCK_HEADER -> {
+                        BLOCK_HEADER,
+                        TRACE_DATA -> {
                     MessageDigest digest = sha384DigestOrThrow();
                     bytes.writeTo(digest);
                     hash = ByteBuffer.wrap(digest.digest());
@@ -695,13 +724,16 @@ class SequentialTask extends AbstractTask {
         protected boolean onExecute() {
             final var kind = item.item().kind();
             switch (kind) {
-                case EVENT_HEADER, EVENT_TRANSACTION, ROUND_HEADER -> inputTreeHasher.addLeaf(hash);
+                case ROUND_HEADER, EVENT_HEADER -> consensusHeaderHasher.addLeaf(hash);
+                case EVENT_TRANSACTION -> inputTreeHasher.addLeaf(hash);
                 case TRANSACTION_RESULT -> {
                     runningHashManager.nextResultHash(hash);
                     hash.rewind();
                     outputTreeHasher.addLeaf(hash);
                 }
-                case TRANSACTION_OUTPUT, STATE_CHANGES, BLOCK_HEADER -> outputTreeHasher.addLeaf(hash);
+                case TRANSACTION_OUTPUT, BLOCK_HEADER -> outputTreeHasher.addLeaf(hash);
+                case STATE_CHANGES -> stateChangesHasher.addLeaf(hash);
+                case TRACE_DATA -> traceDataHasher.addLeaf(hash);
             }
 
             final BlockHeader header = item.blockHeader();
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImplTest.java
index 3f67b19d1aa3..6cf17ec32e0f 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImplTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/BlockStreamManagerImplTest.java
@@ -318,17 +318,23 @@ void startsAndEndsBlockWithSingleRoundPerBlockAsExpected() throws ParseException
                 Bytes.fromHex(
                         "edde6b2beddb2fda438665bbe6df0a639c518e6d5352e7276944b70777d437d28d1b22813ed70f5b8a3a3cbaf08aa9a8"),
                 ZERO_BLOCK_HASH,
-                4,
+                2,
                 List.of(
-                        Bytes.EMPTY,
                         Bytes.EMPTY,
                         Bytes.fromHex(
-                                "dda122623c97d82bfad19873fedd61db98605b434fc8a36a71a7fe99aa017ab45238ceed20a4ae97dbaca7fb728980e3")),
+                                "a02721663224af22a28e784ed41ac694a60b03a7f9eebb644d9766a93b8a39de0a9bdb378b98f4aaa487cc1d9eec660c")),
                 Timestamp.DEFAULT,
                 true,
                 SemanticVersion.DEFAULT,
                 CONSENSUS_THEN,
-                CONSENSUS_THEN);
+                CONSENSUS_THEN,
+                Bytes.fromHex(
+                        "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"),
+                Bytes.fromHex(
+                        "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"),
+                Bytes.fromHex(
+                        "f8fe87ef851b795cd957b8f344aca46e872887b81231fb24d2ab46a6c5cafcf8be7eaa643a89557002ae5a40c5c1d6d8"));
+
         final var actualBlockInfo = infoRef.get();
         assertEquals(expectedBlockInfo, actualBlockInfo);
 
@@ -542,17 +548,22 @@ void alwaysEndsBlockOnFreezeRoundPerBlockAsExpected() throws ParseException {
                 Bytes.fromHex(
                         "edde6b2beddb2fda438665bbe6df0a639c518e6d5352e7276944b70777d437d28d1b22813ed70f5b8a3a3cbaf08aa9a8"),
                 ZERO_BLOCK_HASH,
-                4,
+                2,
                 List.of(
-                        Bytes.EMPTY,
                         Bytes.EMPTY,
                         Bytes.fromHex(
-                                "dda122623c97d82bfad19873fedd61db98605b434fc8a36a71a7fe99aa017ab45238ceed20a4ae97dbaca7fb728980e3")),
+                                "a02721663224af22a28e784ed41ac694a60b03a7f9eebb644d9766a93b8a39de0a9bdb378b98f4aaa487cc1d9eec660c")),
                 Timestamp.DEFAULT,
                 false,
                 SemanticVersion.DEFAULT,
                 CONSENSUS_THEN,
-                CONSENSUS_THEN);
+                CONSENSUS_THEN,
+                Bytes.fromHex(
+                        "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"),
+                Bytes.fromHex(
+                        "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"),
+                Bytes.fromHex(
+                        "f8fe87ef851b795cd957b8f344aca46e872887b81231fb24d2ab46a6c5cafcf8be7eaa643a89557002ae5a40c5c1d6d8"));
         final var actualBlockInfo = infoRef.get();
         assertEquals(expectedBlockInfo, actualBlockInfo);
 
@@ -636,7 +647,7 @@ void supportsMultiplePendingBlocksWithIndirectProofAsExpected() throws ParseExce
         final var aProof = aItem.blockProofOrThrow();
         assertEquals(N_BLOCK_NO, aProof.block());
         assertEquals(FIRST_FAKE_SIGNATURE, aProof.blockSignature());
-        assertEquals(2, aProof.siblingHashes().size());
+        assertEquals(3, aProof.siblingHashes().size());
         // And the proof for N+1 using a direct proof
         final var bProofItem = lastBItem.get();
         assertNotNull(bProofItem);
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/schemas/V0560BlockStreamSchemaTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/schemas/V0560BlockStreamSchemaTest.java
index d2b055b528fe..ea7ce613b205 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/schemas/V0560BlockStreamSchemaTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/schemas/V0560BlockStreamSchemaTest.java
@@ -114,7 +114,10 @@ void assumesMigrationIfNotGenesisAndStateIsNull() {
                 false,
                 SemanticVersion.DEFAULT,
                 blockInfo.consTimeOfLastHandledTxn(),
-                blockInfo.consTimeOfLastHandledTxn());
+                blockInfo.consTimeOfLastHandledTxn(),
+                Bytes.EMPTY,
+                Bytes.EMPTY,
+                Bytes.EMPTY);
         verify(state).put(expectedInfo);
     }
 
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java
index e8514f51460a..26fc595db867 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java
@@ -7,6 +7,7 @@
 import static com.hedera.hapi.node.base.HederaFunctionality.HINTS_PARTIAL_SIGNATURE;
 import static com.hedera.hapi.util.HapiUtils.asInstant;
 import static com.hedera.node.app.blocks.impl.BlockImplUtils.combine;
+import static com.hedera.node.app.blocks.impl.BlockStreamManagerImpl.NULL_HASH;
 import static com.hedera.node.app.hapi.utils.CommonUtils.noThrowSha384HashOf;
 import static com.hedera.node.app.hapi.utils.CommonUtils.sha384DigestOrThrow;
 import static com.hedera.node.app.ids.schemas.V0590EntityIdSchema.ENTITY_COUNTS_KEY;
@@ -318,6 +319,10 @@ public void validateBlocks(@NonNull final List<Block> blocks) {
             }
             final StreamingTreeHasher inputTreeHasher = new NaiveStreamingTreeHasher();
             final StreamingTreeHasher outputTreeHasher = new NaiveStreamingTreeHasher();
+            final StreamingTreeHasher consensusHeaderHasher = new NaiveStreamingTreeHasher();
+            final StreamingTreeHasher stateChangesHasher = new NaiveStreamingTreeHasher();
+            final StreamingTreeHasher traceDataHasher = new NaiveStreamingTreeHasher();
+
             long firstBlockRound = -1;
             long eventNodeId = -1;
             for (final var item : block.items()) {
@@ -326,7 +331,13 @@ public void validateBlocks(@NonNull final List<Block> blocks) {
                 }
                 servicesWritten.clear();
                 if (shouldVerifyProof) {
-                    hashInputOutputTree(item, inputTreeHasher, outputTreeHasher);
+                    hashSubTrees(
+                            item,
+                            inputTreeHasher,
+                            outputTreeHasher,
+                            consensusHeaderHasher,
+                            stateChangesHasher,
+                            traceDataHasher);
                 }
                 if (item.hasStateChanges()) {
                     final var changes = item.stateChangesOrThrow();
@@ -368,8 +379,14 @@ public void validateBlocks(@NonNull final List<Block> blocks) {
                         "Previous block hash mismatch for block " + blockProof.block());
 
                 if (shouldVerifyProof) {
-                    final var expectedBlockHash =
-                            computeBlockHash(startOfStateHash, previousBlockHash, inputTreeHasher, outputTreeHasher);
+                    final var expectedBlockHash = computeBlockHash(
+                            startOfStateHash,
+                            previousBlockHash,
+                            inputTreeHasher,
+                            outputTreeHasher,
+                            consensusHeaderHasher,
+                            stateChangesHasher,
+                            traceDataHasher);
                     blockNumbers.put(
                             expectedBlockHash,
                             block.items().getFirst().blockHeaderOrThrow().number());
@@ -465,17 +482,25 @@ private void assertEntityCountsMatch(final WritableSingletonState<EntityCounts>
         assertEquals(expectedNumTopics.size(), actualCounts.numTopics(), "Topic counts mismatch");
     }
 
-    private void hashInputOutputTree(
+    private void hashSubTrees(
             final BlockItem item,
             final StreamingTreeHasher inputTreeHasher,
-            final StreamingTreeHasher outputTreeHasher) {
+            final StreamingTreeHasher outputTreeHasher,
+            final StreamingTreeHasher consensusHeaderHasher,
+            final StreamingTreeHasher stateChangesHasher,
+            final StreamingTreeHasher traceDataHasher) {
         final var itemSerialized = BlockItem.PROTOBUF.toBytes(item);
         final var digest = sha384DigestOrThrow();
         switch (item.item().kind()) {
-            case EVENT_HEADER, EVENT_TRANSACTION, ROUND_HEADER ->
+            case EVENT_HEADER, ROUND_HEADER ->
+                consensusHeaderHasher.addLeaf(ByteBuffer.wrap(digest.digest(itemSerialized.toByteArray())));
+            case EVENT_TRANSACTION ->
                 inputTreeHasher.addLeaf(ByteBuffer.wrap(digest.digest(itemSerialized.toByteArray())));
-            case TRANSACTION_RESULT, TRANSACTION_OUTPUT, STATE_CHANGES, BLOCK_HEADER ->
+            case TRANSACTION_RESULT, TRANSACTION_OUTPUT, BLOCK_HEADER ->
                 outputTreeHasher.addLeaf(ByteBuffer.wrap(digest.digest(itemSerialized.toByteArray())));
+            case STATE_CHANGES ->
+                stateChangesHasher.addLeaf(ByteBuffer.wrap(digest.digest(itemSerialized.toByteArray())));
+            case TRACE_DATA -> traceDataHasher.addLeaf(ByteBuffer.wrap(digest.digest(itemSerialized.toByteArray())));
             default -> {
                 // Other items are not part of the input/output trees
             }
@@ -486,13 +511,20 @@ private Bytes computeBlockHash(
             final Bytes startOfBlockStateHash,
             final Bytes previousBlockHash,
             final StreamingTreeHasher inputTreeHasher,
-            final StreamingTreeHasher outputTreeHasher) {
+            final StreamingTreeHasher outputTreeHasher,
+            final StreamingTreeHasher consensusHeaderHasher,
+            final StreamingTreeHasher stateChangesHasher,
+            final StreamingTreeHasher traceDataHasher) {
         final var inputTreeHash = inputTreeHasher.rootHash().join();
         final var outputTreeHash = outputTreeHasher.rootHash().join();
-
-        final var leftHash = combine(previousBlockHash, inputTreeHash);
-        final var rightHash = combine(outputTreeHash, startOfBlockStateHash);
-        return combine(leftHash, rightHash);
+        final var consensusHeaderHash = consensusHeaderHasher.rootHash().join();
+        final var stateChangesHash = stateChangesHasher.rootHash().join();
+        final var traceDataHash = traceDataHasher.rootHash().join();
+
+        final var leftParent =
+                combine(combine(previousBlockHash, startOfBlockStateHash), combine(consensusHeaderHash, inputTreeHash));
+        final var rightParent = combine(combine(outputTreeHash, stateChangesHash), combine(traceDataHash, NULL_HASH));
+        return combine(leftParent, rightParent);
     }
 
     private void validateBlockProof(


Reviews:
user: login: tinker-michaelj
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-30T14:08:18Z
user: login: tinker-michaelj
body: Looks perfect to me, tyvm @Neeharika-Sompalli !
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-30T14:10:38Z
user: login: Neeharika-Sompalli
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-30T17:00:11Z
user: login: tinker-michaelj
body: LGTM, tyvm @Neeharika-Sompalli !
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-30T21:53:32Z

Review comments:
diff_hunk: @@ -1247,9 +1254,10 @@ private Bytes startBlockHashFrom(@NonNull final State state) {
                 .stateChanges(new StateChanges(blockStreamInfo.blockEndTime(), List.of(blockStreamInfoChange)))
                 .build();
         // So we can combine this last leaf's has with the size and rightmost hashes
-        // store from the pending output tree to recompute its final root hash
+        // store from the pending state changes tree to recompute its final root hash
         final var penultimateOutputTreeStatus = new StreamingTreeHasher.Status(
path: hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java
user: login: tinker-michaelj
body: _Nit_ - could update this name to `penultimateStateChangesTreeStatus` in later PR.
created_at: 2025-05-30T14:08:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1247,9 +1254,10 @@ private Bytes startBlockHashFrom(@NonNull final State state) {
                 .stateChanges(new StateChanges(blockStreamInfo.blockEndTime(), List.of(blockStreamInfoChange)))
                 .build();
         // So we can combine this last leaf's has with the size and rightmost hashes
-        // store from the pending output tree to recompute its final root hash
+        // store from the pending state changes tree to recompute its final root hash
         final var penultimateOutputTreeStatus = new StreamingTreeHasher.Status(
path: hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java
user: login: Neeharika-Sompalli
body: Changed. Thanks Michael
created_at: 2025-05-30T17:00:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-05-29T00:32:17Z
body: ## [Codecov](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) Report
Attention: Patch coverage is `63.88889%` with `13 lines` in your changes missing coverage. Please review.

| [Files with missing lines](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) | Patch % | Lines |
|---|---|---|
| [...-app/src/main/java/com/hedera/node/app/Hedera.java](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&filepath=hedera-node%2Fhedera-app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fnode%2Fapp%2FHedera.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-aGVkZXJhLW5vZGUvaGVkZXJhLWFwcC9zcmMvbWFpbi9qYXZhL2NvbS9oZWRlcmEvbm9kZS9hcHAvSGVkZXJhLmphdmE=) | 0.00% | [12 Missing :warning: ](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) |
| [...a/node/app/blocks/impl/BlockStreamManagerImpl.java](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&filepath=hedera-node%2Fhedera-app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fnode%2Fapp%2Fblocks%2Fimpl%2FBlockStreamManagerImpl.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-aGVkZXJhLW5vZGUvaGVkZXJhLWFwcC9zcmMvbWFpbi9qYXZhL2NvbS9oZWRlcmEvbm9kZS9hcHAvYmxvY2tzL2ltcGwvQmxvY2tTdHJlYW1NYW5hZ2VySW1wbC5qYXZh) | 95.83% | [1 Missing :warning: ](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) |

[![Impacted file tree graph](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429/graphs/tree.svg?width=650&height=150&src=pr&token=oCfwLn4U3e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger)](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger)

```diff
@@             Coverage Diff              @@
##               main   #19429      +/-   ##
============================================
+ Coverage     69.39%   69.40%   +0.01%     
- Complexity    23321    23326       +5     
============================================
  Files          2646     2646              
  Lines        100886   100904      +18     
  Branches      10442    10442              
============================================
+ Hits          70005    70030      +25     
+ Misses        26915    26906       -9     
- Partials       3966     3968       +2     
```

| [Files with missing lines](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [...a/node/app/blocks/impl/BlockStreamManagerImpl.java](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&filepath=hedera-node%2Fhedera-app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fnode%2Fapp%2Fblocks%2Fimpl%2FBlockStreamManagerImpl.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-aGVkZXJhLW5vZGUvaGVkZXJhLWFwcC9zcmMvbWFpbi9qYXZhL2NvbS9oZWRlcmEvbm9kZS9hcHAvYmxvY2tzL2ltcGwvQmxvY2tTdHJlYW1NYW5hZ2VySW1wbC5qYXZh) | `76.57% <95.83%> (+0.34%)` | `51.00 <0.00> (ø)` | |
| [...-app/src/main/java/com/hedera/node/app/Hedera.java](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&filepath=hedera-node%2Fhedera-app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhedera%2Fnode%2Fapp%2FHedera.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger#diff-aGVkZXJhLW5vZGUvaGVkZXJhLWFwcC9zcmMvbWFpbi9qYXZhL2NvbS9oZWRlcmEvbm9kZS9hcHAvSGVkZXJhLmphdmE=) | `51.95% <0.00%> (-0.46%)` | `54.00 <0.00> (ø)` | |

... and [9 files with indirect coverage changes](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger)

[![Impacted file tree graph](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429/graphs/tree.svg?width=650&height=150&src=pr&token=oCfwLn4U3e&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger)](https://app.codecov.io/gh/hiero-ledger/hiero-consensus-node/pull/19429?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hiero-ledger)

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: codacy-production[bot]
created_at: 2025-05-29T00:32:20Z
body: ## Coverage summary from Codacy
### [See diff coverage on Codacy](https://app.codacy.com/gh/hiero-ledger/hiero-consensus-node/pull-requests/19429/coverage?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummaryPullRequest)

 | Coverage variation | Diff coverage | 
 | ------------- | ------------- |
 | :white_check_mark: **+0.01%** (target: -1.00%) | :white_check_mark: **63.89%** |

<details>
<summary>Coverage variation details</summary>

| | Coverable lines | Covered lines | Coverage |
| ------------- | ------------- | ------------- | ------------- |
| Common ancestor commit (6b2c2d821b51cccb2445c12e00882cda9448fe77) | 100791 | 73924 | 73.34% | |
| Head commit (42a7233e16b45a1d4e4849d4e37f56677904f9ef) | 100809 (+18) | 73951 (+27) | 73.36% (**+0.01%**) |

**Coverage variation** is the difference between the coverage for the head and common ancestor commits of the pull request branch: `<coverage of head commit> - <coverage of common ancestor commit>`
</details>

<details>
<summary>Diff coverage details</summary>

| | Coverable lines | Covered lines | Diff coverage |
| ------------- | ------------- | ------------- | ------------- |
| Pull request (#19429) | 36 | 23 | **63.89%** |

**Diff coverage** is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: `<covered lines added or modified>/<coverable lines added or modified> * 100%`
</details>

#### [See your quality gate settings](https://app.codacy.com/gh/hiero-ledger/hiero-consensus-node/settings/quality?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummaryGates)&nbsp;&nbsp;&nbsp;&nbsp;[Change summary preferences](https://app.codacy.com/gh/hiero-ledger/hiero-consensus-node/settings/integrations?utm_source=github.com&utm_medium=coverageSummary&utm_campaign=coverageSummarySettings)

<!-- 108f945d-e553-4918-8ccf-027c6d4acc2c -->
author_association: NONE