# Cross-Ledger Escrow — open → claim/refund (HTLC) and open → complete (PvP)

← [10 Settlement Platforms](10-settlement-platforms.md) | [Index](README.md)

§15a Cross-Ledger Escrow protocol. Two modes:
- **HTLC** (atomicity_mode=0): classic atomic swap with hashlock + timelock. Buyer locks risk bond; operator reveals preimage when CBDC leg settles.
- **PvP** (atomicity_mode=1): mBridge native Payment-vs-Payment between two §48.16-admitted platforms. Operator submits Multi-CB attestation; no preimage required.

---

## Prerequisites

- Topic 01 (onboarding — you need a registered agent)
- Topic 09 (settlement venues — you need a `settlement_venue_id`)
- For PvP: Topic 10 (settlement platforms — you need a `platform_id`)
- A funded COSR balance to cover the risk bond

---

## HTLC lifecycle

```
Buyer                         Operator                      Bridge
  │                               │                            │
  │── open_cross_ledger_htlc ────►│                            │
  │   (hashlock, timelock)         │                            │
  │◄─ {escrow_pda_hex, ...} ──────│                            │
  │                               │                            │
  │                         CBDC leg settled on venue          │
  │                               │                            │
  │                    ── claim_cross_ledger_htlc ────────────►│
  │                         (preimage reveals hashlock)        │
  │                    ◄─ {escrow_state: settled} ─────────────│
  │◄─ risk_bond returned                                       │
  │   (minus fee)                                              │
  │                                                            │
  │   ... OR if timelock expires ...                           │
  │── refund_cross_ledger_htlc ─────────────────────────────►  │
  │◄─ {escrow_state: refunded, risk_bond_refunded} ──────────  │
```

---

## Step 1 — Open a HTLC escrow

The buyer opens the escrow. The `hashlock_hex` is `sha256(preimage)` where the preimage is known only to the operator.

```json
{
  "tool": "thread.open_cross_ledger_htlc",
  "params": {
    "secret_key_hex": "<buyer 32-byte seed hex>",
    "cross_escrow_id_hex": "<random 32-byte hex — must be globally unique>",
    "acceptance_ref_hex": "<32-byte acceptance reference>",
    "seller_id_hex": "<32-byte seller agent_id>",
    "settlement_venue_id": 1,
    "principal_amount_native": 100000,
    "risk_bond_micro_cosr": 10000000,
    "operator_agent_id_hex": "<32-byte operator agent_id>",
    "hashlock_hex": "<sha256 of preimage — 32 bytes hex>",
    "timelock_slots": 21600,
    "created_slot": 12345678,
    "fx_rate_attestation_ref_hex": "<32-byte §54.7 attestation ref>"
  }
}
```

**Response:**

```json
{
  "cross_escrow_id_hex": "abc123...",
  "escrow_pda_hex": "def456...",
  "vault_pda_hex": "789abc...",
  "buyer_agent_id_hex": "...",
  "expires_slot": "12367278",
  "escrow_state": "opened",
  "atomicity_mode": 0
}
```

Fields to save: `cross_escrow_id_hex`, `expires_slot`.

---

## Step 2a — Claim (operator, happy path)

After the CBDC leg settles, the operator claims with the preimage:

```json
{
  "tool": "thread.claim_cross_ledger_htlc",
  "params": {
    "secret_key_hex": "<operator 32-byte seed hex>",
    "cross_escrow_id_hex": "<same as step 1>",
    "preimage_hex": "<32-byte preimage that hashes to hashlock>"
  }
}
```

**Response:**

```json
{
  "cross_escrow_id_hex": "abc123...",
  "escrow_state": "settled",
  "settlement_attestation_hint": "Publish a DOC_SETTLEMENT_ATTESTATION (0x54485253) referencing ..."
}
```

After claiming, publish a `DOC_SETTLEMENT_ATTESTATION` (`0x54485253`) with both operator and central-bank co-signatures.

---

## Step 2b — Refund (buyer, timeout path)

If the timelock expires without a claim:

```json
{
  "tool": "thread.refund_cross_ledger_htlc",
  "params": {
    "secret_key_hex": "<buyer 32-byte seed hex>",
    "cross_escrow_id_hex": "<same as step 1>"
  }
}
```

**Response:**

```json
{
  "cross_escrow_id_hex": "abc123...",
  "escrow_state": "refunded",
  "risk_bond_micro_cosr_refunded": "10000000"
}
```

Only valid when `current_slot >= expires_slot`. The bridge handles operator stake slash (`BRIDGE_ATOMICITY_FAILED_SLASH_BPS`) off-chain.

---

## PvP lifecycle (mBridge native)

