feat(escrow_wip): MCP escrow spend-tool surface (deposit/agree/veto/settle/refund_timeout)

Five new MCP tools wrapping the four Plutus V3 spend builders shipped
earlier in this branch:

- escrow_deposit_unsigned     → Deposit redeemer (continuing-output state)
- escrow_agree_unsigned       → Agree redeemer (Open → Agreed{at=upper}, both sign)
- escrow_veto_unsigned        → Veto redeemer (Agreed → multi-output refund)
- escrow_settle_unsigned      → Settle redeemer (Agreed → recipient payout)
- escrow_refund_timeout_unsigned → Refund redeemer (Open after open_deadline → multi-output refund)

Each takes the existing escrow UTxO ref + lovelace + datum_cbor_hex
(caller pulls via chain_address_info), the V3 validator UPLC
(inline hex OR file-path to dodge the >4500-char MCP transport bug),
the redeemer-specific args, and fee_lovelace. Validity windows
default to 30 min from chain tip; agree's window auto-clamps to
open_deadline_ms when needed.

Helper additions:
- EscrowDatum::from_cbor_hex on aldabra-dao keeps pallas-codec /
  pallas-primitives direct deps OUT of aldabra-mcp.
- decode_pkh28, resolve_validator_required, build_escrow_spend_in,
  fetch_tip_slot_ms in tools.rs — small helpers shared by all 5
  spend tools.

Drops the [features] section on aldabra-mcp's Cargo.toml. rmcp 0.1.5's
#[tool(tool_box)] macro scans the impl AST and references every
#[tool]-annotated method's generated wrapper regardless of cfg
eligibility — cfg-on-method gating fails to compile when the feature
is off because the macro emits unresolved symbol references. Pivot:
always-pull aldabra-dao/escrow_wip via the dep itself. The runtime
gate is the "WIP — UNAUDITED:" prefix in every tool description plus
the "wip_warning" field in JSON responses; the dao crate's escrow_wip
feature still gates downstream Rust consumers that want source-level
opt-out.

Verified: aldabra-mcp builds clean (default + release). 132 aldabra-
dao tests pass under --features escrow_wip including all 35 escrow
builder tests. Release binary produced.
This commit is contained in:
Sulkta 2026-05-09 13:36:44 -07:00
parent 54a90a5f8d
commit ef38ff0e57
3 changed files with 606 additions and 12 deletions

View file

@ -17,17 +17,21 @@ authors.workspace = true
name = "aldabra"
path = "src/main.rs"
[features]
default = []
# Enable the escrow_wip feature surface (Aiken V3 escrow validator + 6 builders
# + MCP tools). Mirrors aldabra-dao's escrow_wip — propagates the feature so
# the MCP binary actually exposes escrow_* tools. WIP / UNAUDITED — preprod only.
escrow_wip = ["aldabra-dao/escrow_wip"]
[dependencies]
aldabra-core = { path = "../aldabra-core" }
aldabra-chain = { path = "../aldabra-chain" }
aldabra-dao = { path = "../aldabra-dao" }
# Always pulls aldabra-dao/escrow_wip so the MCP binary can expose
# the escrow_* tool surface unconditionally. The "WIP — UNAUDITED:"
# prefix in every tool's description is the runtime gate; the dao
# crate's escrow_wip feature stays gated for downstream Rust consumers
# that want to opt out at the source level.
#
# Rationale: rmcp 0.1.5's #[tool(tool_box)] macro doesn't compose with
# #[cfg] on individual methods (it scans the impl AST and references
# every #[tool]-tagged method's generated wrapper, regardless of cfg
# eligibility). Force-pulling the feature at the dep level avoids the
# macro/cfg conflict.
aldabra-dao = { path = "../aldabra-dao", features = ["escrow_wip"] }
# Used directly in tools.rs to decode the wallet's bech32 address into a
# payment-credential hash (so `dao_my_stake` can match against StakeDatum.owner).