aldabra/aiken-escrow
2026-06-28 11:39:05 -07:00
..
validators chore: scrub internal session-log narrative from code comments 2026-05-10 21:29:40 -07:00
.gitignore feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00
aiken.toml feat(escrow_wip): aiken validator + plutus.json blueprint 2026-05-09 11:38:45 -07:00
plutus.json fix(escrow_wip): apply 2026-05-09 internal audit findings 2026-05-09 14:06:17 -07:00
README.md docs: public-facing README + contributing notes; generic CI comments 2026-06-28 11:39:05 -07:00
validator.cbor.hex fix(escrow_wip): apply 2026-05-09 internal audit findings 2026-05-09 14:06:17 -07:00

aiken-escrow

⚠️ UNAUDITED. No third-party security review has been performed. Internal review only. Treat as use-at-own-risk for high-value flows until external audit lands.

Two-party agreement-with-veto escrow validator (Plutus V3, Aiken v1.1.21). The off-chain (Rust) side lives in crates/aldabra-dao and is wired into the MCP tool surface via aldabra-mcp.

State machine

   Open ──(both sign Agree)──▶ Agreed{at} ──(lock_period elapsed, no veto)──▶ Settle (→ recipient)
     │                              │
     │                              └──(A or B fires Veto)─────────────▶ Refund (per-contributor)
     │
     └──(open_deadline passed, no agreement)─────────────────────────▶ Refund (per-contributor)

Build

cd aiken-escrow
aiken check       # type check + tests
aiken build       # produces plutus.json blueprint

The blueprint at plutus.json is consumed by aldabra's escrow builders to construct script addresses + spending witnesses.

Threat model — known gaps (out-of-scope for v1)

These are KNOWN gaps the validator does not protect against:

  • Datum CBOR canonicality. The Deposit redeemer compares cbor.serialise(expected) == cbor.serialise(new.deposits). If the Aiken stdlib's CBOR encoder is non-canonical for any input shape (e.g. map ordering), an attacker could submit a continuing output with the same logical content but byte-different and bypass the check. Mitigated by using List<Deposit> (not Map) which has deterministic order, but external review should re-confirm.
  • Stake credential preservation on refund outputs. Refund outputs are derived from contributor PKHs as null-stake base addresses. If a contributor's wallet uses a custom stake credential, refund value bypasses their stake-delegation pool. Acceptable v1 tradeoff.
  • Min-utxo per refund leg. Validator does not enforce min-utxo per refund output — assumes the off-chain builder has already ensured each deposit cleared min-utxo at deposit time. A pathological multi-asset deposit that splits below min-utxo on refund would brick the escrow until manual recovery.
  • Multi-script-input attack. If a single tx spends multiple escrow UTxOs simultaneously with overlapping signers, the per-UTxO validator runs independently. Cross-UTxO consistency is not enforced.

Status

  • Validator compiles cleanly (aiken build produces plutus.json).
  • Off-chain codecs in aldabra-dao::agora::escrow.
  • Off-chain unsigned-tx builders for all 6 paths (open / deposit / agree / veto / settle / refund-timeout) implemented + unit-tested.
  • MCP tool wrappers exposed under escrow_* prefix.
  • Lifecycle paths exercised end-to-end on preprod (settle / veto / refund-timeout).
  • Outstanding: external third-party audit before mainnet release.