Commit Graph

9 Commits

Author SHA1 Message Date
david
57e77e0e25 shared: route ref-script server helpers via tx/server (keep buyer bundle clean)
Static re-exports from tx/index.ts caused vite to trace
loadEscrowScript/loadAdvertScript/buildLiveTxForPublishReferenceScripts
into the buyer UI bundle, hitting the CML WASM "ESM integration proposal"
limitation. Sibling builders avoid this with /* @vite-ignore */ dynamic
imports; we keep symmetry by parking the helpers on a separate
@marketplace/shared/tx/server entry that the buyer never imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:41:29 +02:00
david
5cae0c4f6c shared: opt-in CIP-33 reference-scripts for script-spending txs
When ESCROW_REF_UTXO / ADVERT_REF_UTXO env vars are set (format
<txhash>#<idx>), Claim/Submit/Accept/Reclaim/RetireAdvert call
.readFrom([refUtxo]) instead of inlining the validator CBOR via
.attach.SpendingValidator(). Lucid-evolution 0.4.30 auto-extracts
utxo.scriptRef into the script-witness map (dist/index.js:1903–1913),
so no separate attach call is needed. Default (env unset) is unchanged.

Adds buildLiveTxForPublishReferenceScripts (a one-shot tx that
publishes both validators as scriptRef-bearing UTxOs at a caller-
supplied address — typically the all-zero-pkh enterprise burn address)
and the supplier tx:publish-reference-scripts CLI that wraps it.

Targeted at testnet trial; mainnet rollout is a separate flip after
verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:25:59 +02:00
david
dcab0ba5b5 deploy: mainnet bring-up patches (env-override genesis + host/network)
shared: VECTOR_ZERO_TIME now reads VECTOR_ZERO_TIME_MS from env, defaults
to testnet genesis (1_752_057_484_000 ms). Mainnet deploys set
VECTOR_ZERO_TIME_MS=1756485600000. Existing testnet deployments unaffected
since env unset → testnet default. Patched in both lucidContext.ts (lucid
slot config override) and liveCbor.ts buildLiveTxForSubmit (submitted_at
slot-alignment).

deploy/mainnet: buyer host changed mp.vector.* → marketplace.vector.* per
operator-owned DNS. Indexer compose drops apex-net network (we point at
the hosted mainnet ogmios over public WSS, not via apex-dashboard-mainnet
which is not deployed on this box).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:32:26 +02:00
david
c09245a507 shared: live-CBOR for deferred builders (Reclaim, RetireAdvert)
Closes 2/4 builders from the M1-F-D-defer list (postAdvert already live in
7ea7328; updateAdvert and release remain).

Reclaim:
- buildLiveTxForReclaim in liveCbor.ts (mirrors buildLiveTxForAccept):
  spends the escrow UTxO with REDEEMER_RECLAIM (Constr3), pays
  payment + buyer_bond + supplier_bond to buyer, delegates UPLC eval
  to Ogmios via localUPLCEval:false. Validity range = tipMs ± 60s.
- reclaim.ts: replace the unconditional mockSlotToWallclockMs tipMs
  computation with `isLive ? Date.now() : mockSlotToWallclockMs(tipSlot)`,
  matching accept.ts:77. Adds the live-CBOR dispatch branch.

Funds-at-risk before this fix: with the broken tipMs path, every live
Reclaim was rejected client-side ("tip < deliver_by" using a Nov-1970
tip). If a supplier never Claimed, buyer's 6-7 AP3X stayed locked
indefinitely. Verified recovery of 4 stuck escrows on Vector L2 testnet
(txs f6660e52, 769f16fd, 79970d86, 9f4db14b — 14 AP3X total recovered).

RetireAdvert:
- buildLiveTxForRetireAdvert in liveCbor.ts: spends advert UTxO with
  REDEEMER_RETIRE_ADVERT (Constr2), advert script address.
- retireAdvert.ts: live-CBOR dispatch.
- supplier CLI: retire-advert.ts + retireAdvertFlow.ts.
- supplier/package.json: tx:retire-advert script.

Indexer:
- follower/blockProcessor + worker: handle RetireAdvert events,
  transition advert row state to Retired.
- db/cache.ts: markAdvertTerminal helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:57:51 +02:00
jelisaveta-talbot
0b93aee1ec test: clarify M1-F-D-defer status + add LIVECBOR_DEBUG env
While checking whether last night's localUPLCEval: false fix unblocks
the 5 tx-submit-live tests skipped under TODO(M1-F-D-defer), found
that those tests still crash — but at a DIFFERENT spot than before.

Original UPLC issue (now fixed in production via 7ea7328 + fd24598):
- lucid's CML WASM UPLC interpreter falsely rejected our Plutus V3
  Submit/Claim/Accept txs. End-to-end chat + TTS lifecycle now works
  on Vector testnet by routing eval to Ogmios.

New / remaining test-side issue:
- Even with a proper evaluateTransaction mock response, lucid's
  applyUPLCEvalProvider → CML TransactionBuilder.set_exunits still
  panics with wasm "unreachable" inside the test fixture. The
  production path doesn't hit this (real Ogmios + real wallet UTxOs).
  Whatever the test-mock + CML interaction trips, it's separate from
  the original UPLC bug.

Refinements landed
- liveCbor.ts: opt-in LIVECBOR_DEBUG=1 env that prints the full inner
  err + WASM stack from rethrowAsTxError, so the next investigation
  doesn't need to re-add this scaffolding.
- tx-submit-live.test.ts:
  - setupSubmitHappyPath() now mocks "evaluateTransaction" with a
    proper EvalRedeemer-shaped response (useful baseline for any
    future un-skip attempt; not enough on its own to make the tests
    pass).
  - The first skipped test's TODO comment updated to record what's
    fixed in production vs. what remains as a test-infra bug.

Test counts unchanged (3 pass, 5 still skipped); production path
still verified end-to-end on testnet. Task M1-F-D-defer stays pending
but the original UPLC-interpreter blocker is gone.
2026-05-11 09:09:49 +02:00
jelisaveta-talbot
fd24598186 shared: also delegate UPLC eval on Accept tx
buildLiveTxForAccept has its own .complete() call separate from
buildSpendOpenOrClaimedTx (which already routed eval to Ogmios in
7ea7328). The same lucid 0.4.30 CML WASM false-rejection bit Accept
on every escrow — confirmed by 502s on three different pending
receipts in the SPA after the chat/TTS Submit fix landed.

Adding `localUPLCEval: false` to Accept's .complete() options closes
the gap; verified end-to-end against Vector testnet (Accept tx
0385442fd7ef72743194f8fa0b468c2c56bacb90b023f86ec2c3df22b18b7ca8
confirmed under the prior workaround; subsequent Accepts on the
patched build also confirm cleanly).
2026-05-07 21:48:47 +02:00
jelisaveta-talbot
7ea7328eed shared: live PostAdvert + delegate UPLC eval to Ogmios
Two production-essential fixes that surfaced during the path-A TTS
deployment to Vector testnet:

1. localUPLCEval: false on every Plutus V3 spend
   ──────────────────────────────────────────────
   lucid-evolution 0.4.30's CML WASM UPLC evaluator has a false-rejection
   bug for our V3 spend pattern: it rejects perfectly valid txs with
   "failed script execution Spend[0] the validator crashed / exited
   prematurely" while the chain itself accepts them without complaint.
   Routing eval to Ogmios's evaluateTransaction (cardano-node's
   evaluator) sidesteps the WASM bug and returns correct ex-units.
   OgmiosLucidProvider.evaluateTx already implements this path.

   This also unblocks the 5 tx-submit-live tests previously skipped
   with the comment "UPLC wasm crashes" (M1-F-D-defer / task #28).

   Without this fix, every Claim/Submit/Accept tx from our supplier
   was failing locally before reaching the chain. The third-party
   summitstak.ing supplier worked because it uses a different
   toolchain that does not run client-side phase-2 evaluation.

2. AdvertDatum BigInt coercion
   ────────────────────────────
   cbor-x emits float64 (`fb` prefix) for JS Number values that can't
   fit CBOR's compact int forms (e.g. POSIX-ms timestamps). Plutus
   Data has no float type, so CML.PlutusData.from_cbor_hex panics
   with "RuntimeError: unreachable" when it sees one. Coerce
   max_output_tokens, max_processing_ms, advertised_at to BigInt
   before encoding so cbor-x always emits major-type-0/1 integers.
   Mirrors the intAsBig pattern already used in EscrowDatum.

3. Live-CBOR PostAdvert builder
   ─────────────────────────────
   M1-F-4 deferred postAdvert/updateAdvert/retireAdvert/reclaim/
   release on the live path. With (1) and (2) in place, posting an
   advert via tx:post-advert against Vector testnet now works.
   Added buildLiveTxForAdvert in liveCbor.ts (mirrors PostEscrow)
   and routed buildPostAdvertTx through it when chain is live.

40 advert/post-advert tests still pass. Verified end-to-end on
Vector testnet: TTS advert posted at
5df793ac12dc426fae7a7a01d56d3e923d3985103d62f0182d9ba63dda871f96#0,
full chat + TTS marketplace lifecycle (PostEscrow → Claim → Submit →
Accept) confirmed completing successfully on chain.
2026-05-07 05:17:19 +02:00
jelisaveta-talbot
34a52d5d16 buyer: TTS marketplace lifecycle (audio.synthesize.piper.v1)
Path-A buyer side: PostEscrow → supplier /v1/audio/synthesize → 202+poll
→ on-chain Submit → receipt verify, returning the audio bytes + signed
receipt to the SPA. Pairs with the supplier-side scaffolding in 3c3e009.

Files

- packages/shared/src/tx/escrow/postTtsEscrow.ts (new):
  buildPostTtsEscrowTx() mirrors postEscrow.ts but commits a TTS-shaped
  prompt_hash = sha256(canonical({text, voice, format, speed})). The
  same hash is exposed as ttsPromptHash() so the buyer SDK and supplier
  validation can pin the canonicalisation; any drift fails the
  supplier's prompt_mismatch gate. Validates body shape (text non-empty,
  voice/format in allow-list, speed ∈ [0.5,1.5]); reuses the chat live-
  cbor path which is already capability-agnostic.

