aldabra/Dockerfile
Kayos eb192fa676 fix(escrow_wip): apply 2026-05-09 internal audit findings
Two HIGH validator-side bugs + several MED/LOW off-chain issues found
in the subagent-driven audit on this branch. New validator hash:
a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d.

## HIGH-1: Deposit redeemer let depositors drain tokens

aiken-escrow/validators/escrow.ak Deposit branch now requires
`value_geq_value(new_value, in_value)` before computing net_added.
Previously net_added could carry negative quantities (when new_value
< in_value component-wise), letting a depositor write a matching
new_d.deposits with reduced values and pocket the difference as
wallet change. Latent under v1 ADA-only MCP usage but the validator
must hold against all callers.

## HIGH-2: Empty/partial deposits enabled funds drain via Veto/Refund

Veto and Refund branches now require
`value_eq(deposits_to_value(d.deposits), in_value)` — the tracked
deposits must account for the full locked value. Previously
`refund_outputs_satisfy(_, [])` was vacuously true on empty deposits,
so a driver could fire Veto/Refund on an escrow opened with
`initial_contributor=None` (deposits=[], in_value>0) and pocket the
input's lovelace as change.

Defense in depth: escrow_open builder now refuses
`initial_contributor=None`. New helper `deposits_to_value` folds
deposit FlatValues into a Value via `assets.add` for the equality
check.

## MED: off-chain fixes

- escrow_open min-utxo bumped 1M → 2M (Conway-era inline-datum
  + script-address outputs need ~1.4-1.7 ADA, NOT the 1 ADA default).
- escrow_settle_unsigned + escrow_refund_timeout_unsigned now derive
  `validity_lower_ms` via slot_to_posix_ms(network, slot) instead of
  Koios's `block_time*1000` — the chain reconstructs `lower` from the
  slot, so Koios's ~1s drift could pass off-chain preflight while the
  chain rejects at the strict-`>` boundary.
- escrow_open_unsigned MCP tool no longer accepts (and silently
  discards) `fee_lovelace` — the unsigned-tx builder auto-estimates.

## LOW: defensive depth

- escrow_veto + escrow_refund_timeout: `qty as u64` → `u64::try_from`
  so a corrupt or adversarial datum with negative i128 qty can't slip
  through with a wraparound.

## Tests

- 36 escrow builder tests pass (added rejects_no_initial_contributor)
- 132 dao tests pass under --features escrow_wip
- aldabra-mcp release build clean

## Infra

- Validator artifact files (plutus.json, validator.cbor.hex)
  regenerated. Dockerfile already wired to bake them at
  /etc/aldabra/escrow/ for MCP tools' validator_script_path arg.
- Internal audit findings written up at
  audits/2026-05-09-escrow-internal-audit.md including the v2-deferred
  items (multi-asset spend-input, lovelace-not-cross-checked, etc.)

Third-party audit still required before any mainnet deployment.
2026-05-09 14:06:17 -07:00

91 lines
4 KiB
Docker

# syntax=docker/dockerfile:1.4
# aldabra — Cardano lite wallet over MCP.
#
# Multi-stage:
# 1. builder — rust toolchain, cargo build --release
# 2. runtime — debian:bookworm-slim, just the binary + ca-certs.
#
# Built nightly on Lucy (see lucy-infra/scripts/nightly-builds.sh) and
# published as `lucy-registry:5000/aldabra/mcp:{SHA,latest}`. Pulled
# anywhere we want the MCP server available — usually as a sidecar
# spawned by an MCP client (Claude Code, OpenClaw).
#
# Required env at runtime:
# ALDABRA_DATA directory containing mnemonic.age (must
# already exist; bootstrap separately on
# first install)
# ALDABRA_NETWORK mainnet | preview | preprod (default preprod)
# ALDABRA_KOIOS_BASE defaults to public Koios for the network
# ALDABRA_PASSPHRASE unlocks mnemonic.age. Source from a docker
# secret or systemd EnvironmentFile — never
# commit it.
FROM rust:1.95-bookworm AS builder
WORKDIR /build
# Cache deps separately from source. Copy manifests + dummy bins so
# `cargo build` resolves and downloads everything before the real
# source rebuilds invalidate the layer.
COPY Cargo.toml ./
COPY crates/aldabra-core/Cargo.toml crates/aldabra-core/
COPY crates/aldabra-chain/Cargo.toml crates/aldabra-chain/
COPY crates/aldabra-mcp/Cargo.toml crates/aldabra-mcp/
RUN mkdir -p crates/aldabra-core/src crates/aldabra-chain/src crates/aldabra-mcp/src && \
echo 'fn main() {}' > crates/aldabra-mcp/src/main.rs && \
echo '' > crates/aldabra-core/src/lib.rs && \
echo '' > crates/aldabra-chain/src/lib.rs && \
cargo build --release --bin aldabra || true && \
rm -rf crates/*/src
COPY crates ./crates
# Touch every src file so cargo notices and rebuilds. The dummy-source
# trick above leaves stale build artifacts otherwise.
RUN find crates -name '*.rs' -exec touch {} +
# Fetch the pallas patch dep via HTTP+PAT at build time. Source URLs
# stay SSH (Cargo.toml + Cargo.lock) — the rewrite is git-CLI-level
# only, so no credential gets baked into the lock file or the image.
# Pass `--secret id=git_credentials,src=<file>` where <file> is one
# line: http://USER:PAT@192.168.0.5:3001
RUN --mount=type=secret,id=git_credentials,target=/root/.git-credentials,mode=0400,required=true \
git config --global credential.helper store && \
git config --global url."http://192.168.0.5:3001/".insteadOf "ssh://git@192.168.0.5:23/" && \
cargo build --release --bin aldabra && \
strip target/release/aldabra
FROM debian:bookworm-slim AS runtime
# rustls-tls needs ca-certificates to verify Koios's TLS cert.
# tini for proper signal forwarding when running as PID 1.
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates tini && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/target/release/aldabra /usr/local/bin/aldabra
# Escrow V3 validator CBOR (escrow_wip surface). Baked at /etc/aldabra/
# escrow/validator.cbor.hex so MCP escrow_*_unsigned tools can pass it
# via `validator_script_path`. The MCP arg-truncation bug at >4500 hex
# chars makes the inline `validator_script_cbor_hex` option unusable
# for the 7902-char compiled validator. Validator hash:
# a8081acef26935d9b5f44b92052178e17301b6d6e6808c91c5b56f5d — check
# against the file's compiled hash before invoking on-chain ops.
COPY aiken-escrow/validator.cbor.hex /etc/aldabra/escrow/validator.cbor.hex
COPY aiken-escrow/plutus.json /etc/aldabra/escrow/plutus.json
# Default data dir — mount a volume here in compose / k8s / docker run.
ENV ALDABRA_DATA=/var/lib/aldabra
RUN mkdir -p /var/lib/aldabra && chmod 700 /var/lib/aldabra
# Non-root user for runtime. Mnemonic.age is owner-readable only
# (chmod 600 from the bootstrap path), so the runtime UID must own
# the data dir.
RUN groupadd -r aldabra && useradd -r -g aldabra -d /var/lib/aldabra aldabra && \
chown -R aldabra:aldabra /var/lib/aldabra
USER aldabra
# tini handles SIGTERM and reaps zombies.
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/aldabra"]