> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sundialprotocol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fraud proofs and ledger rules

> How an invalid Sundial L2 block is challenged: the 21 ledger rules, the fraud proof catalogue, multi-step computation threads, and a worked double-spend example.

A fraud proof is a demonstration, verified on L1, that a committed block breaks a ledger rule. A successful proof removes the block from the state queue and slashes its operator. Anyone can submit one.

## How a proof is verified

Verifying a rule violation can be too large for one L1 transaction. The proof protocol splits it into steps.

* **The fraud proof catalogue** lists the categories of proof. It is fixed when the L2 instance is initialized and can't be changed afterwards.
* **A computation thread** is a chain of spending validators, one per step. Each step reads the computation state from its datum, advances it, and passes the new state to the next step.
* **A fraud proof token** is minted when the last step succeeds. It is the permanent record of the proof, and it is what the state queue requires in order to remove the block.

The watcher that starts a thread signs the first transaction and is recorded as the fraud prover. Only that party can advance or cancel the thread. Cancelling returns the ADA locked in it.

### The reference catalogue

The reference instance defines four categories. The validators are in [`onchain/aiken/validators/fraud-proofs/`](https://github.com/sundial-protocol/sundial-monorepo/tree/main/onchain/aiken/validators/fraud-proofs).

| Index | Category | Violation proven | Steps |
| :-: | - | - | :-: |
| 0 | `double_spend` | Two transactions in the block spend the same input | 3 |
| 1 | `non_existent_input` | A transaction spends an input that isn't in the previous UTXO set | 2 |
| 2 | `non_existent_input_no_index` | A transaction spends an output index that the referenced transaction doesn't have | 4 |
| 3 | `invalid_range` | A transaction's validity interval doesn't overlap its block's event interval | 2 |

The whitepaper gives a fraud proof construction for every violation below. The catalogue has room for 2<sup>32</sup> categories.

## Worked example: a double spend

Alice's output holds 10 ADA. Operator Bob commits a block in which two transactions both spend it. Watcher Charley reads the block from the data availability layer and notices.

Charley proves it in six L1 transactions:

**1. Init**

Charley references the catalogue and the block's state queue node, and mints a thread token named after the category and the block's header hash. Signing this records him as the fraud prover.

**2. Step 1, first run**

He presents the first transaction with a Merkle proof that it is in the block's `transactions_root`. The step records the root of that transaction's input set.

**3. Step 1, second run**

He presents the second transaction with its proof. The step requires that the two transaction IDs differ.

**4. Step 2**

He names Alice's output and proves it is in the first transaction's input set.

**5. Step 3**

He proves the same output is in the second transaction's input set. The step mints the fraud proof token and burns the thread token.

**6. Removal**

He uses the fraud proof token to remove the block from the state queue. Bob's bond is slashed, and Charley receives the fraud prover reward.

If later blocks were appended after the fraudulent one, they are removed first, one transaction each.

## Ledger rules

Every block must satisfy 21 ledger rules. Rules 1 to 16 follow the L1's ledger rules. Rules 17 to 21 enforce the restrictions of the [L2 transaction type](https://docs.sundialprotocol.com/l2/ledger.md#how-the-transaction-type-differs-from-cardanos).

| # | Rule | Violations |
| :-: | - | - |
| 1 | All inputs must be valid | `NO-INPUT`, `INPUT-NO-IDX`, `WITHDRAWN-INPUT`, `DOUBLE-SPEND`, `DOUBLE-WITHDRAW` |
| 2 | Transaction validity range | `INVALID-RANGE` |
| 3 | At least one input | `ZERO-INPUT` |
| 4 | Minimum fee | `MIN-FEE` |
| 5 | Required signatures are correct | `MISSING-REQ-SIGNER-TX`, `MISSING-REQ-SIGNER-UTXO`, `NON-REQ-SIGNER` |
| 6 | Signatures are valid | `INVALID-SIGNATURE` |
| 7 | Every needed signature is provided | `MISSING-SIGNATURE` |
| 8 | Native scripts are available | `MISSING-NATIVE-SCRIPT` |
| 9 | Native scripts validated | `NATIVE-SCRIPT-INVALID` |
| 10 | Value preservation | `VALUE-NOT-PRESERVED` |
| 11 | No ADA minted | `ADA-MINTED` |
| 12 | No negative value | `NEGATIVE-OUTPUT-VALUE` |
| 13 | Minimum UTXO value | `MIN-ADA-TX`, `MIN-ADA-UTXO` |
| 14 | Network ID of outputs | `OUTPUT-NETWORK-TX`, `OUTPUT-NETWORK-UTXO` |
| 15 | Network ID of transaction | `TRANSACTION-NETWORK` |
| 16 | All reference inputs must be valid | `NO-REFERENCE-INPUT`, `REFERENCE-INPUT-NO-IDX` |
| 17 | Transaction hash integrity | `TX-HASH-MISMATCH` |
| 18 | Field admissibility | `INADMISSIBLE-FIELD` |
| 19 | No duplicate inputs | `DUPLICATE-INPUT` |
| 20 | Validity flag | `INVALID-FLAG` |
| 21 | No auxiliary data | `AUX-DATA-PRESENT` |

That is 30 violations.

### Data availability rules

One further rule binds published block data to the header: each root in a header must be the trie root of the corresponding set in the block body. It has four violations, one per set:

`UTXOS-ROOT-MISMATCH`, `TRANSACTIONS-ROOT-MISMATCH`, `DEPOSITS-ROOT-MISMATCH`, `WITHDRAWALS-ROOT-MISMATCH`.

See [Data availability](https://docs.sundialprotocol.com/l2/data-availability.md).

### Block-level conditions

Three conditions concern a block as a whole:

1. **Event inclusion.** The block's deposits, transaction orders and withdrawal orders are exactly the L1 events whose inclusion time lies in its event interval, and each corresponds to a real L1 event.
2. **Event application order.** Withdrawals, then transaction orders, then transaction requests, then deposits.
3. **Event validity tags.** Each withdrawal order and transaction order carries the validity tag that its content determines.

## How the node applies the rules

A node doesn't wait for fraud proofs to keep its own blocks valid. Its rule engine checks every transaction at admission, in two phases: stateless checks first, then checks against the ledger state. A rejected transaction gets one of 21 reject codes. See [Reject codes](https://docs.sundialprotocol.com/reference/reject-codes.md).

## Running a watcher

[Run a watcher](https://docs.sundialprotocol.com/guides/run-a-watcher.md) covers what a watcher needs to do in practice.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.