- packages/shared/src/tx/index.ts: export the new TTS surface.

- buyer/src/sdk/Marketplace.ts: + submitTts() — full lifecycle mirror of
  submitPrompt with TTS-shaped POST body, /v1/audio/synthesize endpoint,
  and audio_b64 in the polled response. Receipt verification gates on
  prompt_hash via ttsPromptHash() instead of canonicalize(messages).

- buyer/src/sdk/types.ts: + SubmitTtsOptions / SubmitTtsResult.

- buyer/src/server.ts: + POST /v1/submit-tts (mirrors /v1/submit-prompt
  shape; routes to marketplace.submitTts).

- buyer/src/ui/components/PiperTTSForm.tsx: two-mode rendering. When
  advertRef + payment_lovelace are passed, posts to /v1/submit-tts and
  decodes base64 audio into a blob URL. When neither is passed, falls
  back to /v1/synth-speech (the path-B direct demo proxy).

- buyer/src/ui/pages/Dashboard.tsx: capability dispatch routes a real
  on-chain TTS supplier to the marketplace mode and the synthetic demo
  card to the direct mode (its utxo_ref="demo:piper-tts" fails the ref
  parser → advertRef is null → demo mode). Same component for both.

Tests still passing: 31 buyer + 29 supplier; both typechecks clean.
Chat path behaviourally untouched.
2026-05-06 22:00:31 +02:00
jelisaveta-talbot
cbab81efbd M1-F: end-to-end LLM marketplace lifecycle on Vector testnet
Bonded-escrow marketplace for prompt → response between buyers and
local-LLM suppliers, settling on Vector (UTxO) via Aiken Plutus V3
validators. Lifecycle PostEscrow → Claim → Submit → Accept proven on
Vector testnet 2026-05-06; AcceptEscrow tx 125e3cfe…7dc100d distributes
payment + bonds per the validator's distribution rule.

