Skip to content

Instantly share code, notes, and snippets.

@melvincarvalho
Last active May 8, 2025 18:32
Show Gist options
  • Save melvincarvalho/c1111afb4a26de98c44dfde7508a28ef to your computer and use it in GitHub Desktop.
Save melvincarvalho/c1111afb4a26de98c44dfde7508a28ef to your computer and use it in GitHub Desktop.
txop.md

1. High-level architecture

┌──────────────┐        WebSocket / HTTP-JSON        ┌──────────────────┐
│  CJ Client   │  <-- register / sign / finalize --> │  CJ Coordinator  │
└──────────────┘                                      └──────────────────┘
        ▲                                                       │
        │ uses                                                  │ broadcasts
        │                                                       ▼
┌──────────────────┐                                ┌──────────────────┐
│  Wallet Adapter  │–– talk to local wallet RPC ––▶│  Bitcoin network │
└──────────────────┘                                └──────────────────┘
Component Role Key files / packages
cj_client (library + CLI) Talks to the coordinator; wraps txo_parser @myorg/cj_client
cj_coordinator Stateless HTTP/WebSocket API that orchestrates a round @myorg/cj_coordinator
wallet_adapter Pluggable module that turns a wallet’s UTXO list ⇄ TXO URIs; signs PSBTs @myorg/wallet_adapter_xxx
shared-types TypeScript *.d.ts exported for both sides @myorg/cj_types

2. Message contract (LLM-optimized JSON)

// cj_types/src/messages.ts
export interface RegisterInputReq {
  type: 'register_input'
  txo_uri: string        // e.g. "txo:btc:<txid>:0?amount=0.1"
  denomination: string   // "0.1" (as decimal string)
  blinded_output: string // credential-request for output address
}

export interface RegisterInputAck {
  type: 'register_input_ok'
  blinded_signature: string
}

export interface CommitOutputReq {
  type: 'commit_output'
  output_script: string   // hex
  amount: string          // must equal denomination
}

export interface FinalPsbt {
  type: 'psbt'
  base64_psbt: string
}

Why this works well for LLMs

  • Keys are full English words (blinded_output not bout)
  • Snake-case everywhere → crystal token boundaries
  • Explicit type field → easy pattern matching in GPT / copilot code

3. Coordinator round flow

Step Who Endpoint Payload (excerpt)
1. Round info Client → Coord. GET /round { denomination: "0.1", round_id: "abc123" }
2. Input registration Client → Coord. (POST) /round/abc123/register-input RegisterInputReq
3. Output commitment Client → Coord. (POST) /round/abc123/commit-output CommitOutputReq
4. PSBT distribution Coord. → Client (WS) FinalPsbt
5. Signature collection Client signs & returns /round/abc123/sign { signed_psbt }
6. Broadcast Coord. → Bitcoin raw tx

All numeric amounts remain decimal strings in JSON to dodge JS Number pitfalls.


4. Using txo_parser in the client

import {
  parseTxoUri,
  isValidTxoUri,
  formatTxoUri,
} from 'txo_parser'

function prepareInput(txoUri: string): RegisterInputReq {
  if (!isValidTxoUri(txoUri)) throw new Error('Bad TXO URI')

  const { amount } = parseTxoUri(txoUri)
  const blinded = credentialClient.blindOutputKey()

  return {
    type: 'register_input',
    txo_uri: txoUri,
    denomination: amount.toString(),
    blinded_output: blinded.request,
  }
}

// Display back to user
console.log(
  'Registered:',
  formatTxoUri(parseTxoUri(txoUri)) // canonical form
)

5. Wallet adapter sketch

// wallet_adapter_electrum.ts
import ElectrumClient from 'electrum-client'
import { formatTxoUri } from 'txo_parser'

export async function listSpendableUris(): Promise<string[]> {
  const utxos = await electrum.listUnspent()
  return utxos.map(u =>
    formatTxoUri({
      network: 'btc',
      txid: u.tx_hash,
      output: u.tx_pos,
      amount: u.value / 1e8,
    })
  )
}

Same adapter signs the PSBT and returns partial sigs.


6. Security & privacy knobs

Risk Mitigation tied to TXO URI
Linking inputs ↔ outputs Use blind credential scheme; outputs never reference origin TXO URI
URI leaks private_key Coordinator rejects any URI that contains private_key key
Denial-of-service spam Coordinator checks tx_id on mempool before accepting

7. Folder layout (monorepo)

packages/
  txo_parser/          ← your existing lib
  cj_types/            ← shared message/DTO defs
  cj_client/
    src/
      index.ts
      round.ts
  cj_coordinator/
    src/
      api.ts
      round_engine.ts
  wallet_adapter_electrum/

8. Developer UX

# user flow
npm i -g cj_client wallet_adapter_electrum

cj-client discover        # lists open rounds
cj-client join \
  --txo $(wallet-list-utxos | fzf) \
  --round abc123

Under the hood every UTXO is fed in as a single line TXO URI, keeping pipes and scripts dead simple.


9. Why this design is LLM-friendly

  1. Uniform resource string (txo:*) means prompts don’t juggle multiple representations.
  2. Snake_case everywhere → fewer sub-tokens, higher embedding overlap (output_index ≈ “output” + “index”).
  3. Topic-aligned filenames (round_engine.ts, wallet_adapter.ts) boost code-completion precision.
  4. All numeric strings are plain decimals—no scientific notation surprises.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment