cardano-foundation / cardano-foundation/cardano-rosetta-java

add collateral inputs and collateral returns for trasnsacations

Open
#781 0 comments 0 reactions 0 assignees View on GitHub
cip-113
Dominant language
Java
Stars
26
Forks
15
Avg merge
5d 3h
Merged PRs (30d)
2

Description

### Background

`collateralInput` and `collateralReturn` operations are not emitted today. `BlockTx` has no collateral fields, so the information never reaches `OperationMapperService`. This issue adds them, and absorbs the generic Plutus collateral rule originally tracked in #603.

This is a balance correctness issue, not a display one. Rosetta reconciles per account: the sum of operations with a successful status must equal that account's balance change. `/network/options` declares `invalid` as `successful=false` and an empty `balance_exemptions`, so reconciliation is strict.

For a failed (phase-2) transaction today the regular operations are excluded by their `invalid` status and no collateral operations are emitted, so the computed balance change is 0 while the real change is `-total_collateral`. `rosetta-cli check:data` with reconciliation enabled would report a failure. This is pre-existing for every failed Plutus transaction; CIP-113 makes it more likely to be hit, because a transfer rejected by `transfer_logic_script` fails phase-2 validation by design. See also #602.

### Expected Behavior

Emit `collateralInput` and `collateralReturn` for any transaction that declares collateral, using the on-chain values as recorded. **Operation status alone distinguishes whether the collateral was actually consumed:**

| Transaction | Regular operations | Collateral operations | Counted toward balance |
|---|---|---|---|
| Successful | `success` | `invalid` | regular only — collateral was not consumed |
| Failed (phase-2) | `invalid` | `success` | collateral only — regular operations did not apply |

Equivalently: **collateral operations carry the inverse of the transaction status.** Collateral applies exactly when the regular operations do not, so one rule covers both cases.

Half of this already works. `OperationMapperService` computes `txStatus = source.isInvalid() ? invalid : success` and applies it to every operation, so regular operations on a failed transaction already carry `status: "invalid"` today. The new work is emitting the collateral operations and giving them the inverse status.

This reconciles correctly in both directions:

- **Successful tx** — collateral is declared but never consumed, so the collateral operations are excluded and the computed change equals the regular operations. Correct.
- **Failed tx** — the regular operations are excluded and the collateral operations net to `collateralInput + collateralReturn = -total_collateral`, which is exactly the real balance change. Correct.

Because status carries the distinction, the reported amounts are the **actual declared on-chain values** — no synthesized balance-neutral pair is needed, and no value is reported that is not present in the transaction.

```json
// Successful transaction — declared, not consumed
{ "operation_identifier": { "index": 8 }, "type": "collateralInput", "status": "invalid",
"account": { "address": "addr_test1qz4r62d...qg6cd2w" },
"amount": { "value": "-54513166", "currency": { "symbol": "ADA", "decimals": 6 } } }

{ "operation_identifier": { "index": 9 }, "type": "collateralReturn", "status": "invalid",
"account": { "address": "addr_test1qz4r62d...qg6cd2w" },
"amount": { "value": "53669527", "currency": { "symbol": "ADA", "decimals": 6 } } }
```

On a failed transaction the same two operations carry `status: "success"` and the same amounts, netting `-843,639` for the sample transaction below.

**This needs no CIP-113 detection.** Status depends only on whether the transaction succeeded, so the rule is uniform across CIP-113 and generic Plutus transactions and requires no `programmableLogicBase` configuration. This is a deliberate simplification of the two-rule model described in EPIC-4, which distinguished CIP-113 from generic Plutus and omitted collateral entirely on successful generic transactions. The trade-off is that collateral operations now appear on every Plutus transaction that declares them; marked `invalid` they are excluded from balance and carry no reconciliation risk, but they are additional operations in the response. If that is unwanted, the alternative is to omit them on success rather than marking them `invalid`, which reconciles equally well but loses the record that collateral was pledged.

Operation status does not affect the `parse(payloads(ops)) == ops` round-trip: `/construction/parse` sets status to `""` (`ParseConstructionUtil:229,277`), so only operation presence, type, account and amount are compared.

**Implementation note.** No migration or indexer change is required. The `transaction` table already has `collateral_inputs`, `collateral_return`, `total_collateral` and `reference_inputs`, and jOOQ already maps all of them. What is missing is selecting the columns, adding the fields to `BlockTx` (which has none today), adding the `OperationType` enum values, and the mapper logic. Because `OperationMapperService` is shared, this applies to `/block`, `/block/transaction` and `/search/transactions` at once.

**Depends on** # and #, so the phase-1 behavior they cover is provably unaffected by this change.

### Acceptance Criteria

- [ ] `collateralInput` and `collateralReturn` operations are emitted for transactions declaring collateral
- [ ] On a successful transaction both carry `status: "invalid"` and the declared on-chain amounts
- [ ] On a failed transaction both carry `status: "success"` and regular operations carry `status: "invalid"`
- [ ] Summed successful operations reconcile against `/account/balance` for successful and failed transactions
- [ ] Collateral operations on a failed transaction sum to `-total_collateral`
- [ ] `collateralInput` and `collateralReturn` are listed in the `/network/options` `operation_types` response
- [ ] A transaction declaring no collateral produces no collateral operations
- [ ] Non-Plutus transactions unaffected (regression check)

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by tracing transaction column selection into BlockTx, then inspect OperationMapperService and the OperationType enum. Check the /block, /block/transaction, /search/transactions, and /network/options entry points, and use rosetta-cli check:data with reconciliation enabled. Done means collateral operations and statuses match the acceptance criteria without changing non-Plutus behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend-api-design, blockchain
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.