feat(escrow_wip): aiken validator + plutus.json blueprint
⚠ WIP — UNAUDITED. Plutus V3, Aiken v1.1.21. Preprod-only. Five-redeemer two-party agreement-with-veto escrow validator. Mirrors the off-chain codecs at crates/aldabra-dao/src/agora/escrow.rs. Validator script hash: 223aa7ace4a98ff5b8f8988c1c07b846c046de1a2bc9e8dc77411486 Compiled UPLC size: 7902 bytes. Datum: ProductIsData (Constr 0 [a, b, recipient, deadline, lock, state, deposits]). Redeemer: Constr 0..4 (Deposit | Agree | Veto | Settle | Refund). DepositEntry.value uses concrete Pairs<ByteArray, Pairs<ByteArray, Int>> since cardano/assets.Value is opaque (datums require concrete types). Pairs encode as Plutus Map at the dataType layer — matches the off-chain EscrowValue codec's PlutusData::Map(KeyValuePairs) emission. Build: cd aiken-escrow && aiken build (produces plutus.json blueprint). Threat-model gaps explicitly documented in aiken-escrow/README.md: - CBOR canonicality of Pairs serialisation (validator equality check) - Stake-credential null'd on refund outputs (intentional — protects pool delegation privacy at cost of stake reward routing) - No min-utxo enforcement on refund legs (off-chain builder's job) - No multi-script-input cross-UTxO consistency External audit gates mainnet deployment.
This commit is contained in:
parent
aff39a668f
commit
b1a95284b5
5 changed files with 674 additions and 0 deletions
73
aiken-escrow/README.md
Normal file
73
aiken-escrow/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# aiken-escrow
|
||||
|
||||
> ⚠️ **WIP — UNAUDITED.** Preprod testing only. Do **NOT** route mainnet
|
||||
> funds through this validator. No third-party security review has been
|
||||
> performed.
|
||||
|
||||
Two-party agreement-with-veto escrow validator (Plutus V3, Aiken
|
||||
v1.1.21). The off-chain (Rust) side lives in `crates/aldabra-dao` behind
|
||||
the `escrow_wip` feature flag.
|
||||
|
||||
## Spec
|
||||
|
||||
`aiken-escrow/README.md` documents the state machine, datum
|
||||
shape, and redeemer invariants.
|
||||
|
||||
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
|
||||
|
||||
```bash
|
||||
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 (out-of-scope for v1)
|
||||
|
||||
These are KNOWN gaps the validator does not protect against. They
|
||||
inform the WIP designation:
|
||||
|
||||
- **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. We mitigate 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;
|
||||
documented in spec.
|
||||
- **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
|
||||
|
||||
- [x] Validator compiles (`aiken build` produces `plutus.json`).
|
||||
- [x] Off-chain codecs in `aldabra-dao::agora::escrow`.
|
||||
- [ ] Off-chain unsigned-tx builders (5 paths).
|
||||
- [ ] MCP tool wrappers.
|
||||
- [ ] Preprod E2E (open → both deposit → agree → settle).
|
||||
- [ ] Preprod E2E (open → agree → veto).
|
||||
- [ ] Preprod E2E (open → refund-timeout).
|
||||
- [ ] External audit.
|
||||
- [ ] Mainnet release gate.
|
||||
Loading…
Add table
Add a link
Reference in a new issue