Workspace layout (pnpm + TypeScript):
  contracts/marketplace/      Aiken validators (advert + escrow) + plutus.json
  packages/shared/            Tx-builders (lucid-evolution), CBOR codecs,
                              chain providers (mock / read-only / live Ogmios)
  supplier/                   OpenAI-compatible HTTP node (Ollama by default;
                              vLLM / llama.cpp / OpenRouter / OpenClaw all
                              swap-in via OLLAMA_URL); 202+poll chat handler
  buyer/                      Express + React SPA; server-side runs the full
                              chain lifecycle via /v1/submit-prompt and
                              /v1/accept; SPA never holds the buyer key
  indexer/                    Chain-follower (extends apex-dashboard pattern),
                              SSE event stream + REST queries
  indexer-ui/                 Operator dashboard (events + suppliers + escrow
                              lookup)
  tests/                      1098 vitest unit + integration tests (10 skipped
                              under M1-F-D-defer for UPLC test plumbing)
  deploy/testnet/             Per-service docker-compose for the Hetzner stack
  docs/EXECUTIVE_SUMMARY.*    Marp deck (PDF + PPTX) — 11 slides, mermaid
                              diagrams pre-rendered for portable export
  docs/CLOUDFLARE_WAF_BYPASS.md  CF Skip-rule runbook for /v1/* paths
  docs/ARCHITECTURE.md        canonical spec

Live demo (testnet, 2026-05-06):
  https://mp-buyers.vector.testnet.apexfusion.org      (buyer SPA)
  https://mp-suppliers.vector.testnet.apexfusion.org   (supplier API)
  https://mp-indexer.vector.testnet.apexfusion.org     (indexer + UI)
2026-05-06 13:27:48 +02:00