```
Buyer                         Operator + CBs               Bridge
  │                               │                           │
  │── open_cross_ledger_atomic_swap ────────────────────────►│
  │   (platform_id, both venue_ids)                           │
  │◄─ {escrow_pda_hex, atomicity_mode: 1, ...} ─────────────│
  │                               │                           │
  │              mBridge PvP executes (both legs atomically)  │
  │                               │                           │
  │            ── complete_cross_ledger_atomic_swap(success) ►│
  │◄─ {escrow_state: pvp_complete, risk_bond_released} ──────│
  │                                                           │
  │   ... OR on mBridge atomicity failure ...                 │
  │            ── complete_cross_ledger_atomic_swap(failure) ►│
  │◄─ {escrow_state: pvp_failed, risk_bond_released} ────────│
```

---

## Step A — Open a PvP escrow

```json
{
  "tool": "thread.open_cross_ledger_atomic_swap",
  "params": {
    "secret_key_hex": "<buyer seed>",
    "cross_escrow_id_hex": "<random 32-byte hex>",
    "acceptance_ref_hex": "<32-byte ref>",
    "seller_id_hex": "<32-byte seller agent_id>",
    "settlement_venue_id": 1,
    "second_venue_id": 2,
    "principal_amount_native": 100000,
    "counterparty_principal_amount_native": 36725,
    "risk_bond_micro_cosr": 10000000,
    "operator_agent_id_hex": "<operator with MBRIDGE_PARTICIPANT 0x8000>",
    "timelock_slots": 7200,
    "created_slot": 12345678,
    "platform_id": 1,
    "fx_rate_attestation_ref_hex": "<DOC_CROSS_CBDC_RATE_ATTESTATION ref — 0x54485255>"
  }
}
```

**Response:**

```json
{
  "cross_escrow_id_hex": "...",
  "escrow_pda_hex": "...",
  "buyer_agent_id_hex": "...",
  "expires_slot": "12352878",
  "escrow_state": "opened",
  "atomicity_mode": 1,
  "platform_id": "1",
  "second_venue_id": "2"
}
```

---

## Step B — Complete PvP (operator)

```json
{
  "tool": "thread.complete_cross_ledger_atomic_swap",
  "params": {
    "secret_key_hex": "<operator seed>",
    "cross_escrow_id_hex": "<same as step A>",
    "success": true,
    "attestation_ref_hex": "<DOC_SETTLEMENT_ATTESTATION 0x54485253 with attestation_mode=1>"
  }
}
```

Set `success: false` if mBridge reports atomicity failure (both legs natively reverted).

---

## Error catalog

| Error string | Meaning | Fix |
|---|---|---|
| `cross_escrow_id_duplicate` | cross_escrow_id already used | Generate a new random 32-byte id |
| `htlc_preimage_mismatch` | sha256(preimage) ≠ hashlock | Use the correct preimage |
| `htlc_expired` | claim attempted after expires_slot | Use refund_cross_ledger_htlc instead |
| `not_yet_expired` | refund attempted before expires_slot | Wait until timelock expires |
| `bridge_not_authorized` | secret_key_hex ≠ operator_agent_id | Use the operator key |
| `unauthorized` | secret_key_hex ≠ buyer_agent_id | Use the buyer key |
| `invalid_state` | escrow not in opened state | Check escrow state before calling |
| `wrong_atomicity_mode` | called HTLC claim on PvP or vice versa | Use the correct tool for the mode |
| `pvp_outer_timeout` | PvP completion attempted after expires_slot | Use refund_cross_ledger_htlc instead |
| `escrow_not_found` | cross_escrow_id does not exist | Verify the cross_escrow_id_hex |

---

## Mode selection guide

| Use HTLC (atomicity_mode=0) when... | Use PvP (atomicity_mode=1) when... |
|---|---|
| One CBDC ledger, one counterparty | Two CBDC ledgers on the same mBridge platform |
| Operator holds the preimage secret | Operator has MBRIDGE_PARTICIPANT (0x8000) service bit |
| Timelock-based atomicity is acceptable | Platform-level atomic finality is required |
| No §48.16 platform registration needed | Both venues admitted to the same platform_id |

---

## Document tags (§15a)

| Tag | Constant | Document |
|---|---|---|
| `0x54485252` | `DOC_CROSS_LEDGER_ESCROW` | On-chain CBOR state record |
| `0x54485253` | `DOC_SETTLEMENT_ATTESTATION` | Operator + CB co-signed settlement |
| `0x54485254` | `DOC_SETTLEMENT_PLATFORM_ATTESTATION` | mBridge platform health attestation |
| `0x54485255` | `DOC_CROSS_CBDC_RATE_ATTESTATION` | Cross-CBDC FX rate attestation |

---

## Working examples

End-to-end probes covering all lifecycle paths:

- `platform/scripts/smoke-cross-ledger-escrow-basic.ts` — manifest check + open + error path coverage
- `platform/scripts/smoke-cross-ledger-escrow-e2e.ts` — full HTLC + PvP lifecycle probes (Probes A–E)

Run against a running topology:

```sh
THREAD_BRIDGE_URL=http://127.0.0.1:8443 npx tsx scripts/smoke-cross-ledger-escrow-e2e.ts
```

---

← [09 Settlement Venues](09-settlement-venues.md) | [10 Settlement Platforms](10-settlement-platforms.md) | [Index](README.md)
