Skip to content

feat: NUT-CTF conditional tokens for prediction markets#1666

Draft
joemphilips wants to merge 11 commits intocashubtc:mainfrom
joemphilips:ctf
Draft

feat: NUT-CTF conditional tokens for prediction markets#1666
joemphilips wants to merge 11 commits intocashubtc:mainfrom
joemphilips:ctf

Conversation

@joemphilips
Copy link
Copy Markdown

Summary

Reference implementation of NUT-CTF: Conditional Tokens for Prediction Markets.

This is a work-in-progress draft. More details to follow as the spec stabilizes.

Implements the NUT-CTF specification for conditional tokens using DLC oracles.

- Condition registration with NIP-88 tags metadata

- Conditional keyset derivation per outcome

- CTF split/merge operations

- Outcome redemption with oracle attestation verification

- Numeric outcome markets with boundary keysets

- Multi-oracle threshold attestation support

- SQL migrations for conditions, partitions, and attestation storage

- Conditional-tokens feature flag gating all CTF code
Cargo.toml lists bindings/{dart,swift,kotlin}/rust as workspace members but the Dockerfile never copied the bindings directory, causing the Docker build to fail.
Required for docker-compose healthcheck (curl -f http://localhost:8085/v1/info).
Conditional keysets were being stored in the shared `keyset` table, which caused `reload_keys_from_db`'s `HashMap<CurrencyUnit, Id>` collapse to randomly mark a conditional keyset as the 'primary active sat', breaking wallet keychain binding over `/v1/keys`.

- Move conditional keysets to a new `conditional_keyset` table with a 'one active per outcome_collection_id' unique partial index. The primary `keyset` table keeps its 'one active per unit' invariant.

- Hide conditional keysets from the public NUT-01/NUT-02 list endpoints (`/v1/keys`, `/v1/keysets`). Per-ID lookups (`/v1/keys/{id}`) stay open so wallets holding a conditional token can still fetch its keys.

- Replace `SignatoryKeySet::is_conditional` with `condition_id: Option<String>` propagated from `MintKeySetInfo`, so the filter uses the DLC binding directly.

- Insert new conditional keysets directly into the in-memory signing map instead of full-DB reloads, removing O(N\xc2\xb2) behavior in `register_partition`.

- Share SQL row parsing and cursor-pagination helpers between the public NUT-CTF listing endpoint and the internal reload path, fix `>=` vs `>` on the `since` cursor, and add a `created_at` index on `conditional_keyset`.
The original 20260216 migration was modified in-place (4d95858) to

rename conditional_keysets -> conditional_keyset with a new schema.

Existing deployments that ran the old version skip the migration by

name, leaving the new table missing. This adds a separate migration

that drops the old table and creates the correct one.
The original 20260216 migration created a description column but the

current code expects tags_json. Add column, migrate existing data

into NIP-88 tag arrays, and keep description for safe rollback.
The tags_json ALTER TABLE was included in 20260420000000 which had

already been applied. Split into a separate 20260420010000 migration

so it runs on existing deployments.
The CDK SQL parser treats : as placeholder prefix, so :: in

Postgres type cast syntax triggers InvalidPlaceholder error.
Fresh DBs already have tags_json from the base migration. Guard the

ALTER TABLE with IF NOT EXISTS (postgres) and make SQLite a no-op.
The DLC spec signs over raw Writeable::write() bytes, not TLV-wrapped

bytes. verify_announcement_signature() was hashing full write_as_tlv()

output (including BigSize type+length prefix), causing verification to

fail for real oracle announcements. Tests passed because both signing

and verifying used the same wrong bytes.

Fix: delegate to announcement.validate(&secp) which uses correct

serialization. Update test_helpers to strip TLV prefix before signing

so test announcements produce spec-compliant signatures.
Add Conditional Tokens, CTF Split/Merge, and CTF Numeric to the
supported features section on the mint HTML index page when the
conditional-tokens feature flag is enabled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant