How the Ducat oracle works
Terms used in this lesson
- Oracle
- A system that reports outside information, such as a market price, for use by a protocol.
- Taproot
- A Bitcoin output and spending design that can combine a public-key path with privately committed alternative scripts.
- Witness
- Signatures, scripts, or other data supplied to satisfy a Bitcoin output’s spending condition.
- Guardian
- A Ducat policy-checking signer that can authorize or reject defined protocol transactions.
- DON
- Decentralized Oracle Network: a group of independent Chainlink oracle nodes that retrieve, check, combine, and sign outside data.
- CRE
- Chainlink Runtime Environment: the orchestration layer in which a workflow can request data, run checks, and coordinate outputs. It is not itself the price source.
- Nostr
- A signed-event messaging protocol; Ducat can use relays to distribute oracle material without making those relays Bitcoin validators.
- Validator
- Software or a participant that independently checks state transitions under a system's rules. The exact role depends on the protocol being discussed.
- Preimage
- The original secret data whose hash equals a previously committed value.
- Hash160
- SHA-256 followed by RIPEMD-160, producing the 20-byte hash used by several Bitcoin spending conditions.
- BTC
- The common market ticker for bitcoin.
- USD
- United States dollar, the reference currency in a BTC/USD price.
- Vault
- A set of Bitcoin outputs and spending rules that holds collateral and represents the current loan state.
- Collateral
- An asset pledged against a debt; it can be used to cover the debt if agreed conditions are breached.
- Tapscript
- The version of Bitcoin Script used by a Taproot script-path spend.
- Hashlock
- A spending condition that requires revealing data matching a committed hash.
- Control block
- Proof data showing that a revealed script was committed inside a Taproot output.
- Schnorr signature
- Bitcoin’s Taproot signature scheme, whose algebra supports compact verification and advanced multi-party signing.
Follow one bitcoin price from a signed Chainlink report, through Ducat's precommitted liquidation threshold, to the exact Taproot witness that Bitcoin verifies.
Loading lesson visuals...
Learn the idea
The Ducat oracle turns an outside BTC/USD price into a secret and signature that one precommitted Bitcoin vault path can verify. Chainlink supplies a signed price report. Ducat checks that report, commits many possible price outcomes in advance, and reveals the secret for a requested threshold only after a later checked price is below it. Bitcoin never reads the price. It checks the revealed secret and Guardian signature required by the vault.
Bitcoin's rules cannot fetch a dollar price from an outside service. A vault must lock in its price condition before the price moves, then receive the missing secret only if a later checked price crosses that condition.
Each participant has one job: Chainlink signs the price report, Ducat checks it and manages the hidden secrets, a Guardian approves the exact vault transaction, and Bitcoin checks the revealed secret and signature.Seven handoffs, each with a different job
Start with this plain-language map. The precise names, evidence, and cryptography come later.
- 1Chainlink packages the priceA signed package states the BTC/USD price, which feed it belongs to, and when it was observed.
- 2Ducat checks the packageThe workflow accepts a price only when the expected feed, time, and enough authorized signatures pass.
- 3Ducat prepares many trigger pricesIt publishes the complete set before telling wallets that the new set is current.
- 4A vault locks in one triggerThe vault's Bitcoin output commits to a hidden secret for its chosen price and to a Guardian key.
- 5An authorized caller asks for a checkDucat checks only the requested vault thresholds against a newly verified price.
- 6A Guardian approves the exact transactionThe Guardian checks the proposed liquidation and signs only the transaction it approves.
- 7Bitcoin checks the committed spendBitcoin verifies the revealed secret, Guardian signature, and normal transaction rules. Ducat software then checks the new protocol state.
The nine ideas you need
Read these once, then follow them through the mechanism below.
A versioned package containing a BTC/USD value, signed timing fields, a feed identifier, and signature evidence from authorized Chainlink oracle nodes.
Ducat code executed in the Chainlink Runtime Environment (CRE). It fetches and checks reports, then runs the Ducat publishing or evaluation logic.
One signed Ducat record containing the base price, time, Bitcoin network, oracle public key, and the rate range used to build a threshold ladder.
A set of prebuilt price contracts, one for each configured collateral-rate step. The secret for each step is hidden until that step is evaluated as breached.
The current snapshot is published only after every ladder chunk succeeds. A partial new ladder never becomes the snapshot wallets are told to use.
A signed Nostr record whose identifying tags let a newer valid record replace an older one. Nostr transports the record but does not decide its truth.
The 32-byte threshold key. Its Hash160 value is committed in the vault. Revealing the key lets Bitcoin satisfy that hash check.
Ducat liquidation transaction families. Repo can process one or more liquidated vaults; trim handles one target and preserves a canonical continuation output.
A new Bitcoin output that represents the permitted post-transaction Ducat state. Bitcoin checks ordinary script and value rules; Ducat validators check the protocol-specific shape.
From outside data to Bitcoin settlement
Chainlink returns a signed report
Chainlink Data Streams and its Decentralized Oracle Network (DON)
- Input
- Market data selected and aggregated by the configured Chainlink BTC/USD stream.
- What happens
- The external Chainlink network produces a Data Streams v3 report and signer evidence. Ducat does not control how the upstream market observations are selected or aggregated.
- Output
- A fullReport containing report context, a versioned report blob, and ECDSA signature parts.
- What is checked
- No Ducat decision is made at this stage. The next stage verifies the feed, price, time, and recovered signer addresses.
- If it fails
- If Chainlink is unavailable, the Ducat workflow has no new report. Bitcoin state does not change.
Trace one illustrative oracle cycle
These values are deliberately simple examples. Feed identifiers, signature thresholds, freshness windows, ladder spacing, confirmation targets, and deployment settings must come from the active versioned configuration rather than being invented here.
CRE accepts it only if the feed matches, at least two distinct configured signers recover correctly, and the signed observation time is within 300 seconds.
The minute cycle publishes all 866 ladder contracts before promoting the current snapshot.
The wallet computes the matching commit_hash and commits that contract's thold_hash in the vault leaf.
The new checked price is strictly below this vault's 90,000 threshold. Other ladder entries are irrelevant unless their hashes are also requested.
The workflow regenerates only this requested secret, verifies its commitment, and publishes its kind 1000 breach event.
The stack now satisfies this vault's committed liquidation leaf.
Bitcoin validity and Ducat protocol validity are related but distinct checks.
Inspect all seven stage cards first. This keeps prediction after explanation.
Required: inspect the exact implementationReport bytes, signature checks, hashes, keys, and operator controls. This material is needed for the explanation question.
What CRE receives from Chainlink
The captured code decodes a Data Streams v3 full report. It does not search arbitrary bytes for a plausible price.
- Solidity ABI
- A byte-layout convention for typed values. The decoder uses declared offsets and lengths instead of guessing where fields are.
- int192
- A signed integer stored in 192 bits. Signed means it can represent negative values, which this workflow rejects for BTC/USD.
- Keccak-256
- The 256-bit hash used to reconstruct the digest signed by the Chainlink report signers.
- Report context
- Three fixed 32-byte words included with the report blob when reconstructing the signed digest.
- uint32BE
- An unsigned 32-bit integer encoded most-significant byte first. BE means big-endian.
- HMAC-SHA256
- A keyed hash. Ducat uses the oracle secret and public commit_hash to derive a different threshold key for each contract.
- Event kind
- A Nostr numeric message type, such as 10000 for the current snapshot, 30000 for a threshold contract, or 1000 for a breach.
- Sighash
- The exact transaction digest a Bitcoin signature authorizes. Changing a covered transaction field changes this digest.
| Field | Format | Why it matters |
|---|---|---|
| reportContext[3] | Three 32-byte words | Part of the digest that each authorized signer attests to. |
| feedId | 32 bytes | Must exactly equal the configured BTC/USD feed identifier. |
| validFromTimestamp | Unsigned 32-bit seconds | States when the report becomes valid under the v3 report format. |
| observationsTimestamp | Unsigned 32-bit seconds | Signed observation time used for the five-minute freshness check. |
| nativeFee and linkFee | Unsigned 192-bit integers | Fee fields included in the signed report blob even though Ducat reads the price for this workflow. |
| expiresAt | Unsigned 32-bit seconds | Report expiry field included in the v3 signed payload. |
| price | Signed 192-bit integer with 18 implied decimals | BTC/USD value. The workflow rejects a non-positive or out-of-range result. |
| bid and ask | Signed 192-bit integers | Additional v3 price fields preserved in the report blob. Ducat's captured decoder selects the main price field. |
| rawRs, rawSs, rawVs | ECDSA signature parts | Used to recover distinct signer addresses for the report digest. |
- Decode the Solidity ABI envelope and the v3 report at fixed field positions.
- Hash the report blob, then hash it with all three report-context words using Keccak-256.
- Recover each secp256k1 signer address from its ECDSA signature.
- Require distinct recovered addresses, all in the configured signer set, to meet the configured threshold.
- Reject a feed mismatch, bad signature, missing observation time, price outside bounds, or more than 300 seconds of timestamp difference.
Passing these checks proves that enough configured Chainlink signers attested to this exact report payload.
What it cannot proveIt does not prove that BTC/USD is economically perfect. Chainlink's upstream data selection and aggregation remain an external assumption.
This synthetic teaching envelope contains three report-context words, the complete nine-word v3 report blob, and two r, s, and v signature parts. It is not a live market report, and its dummy signatures do not prove authorization.
fullReport 0x00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000039289090691a1df986e2e2e96670f6435b9a1f913d2306973842db65a9722000000000000000000000000000000000000000000000000000000006553f100000000000000000000000000000000000000000000000000000000006553f1c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006553f29000000000000000000000000000000000000000000000152d02c7e14af680000000000000000000000000000000000000000000000000152a4ce4323444f8000000000000000000000000000000000000000000000000152fb8ab9061a8080000000000000000000000000000000000000000000000000000000000000000000211111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222000000000000000000000000000000000000000000000000000000000000000233333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444| Decoded field | Example value | Decision |
|---|---|---|
| feedId | 0x00039289...a9722 | Matches captured testnet4 configuration |
| observationsTimestamp | 17:00:00 UTC | 200 seconds before server time, so within 300-second limit |
| price | 100000000000000000000000 | Divide by 10^18 to obtain 100,000.00 |
| signature arrays | 2 r words, 2 s words, and v bytes 00 01 | The envelope contains complete signature parts. These synthetic teaching values are not evidence of authorized signers. |
| expiresAt | 17:02:00 UTC | Signed field is decoded, but this captured Ducat path does not use it for the stated freshness gate |
Counting from word[0], which word must CRE read as the signed int192 BTC/USD price before dividing by 10^18?
How one threshold becomes a Bitcoin hashlock
The wallet and oracle independently serialize the same values. Any one-byte difference produces a different commitment and the wallet will not find a matching ladder event.
ceil(base price x 1.35 / selected rate)The selected rate is the vault's collateral-value-to-debt bucket at the base price. A rate of 1.50 means the collateral is worth 150 percent of the debt. The captured liquidation ratio of 1.35 means 135 percent. Multiplying the base price by 1.35 and dividing by the selected rate finds the BTC price where the ratio falls to that liquidation boundary. Captured testnet rates run from 1.35 to 10.00. These are Ducat configuration values, not Bitcoin rules.
oracle key || UTF-8 network || uint32BE base price || uint32BE base time || uint32BE threshold priceThe field order and integer encoding are fixed by the captured implementation.
BIP340 tagged hash with tag ducat/price_contract_commitThe tag hash is placed twice before the serialized commitment preimage.
HMAC-SHA256(oracle secret key, commit_hash)This 32-byte secret can be regenerated by the oracle without storing every ladder secret.
Hash160(threshold key)This 20-byte value is included in the vault's liquidation leaf.
BIP340 tagged hash of commit_hash || thold_hashThe tag is ducat/price_contract_id. The oracle signs this ID with Schnorr.
Change one input and watch every dependent value change
The secret below is a test-only teaching value. It is not an operational Ducat key.
90000921165d9559cdf66632513c565ae6ee0b4b8f2822a094378ddb01edddae6fb9a6923522c91ac8593739d607fac598515af9490a5d0fcf3b9ab4c480793ae5a1036b8d5bfea7bb90faf2a33382941ff4c7f41d56db338008eaadeae09375cf2f95b6eb0d0f9f002f360c31b926f51c1df88fff7ecFour different powers
- Held by
- Members of the configured Chainlink signer set
- Allows
- Sign the Data Streams report digest
- If compromised
- A threshold of compromised authorized signers can authenticate a forged report
- Relationship
- Distinct from every Ducat and Guardian key
- Held by
- The Ducat oracle workflow and its custody system
- Allows
- Derive threshold keys, sign contract IDs, and sign Nostr oracle events
- If compromised
- Can forge Ducat oracle state and reveal any derived threshold secret
- Relationship
- The captured code derives the Nostr public key and HMAC material from the same Ducat private key
- Held by
- Operator identity not established by the captured repository
- Allows
- Authorize adjust, status, quote, and evaluate requests to the CRE HTTP trigger
- If compromised
- Can choose requested hashes and invoke temporary adjustment controls in the dev workflow
- Relationship
- An EVM-style authorization key configured separately from the Ducat oracle signing key
- Held by
- One configured Guardian identity; its private signing topology may be single-party or threshold-based outside this SDK leaf
- Allows
- Sign the liquidation transaction sighash
- If compromised
- Can contribute authorization to a bad Ducat transaction but still cannot bypass the Bitcoin hashlock
- Relationship
- Distinct from Chainlink, oracle, and HTTP trigger keys
Which key can forge a Ducat oracle result?
Assume the attacker does not control Chainlink signers or a Guardian quorum. Which single compromised key could sign a false Ducat oracle event and disclose a derived threshold secret?
A dev operator can temporarily adjust the checked price
In the captured hmac-dev workflow, the same separately configured HTTP authorization key that can call evaluation can submit an adjustment percentage and duration. The workflow writes a signed kind 30078 control event using the Ducat oracle key, reads it before ladder creation or evaluation, and multiplies the checked Chainlink price by 1 + percentage / 100.
- The HTTP trigger verifies the configured EVM-style authorized caller.
- The request is limited to plus or minus 50 percent and at most five minutes.
- The workflow publishes a replaceable control event signed by the Ducat oracle key.
- The next cycle or evaluation verifies that event, applies the percentage, and rechecks price bounds.
- Clients see the adjusted base price in the signed snapshot, but the snapshot does not itself explain the operator's economic reason for changing it.
This dev control is a material operator power and a trust boundary. The captured repository also contained an alpha-mainnet target in the dev tree, while the audit recommended removing dev controls from mainnet builds.
Who is trusted for what?
| Participant | Responsibility | Do not assume |
|---|---|---|
| Chainlink DON | Produce and sign the configured market report. | Bitcoin verifies the price or that signatures guarantee perfect market accuracy. |
| Chainlink CRE | Execute the configured workflow across its runtime and aggregate workflow-node results. | CRE chooses Ducat thresholds or owns the Ducat oracle signing policy. |
| Ducat oracle operator and signing key | Configure the feed and signer set, publish a coherent ladder, and release the requested breached secrets on time. | Commitments remove operator honesty, configuration, key security, or availability assumptions. |
| Authorized evaluate caller | Choose which one to 500 thold_hash values the HTTP workflow evaluates. The captured repository does not identify the human or service holding this separate key. | Every unsafe vault is discovered and requested automatically, or that selective omission is prevented by Bitcoin. |
| Nostr relays | Store and return signed events. The captured workflow queries contract or breach kinds by h tag and pins the expected oracle author, then rechecks the author and NIP-01 Schnorr signature. | Relays decide the price, threshold, Bitcoin validity, or can make altered content pass the oracle signature check. |
| Ducat clients and validators | Verify oracle material, build transactions, and reconstruct confirmed protocol state. | Their database replaces the confirmed Bitcoin transition. |
| Guardians | Check Ducat policy and authorize the exact eligible transaction. | A valid preimage alone is sufficient to spend the vault. |
| Bitcoin full nodes | Verify the Taproot proof, script, preimage hash, signature, and transaction rules. | Nodes fetch Chainlink data or know what BTC/USD should be. |
Configuration and deployment status
These values must be read from the active versioned system. The lesson marks them instead of inventing permanent protocol constants.
Feed 0x00039289...a9722 on the Chainlink testnet Data Engine. The report uses v3 ABI fields and 18 implied price decimals.
Four configured signer addresses, threshold two, and require_report_verification set true. Signer membership can rotate with the Chainlink feed configuration.
Every minute at second zero; rates 1.35 through 10.00; step 0.01; liquidation value 1.35; 866 contracts in seven chunks; 30-minute ladder expiry.
Fixed v3 decoding, authorized distinct signer recovery, exact feed matching, and fail-closed verification exist in the captured source.
Reports over 300 seconds from signed observation time are rejected. Adjustment controls are bounded to plus or minus 50 percent for at most five minutes in this captured code.
The repository deployment table had empty workflow IDs and explicitly said this version was not running. No lesson should present these fixes as live production behavior without a newer deployment snapshot.
The old kind 10000 event remains when a new ladder cycle fails, but these repositories do not establish a universal downstream rejection rule once that snapshot becomes old. A production wallet or operator needs an explicit fail-closed age policy for new vault construction and evaluation. This lesson does not invent one.
The captured repository audit identified exposed historical signing material and required key rotation before real-funds use. The lesson does not treat a source-level signature implementation as proof of safe live key custody.
No universal confirmation count is created by this oracle. The active wallet, Guardian, mempool, and deployment policy must supply it.
The captured evaluateQuotes endpoint accepts one to 500 thold_hash values, but the inspected repositories do not identify the service that watches vaults, selects those hashes, holds the authorized HTTP key, or hands released secrets to a liquidator. A deployment must define and monitor that path; this lesson does not invent it.
Chainlink documents Data Streams as a signed, pull-based report produced by a decentralized oracle network. Exact data-vendor selection, aggregation parameters, and CRE workflow-node agreement are external Chainlink runtime behavior rather than code captured in the Ducat repositories.
The SDK leaf commits one Guardian public key. Whether it is one signer or a FROST group, its live quorum, key rotation, emergency policy, and the human authorization policy for dev price adjustment were not established in the captured deployment evidence.
Captured 3 August 2026 at 12:00 UTC
cre-hmac bd394c3763b4datastream/datastream.go: datastream.DecodeFullReport, DecodeV3Price, VerifyReportSigners; hmac-dev/price.go: fetchPrice; hmac-dev/handlers.go: unifiedOracleCycle, evaluateQuotes; hmac-dev/relay.go: fetchEventByDTag; hmac-dev/crypto.go: signNostrEventImplemented in source; workflow IDs empty and not proven deployed
protocol-sdk 96cfbe996f8fcrates/ducat-protocol/src/price_contract.rs: price_contract::new; crates/ducat-protocol/src/vault/output.rs: VaultOutput::to_txout; crates/ducat-protocol/src/script/liquidation.rs: create_liquidation_script; crates/ducat-protocol/src/vault/action/repo/validated.rs: RepoAction::validate; crates/ducat-protocol/src/vault/action/trim/validated.rs: TrimAction::validateImplemented in source and exercised by regtest code
v3 report format in captured adapterhttps://docs.chain.link/data-streams and https://docs.chain.link/creExternal service; signer configuration and runtime behavior can rotate
This lesson explains the captured implementation, not an asserted live production deployment. A newer code or deployment snapshot must trigger revalidation.
Who receives what, and what do they produce?
ReceivesMarket observations
ProducesA signed Data Streams report
ReceivesThe fullReport and signer configuration
ProducesA checked BTC/USD value or an error
ReceivesChecked price and ladder configuration
ProducesA complete signed snapshot and hidden threshold commitments
ReceivesThe current snapshot and matching threshold event
ProducesA vault leaf containing thold_hash and a Guardian key
ReceivesRequested thold_hash values and a new checked price
ProducesSigned breach events with the matching threshold keys
ReceivesThe exact repo or trim signing request
ProducesA Schnorr signature or a refusal
ReceivesThe final transaction and witness
ProducesA Bitcoin spend, then an accepted or rejected Ducat state transition
The report only starts an off-chain process. The workflow must verify it, a vault must already contain the matching commitment, the requested threshold must be evaluated, the secret must be released, a Guardian must sign the exact transaction, and Bitcoin must accept its complete Taproot witness.
The questions unlock after every required learning activity
Inspect all seven stages, solve all sixteen boundary failures, decode the report envelope, complete both commitment checks, distinguish the compromised key, and open the exact implementation section. Your progress is saved automatically.