hiero-ledger / hiero-ledger/hiero-consensus-node

Inner transactions of an atomic batch bypass node due-diligence charging

Open
#26,615 1 comment 0 reactions 1 assignee Claimed by @ruslanvelkov-beep View on GitHub
Dominant language
Java
Stars
406
Forks
226
Avg merge
3d 4h
Merged PRs (30d)
210

Description

**Description**

When a transaction fails a node due-diligence check at pre-handle, the handle workflow charges the submitting node the network fee — the node should have rejected the transaction at ingest, so the cost of letting it reach consensus falls on the node. This charging is silently skipped when the failing transaction is an inner transaction of an atomic batch.

**Current behavior**

For a batch inner transaction whose pre-handle result is `NODE_DUE_DILIGENCE_FAILURE`:

1. `PreHandleWorkflow#preHandleAllTransactions` pre-handles each inner transaction and appends its result to the outer result's `innerResults`; the outer batch itself stays `SO_FAR_SO_GOOD`, so nothing at the batch level reflects the inner failure.
2. At handle, the inner transaction is dispatched with category `BATCH_INNER`. `DispatchValidator#creatorErrorIfKnown` correctly maps the inner's `NODE_DUE_DILIGENCE_FAILURE` to a creator error, but `DispatchProcessor#chargeCreator` sets the inner's status and then returns without charging for `BATCH_INNER` dispatches.
3. The failed inner causes `AtomicBatchHandler` to fail the batch with `INNER_TRANSACTION_FAILED`, and the **batch payer** is charged the batch fees.

Net effect: the network is still compensated (the batch payer pays), but the charge lands on the batch payer instead of the submitting node. The same operation submitted top-level charges the node — wrapping it in a batch inverts who pays.

**Design considerations**

A blanket "charge the node for any inner due-diligence failure" would be wrong. Inner due-diligence failures fall into two classes that today share one result status:

- **Ingest-decidable** — determinable from the payer and transaction body alone (structural pure-checks failures, privilege checks). An honest node rejects these at ingest, so reaching consensus is genuinely the node's fault and the node should be charged.
- **State-dependent** — e.g. an inner payer account deleted or removed between submission and consensus (`PAYER_ACCOUNT_DELETED` / `PAYER_ACCOUNT_NOT_FOUND` in `PreHandleWorkflowImpl#preHandleTransaction`). The node could not have known at ingest; charging it would be wrong.

The fix needs to distinguish these classes across **all** inner due-diligence paths, and decide the charging shape for the ingest-decidable case (charge the node the inner's network fee, or treat the whole batch as a node due-diligence failure and spare the batch payer).

**Acceptance criteria**

- Ingest-decidable inner due-diligence failures charge the submitting node, consistent with top-level behavior.
- State-dependent inner failures continue to not charge the node.
- Batch resolution (`INNER_TRANSACTION_FAILED`) and inner record statuses remain externally consistent.

**References**

- `PreHandleWorkflow#preHandleAllTransactions` — inner results appended, outer stays `SO_FAR_SO_GOOD`
- `DispatchValidator#creatorErrorIfKnown` — due-diligence failure → creator error
- `DispatchProcessor#chargeCreator` — `BATCH_INNER` early return
- `AtomicBatchHandler` — batch failure with `INNER_TRANSACTION_FAILED`

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.