Skip to content

P2P Exchange Escrow Contract: 6-leaf Taproot covenant with CSFS attestations #22

@Kukks

Description

@Kukks

P2P Exchange Escrow Contract

A self-enforcing P2P Bitcoin exchange escrow using the introspector's Taproot + CSFS + transaction introspection opcodes.

Design goals

  • Zero interactive signing sessions — no MuSig2, no nonce exchange, no session state
  • No liveness requirements except for the party that benefits from acting
  • CSFS as authorization gate — seller/buyer attestations prove who can move funds
  • Pre-approved destinations — buyer/seller receive addresses locked into the contract at creation
  • Arbitrator signs transactions directly — controls exact outputs in dispute resolution
  • Percentage-based fee enforcement — computed on-chain from input value using 64-bit arithmetic
  • Recursive covenant for escrow top-up/consolidation
  • Unilateral CSV exits additive to collaborative paths — seller/buyer can always recover
  • Seller CLTV self-release — if all parties disappear (except operator), seller can reclaim after absolute timelock

Entities

Entity Role
Buyer Sends fiat, claims BTC after authorization
Seller Funds escrow, receives fiat, attests to payment receipt
Arbitrator Resolves disputes by signing the transaction directly (OP_CHECKSIG)
Operator Ark server — signs all collaborative closures (from Ark address)
Introspector Validates Arkade scripts — key tweaked with arkade script hash

Parameters

Param Type Description
sellerPubKey 32-byte x-only pubkey Seller's public key — used in CSFS attestations and seller-keyed closures
buyerPubKey 32-byte x-only pubkey Buyer's public key — used in CSFS attestations and buyer-keyed closures
arbitratorPubKey 32-byte x-only pubkey Arbitrator's public key — OP_CHECKSIG on arbitrator closures
buyerSpk scriptPubKey Pre-approved buyer destination — checked via OP_INSPECTOUTPUTSCRIPTPUBKEY on SellerConfirm
sellerSpk scriptPubKey Pre-approved seller destination — checked via OP_INSPECTOUTPUTSCRIPTPUBKEY on BuyerRefund
feeSpk scriptPubKey Fee output address — checked via OP_INSPECTOUTPUTSCRIPTPUBKEY
feeBasisPoints uint64 Fee as basis points (e.g. 200 = 2%) — used in OP_MUL64/OP_DIV64 computation
cltvTimeout int64 Absolute locktime (block height) for seller self-release path
csvTimeout int64 Relative locktime (blocks) for unilateral exit paths
tradeID 32 bytes External trade identifier — used to derive RELEASE and CANCEL oracle messages

Oracle messages

  • RELEASE: SHA256(0x01 || tradeID) — attested by seller (payment received) via CSFS
  • CANCEL: SHA256(0x02 || tradeID) — attested by buyer (voluntary refund) via CSFS

VTXO taproot tree

Each row is one spendable path. Tapscript opcodes are the Bitcoin Script in the tapscript itself. Arkade script opcodes are validated by the introspector engine inside that tapscript.

