From af4cfd7f972080c7875fa3f62b19ecb2780d67a9 Mon Sep 17 00:00:00 2001 From: Kayos Date: Sat, 9 May 2026 18:05:56 -0700 Subject: [PATCH] audits: 2026-05-09 escrow E2E preprod results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5 of 6 builders proven on chain on preprod_test2 against the post-audit validator (a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d). Veto path (4 builders): - open 0972963e8eb46597b37dfa7e1fa15c97fea4e39d55b29c54f723ba1b155bc7cb - deposit 0519cb7dbb8c2c1632db5baee90d5440d2905c151f910a085c78d9050c2d6175 ← first Plutus V3 spend on chain - agree f6664079d96c453e1a6333c33c65744984953c474e57cdb331e78ad3e5429cc7 - veto 14a4be9f233ab02e518356e5963d30517986984f6fd3520151c924057db9c661 ← HIGH-2 fix proven Refund-timeout path (5th builder): - open(short) 0669ef61b8695bedcb4eb4c38fda2bd66c68c9384b57839329a01a745db75305 - refund 41590ac6ed069586e650da58858436cfe6be51a865069a7a4b40f795dfcdbff9 ← strict-> time gate proven, MED-2/3 fix held Settle deferred — needs 30-min lock_period_ms wait. Builder + validator branch unit-tested (5/5 tests pass), on-chain validation in next session. Both HIGH validator fixes from the 2026-05-09 internal audit confirmed running on chain. Multi-party signing flow + slot↔ms boundary math both held up under real preprod chain race conditions. --- audits/2026-05-09-escrow-e2e.md | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 audits/2026-05-09-escrow-e2e.md diff --git a/audits/2026-05-09-escrow-e2e.md b/audits/2026-05-09-escrow-e2e.md new file mode 100644 index 0000000..28ea38e --- /dev/null +++ b/audits/2026-05-09-escrow-e2e.md @@ -0,0 +1,92 @@ +# Escrow v1 — preprod E2E results, 2026-05-09 + +End-to-end execution of the escrow_wip surface on `preprod_test2`, +post-internal-audit (validator hash +`a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d`). + +## Setup + +- **Image:** `lucy-registry:5000/aldabra/mcp:escrow-wip-v1` +- **Validator script address:** `addr_test1wz5qsxkw7f5ntkd4739eypfp0rshxqdk6mngpry3ck6k7hgcgah4g` +- **Validator path inside container:** `/etc/aldabra/escrow/validator.cbor.hex` (8414 hex chars) +- **party_a (preprod wallet):** `4cd61bd67ed72c1cec160bf7de6103c6bddb397da6a500dc4ff805f8` (`addr_test1qpxdvx7k...`) +- **party_b / recipient (bob, fresh test wallet):** `fc7533eb154263a154fb40f1acc4fc40e847cac98fe45da4e948c4f6` (`addr_test1qr782vltz...`) + +## Veto path (4 of 6 builders) + +| Step | tx hash | What it proves | +|------|---------|----------------| +| Open | `0972963e8eb46597b37dfa7e1fa15c97fea4e39d55b29c54f723ba1b155bc7cb` | escrow_open builder; deposits=[(preprod, 5 ADA)] at script | +| Deposit (bob) | `0519cb7dbb8c2c1632db5baee90d5440d2905c151f910a085c78d9050c2d6175` | **first Plutus V3 spend on chain.** Validator's Deposit branch ran — including the new HIGH-1 `value_geq_value(new_value, in_value)` check. Continuing-output deposits-list update verified via `cbor.serialise` byte-equality. | +| Agree | `f6664079d96c453e1a6333c33c65744984953c474e57cdb331e78ad3e5429cc7` | both-party multi-sig (driver=preprod, co-signer=bob). State Open→Agreed{at=upper}. validity_upper ≤ open_deadline_ms enforced. | +| Veto | `14a4be9f233ab02e518356e5963d30517986984f6fd3520151c924057db9c661` | HIGH-2 fix active: `value_eq(deposits_to_value(d.deposits), in_value)`. Multi-output refund (5 ADA each to enterprise addresses of party_a + party_b). | + +Total tADA cycled through validator: 10 ADA (5 from each party). Refunded to enterprise addresses of both contributors. + +## Refund-timeout path (5th builder) + +| Step | tx hash | What it proves | +|------|---------|----------------| +| Open (short deadline) | `0669ef61b8695bedcb4eb4c38fda2bd66c68c9384b57839329a01a745db75305` | escrow_open with `open_deadline = tip_ms + 5 min`, initial_lovelace=2 ADA | +| (wait) | — | tip elapsed past open_deadline (~165s past) before next step | +| Refund-timeout | `41590ac6ed069586e650da58858436cfe6be51a865069a7a4b40f795dfcdbff9` | escrow_refund_timeout builder; `lower > open_deadline_ms` strict-`>` time gate proven; HIGH-2 deposits-sum invariant proven on a single-deposit escrow; single-output refund (2 ADA back to party_a's enterprise address). | + +## Settle path (6th builder, NOT YET TESTED) + +Deferred — requires a 30-minute `lock_period_ms` to elapse between Agree +and Settle. Setup: open + 2 deposits + Agree + wait 30 min + Settle. +Run as a separate session when the window is convenient. + +The Settle builder + validator branch are unit-tested in +`crates/aldabra-dao/src/builder/escrow_settle.rs` (5 tests covering +not-Agreed reject, lock-not-elapsed reject, empty-escrow reject, +happy-path full payout, anyone-can-drive). On-chain validation is the +final gap. + +## What this E2E proved on chain + +- ✅ Plutus V3 validator deploys at the bech32-derived script address + per validator hash + testnet header byte. +- ✅ Inline V3 validator CBOR (~8 KB) bakes into the runtime image at + `/etc/aldabra/escrow/validator.cbor.hex` and the MCP `validator_script_path` + arg correctly resolves it. +- ✅ Conway-era inline-datum + script-output min-utxo (2 ADA floor) + matches MED-5 fix expectations. +- ✅ HIGH-1 fix (`value_geq_value(new_value, in_value)` on Deposit) + ran on chain — validator accepted a clean deposit, no token drain + attempt was made (v1 ADA-only). +- ✅ HIGH-2 fix (`value_eq(deposits_to_value(d.deposits), in_value)` + on Veto and Refund) ran on chain — the new helper functions + `deposits_to_value` and `value_eq` execute within the per-tx UPLC + budget for a 1-deposit and a 2-deposit escrow. +- ✅ Multi-party signing flow works end-to-end: driver signs partial + → co-signer signs partial → submit. wallet_sign_partial appends a + fresh VKeyWitness without disturbing existing ones. +- ✅ Slot↔ms math at the strict-`>` validator boundary (Refund + branch) didn't burn fees — the MED-2/3 fix to derive + `validity_lower_ms` from `slot_to_posix_ms(network, slot)` instead + of Koios `block_time*1000` held up under a real preprod chain race. + +## v2 / future TODO (still open) + +- Settle path on-chain validation. +- External third-party audit (TxPipe / Anastasia Labs / MLabs / Tweag). +- Reference-script deployment for the validator (~8 KB inline per + spend tx is non-trivial; ref-script optimization halves tx size). +- `aiken/fuzz` property tests in `escrow.ak` covering each redeemer + branch's invariant violations. +- Multi-asset escrows (the v2 frontier — needs the foldr-canonicality + golden test for `Pairs<>` serialisation, plus widening the spend + builders to take `escrow_in_assets` and emit non-empty asset entries + on every output). +- Read-side MCP tools (`escrow_show`, `escrow_list`). + +## Wallet movement summary + +party_a (preprod): pre-flow ~9844 tADA → post-flow ≈ 9844 - 100 (sent to bob) - ~5 (fees + topups) ≈ ~9739 tADA. +party_b (bob): pre-flow 0 → post-funding 100 → minus ~5 ADA fee/lockup → ≈ 95 tADA at end of flow. + +Refunds landed at enterprise (null-stake) addresses, NOT the base +addresses of the wallets — this is by validator design (`pkh_to_base_address` +hard-codes `stake_credential: None`). The funds are spendable via the +same payment key; just a different bech32 prefix.