Path When used Tapscript opcodes Arkade script opcodes
Seller confirms Seller received fiat payment and attests RELEASE. Buyer claims BTC to pre-approved address. Fee (input x bp / 10000) enforced. Buyer amount enforced (input - fee). <buyer> OP_CHECKSIGVERIFY <introspector> OP_CHECKSIGVERIFY <operator> OP_CHECKSIG <seller> OP_CHECKSIGFROMSTACK(RELEASE) OP_VERIFY OP_INSPECTNUMINPUTS 1 OP_EQUALVERIFY 0 OP_INSPECTOUTPUTSCRIPTPUBKEY <buyer_ver> OP_EQUALVERIFY <buyer_prog> OP_EQUALVERIFY 1 OP_INSPECTOUTPUTSCRIPTPUBKEY <fee_ver> OP_EQUALVERIFY <fee_prog> OP_EQUALVERIFY OP_PUSHCURRENTINPUTINDEX OP_INSPECTINPUTVALUE <bp> OP_MUL64 OP_VERIFY <10000> OP_DIV64 OP_VERIFY OP_SWAP OP_DROP 1 OP_INSPECTOUTPUTVALUE OP_SWAP OP_GREATERTHANOREQUAL64 OP_VERIFY OP_PUSHCURRENTINPUTINDEX OP_INSPECTINPUTVALUE 1 OP_INSPECTOUTPUTVALUE OP_SUB64 OP_VERIFY 0 OP_INSPECTOUTPUTVALUE OP_SWAP OP_GREATERTHANOREQUAL64
Buyer cancels Buyer voluntarily cancels the trade. Full amount returns to pre-approved seller address. No fee. Amount enforced (seller gets >= input). <seller> OP_CHECKSIGVERIFY <introspector> OP_CHECKSIGVERIFY <operator> OP_CHECKSIG <buyer> OP_CHECKSIGFROMSTACK(CANCEL) OP_VERIFY 0 OP_INSPECTOUTPUTSCRIPTPUBKEY <seller_ver> OP_EQUALVERIFY <seller_prog> OP_EQUALVERIFY OP_PUSHCURRENTINPUTINDEX OP_INSPECTINPUTVALUE 0 OP_INSPECTOUTPUTVALUE OP_SWAP OP_GREATERTHANOREQUAL64
Topup Anyone adds more BTC to the escrow (e.g. top-up or consolidation). No fee. <buyer> OP_CHECKSIGVERIFY <introspector> OP_CHECKSIGVERIFY <operator> OP_CHECKSIG OP_PUSHCURRENTINPUTINDEX OP_INSPECTINPUTSCRIPTPUBKEY OP_1 OP_EQUALVERIFY 0 OP_INSPECTOUTPUTSCRIPTPUBKEY OP_1 OP_EQUALVERIFY OP_EQUALVERIFY OP_PUSHCURRENTINPUTINDEX OP_INSPECTINPUTVALUE 0 OP_INSPECTOUTPUTVALUE OP_LESSTHAN64
Arbitrator releases to buyer Dispute resolved in buyer's favor. Arbitrator signs the transaction directly, controlling exact outputs and fee. No Arkade script. <buyer> OP_CHECKSIGVERIFY <arbitrator> OP_CHECKSIGVERIFY <operator> OP_CHECKSIG
Arbitrator refunds seller Dispute resolved in seller's favor. Arbitrator signs the transaction directly, controlling exact outputs. No Arkade script, no fee. <seller> OP_CHECKSIGVERIFY <arbitrator> OP_CHECKSIGVERIFY <operator> OP_CHECKSIG
Seller self-release (CLTV) After CLTV expires, seller reclaims funds if buyer and arbitrator are absent. No introspector, no Arkade script, no fee. <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <seller> OP_CHECKSIGVERIFY <operator> OP_CHECKSIG
Mutual exit (CSV) Buyer and seller mutually agree to exit without operator/introspector. Available after relative timelock. No fee. <sequence> OP_CHECKSEQUENCEVERIFY OP_DROP <buyer> OP_CHECKSIGVERIFY <seller> OP_CHECKSIG
Seller-only recovery (CSV) Last resort if buyer, arbitrator, and operator are all unresponsive. Available after double relative timelock. No fee. <sequence> OP_CHECKSEQUENCEVERIFY OP_DROP <seller> OP_CHECKSIG

Key design decisions

  1. Checksigs outside Arkade script: Transaction authorization (CHECKSIG) lives in the tapscript closure. CSFS attestations + introspection live in Arkade scripts validated by the introspector.

  2. Arbitrator uses OP_CHECKSIG, not CSFS: The arbitrator signs the transaction directly, controlling exact outputs (who gets what, fee amounts). No need for CSFS attestations or Arkade script fee enforcement on arbitrator paths. This gives the arbitrator full control over dispute resolution outcomes.

  3. Three closure types: Buyer-keyed closures with introspector (SellerConfirm, Topup), seller-keyed closure with introspector (BuyerRefund), and arbitrator closures without introspector (ArbitratorToBuyer, ArbitratorToSeller).

  4. Percentage-based fees: Fee scales with escrow value using on-chain 64-bit arithmetic (OP_MUL64/OP_DIV64). Basis points allow fine-grained control (e.g., 200 = 2%).

  5. External trade ID: Not derived from pubkeys — allows the same parties to have multiple concurrent trades.

  6. CSV unilateral exits are additive: Always available alongside collaborative paths. Seller can always recover.

  7. Seller CLTV self-release: If buyer and arbitrator both disappear, seller can reclaim funds after the CLTV absolute timelock expires via the operator only. No introspector or Arkade script needed — pure multisig with timelock.

  8. Pre-approved destinations: Buyer and seller destination addresses are locked into the contract at creation. SellerConfirm enforces output[0] goes to buyerSpk, BuyerRefund enforces output[0] goes to sellerSpk. This prevents fund diversion even if CSFS attestation keys are compromised.

Implementation

  • Implement each Arkade leaf script using the introspector opcode set
  • Integration tests for SellerConfirm, BuyerRefund, TopupPath
  • Test fee enforcement: attempt fee-skipping TX, verify rejection
  • Test TopupPath: recursive covenant verification
  • Test invalid attestation (wrong key)
  • Percentage-based fee using OP_MUL64/OP_DIV64
  • External trade ID
  • Unilateral CSV exit paths
  • ReadArkadeScript supports all closure types (MultisigClosure, CLTVMultisigClosure, etc.)
  • Seller self-release with CLTV absolute timelock
  • Separate buyer/seller/arbitrator collaborative closures
  • Pre-approved buyer/seller destination address enforcement
  • Integration tests for arbitrator paths (pure multisig, no Arkade)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions