Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Binding consumers

audience: ai

The oracle exposes a family of Stream<PriceTick> endpoints, one per supported token pair. A consumer coalition that wants oracle prices does three things, in order: reference the oracle in its own CoalitionConfig, compose a TicketValidator that admits the oracle’s Measurements, and subscribe to the streams it cares about. This chapter walks all three from the consumer side; the oracle operator does not participate in the handshake beyond publishing ORACLE.content_hash() and ORACLE_MEASUREMENTS.

Referencing the oracle

A consumer coalition (for instance, searcher-α from the web3 example) references the oracle through its own OrganismRef pointer:

use coalition::{OrganismRef, MeasurementsPin};

pub const ORACLE_CONSUMED: OrganismRef<'static> =
    OrganismRef {
        role:         "oracle",
        stable_id:    ORACLE_STABLE_ID,     // from the oracle's release page
        content_hash: Some(ORACLE_CONTENT_HASH), // pinned
    };

The reference is to the same organism the oracle operator publishes. The stable_id and content_hash come from the oracle operator’s release page (setup); nothing is negotiated out-of-band. If the consumer chooses to follow rotations, it pins only the stable_id and tracks content_hash updates through the retirement chain (see sustainability). If the consumer pins both, a Measurements rotation requires a consumer-side republish to remain bonded.

Composing the TicketValidator

The consumer’s TicketValidator admits the oracle’s Measurements in its TDX arm:

use mosaik::tickets::{TicketValidator, And, Or};
use mosaik::tee::tdx::AdmitsMeasurements;

pub fn oracle_consumer_validator()
    -> TicketValidator<'static>
{
    TicketValidator::compose(
        // Consumer's own ACL for who runs this searcher.
        searcher_alpha::SELF_ACL,
        // Admits any peer whose self-quote matches the
        // oracle's published Measurements.
        AdmitsMeasurements::equal(ORACLE_MEASUREMENTS),
    )
}

AdmitsMeasurements::equal is the simplest composition: the consumer accepts exactly the declared Measurements set and nothing else. A consumer that is willing to follow staged rotations may use AdmitsMeasurements::in_set(&[MR_OLD, MR_NEW]) across a rotation window; see sustainability.

First subscription

The stream endpoint for a given pair is addressed by the oracle’s stable_id and the pair identifier:

use mosaik::streams::{StreamId, Subscription};

let network: Arc<Network> = /* from coalition bootstrap */;

let pair = TokenPair::new("USDC", "ETH");
let stream_id = StreamId::derive(
    ORACLE_CONSUMED.stable_id(),
    &pair.preimage(),
    &PRICE_TICK_SCHEMA_V1,
);

let mut subscription: Subscription<PriceTick> =
    network.subscribe(stream_id, oracle_consumer_validator()).await?;

while let Some(tick) = subscription.next().await {
    // PriceTick::verify re-checks the oracle's quote
    // on the stream's bond plus the commit's schema.
    tick.verify(&ORACLE_MEASUREMENTS)?;
    handle_tick(tick);
}

Two properties of the subscription are worth naming:

  • Admission is one-sided. The oracle’s bond ACL admits any subscriber; the consumer’s validator admits any peer whose Measurements match. The bond either forms or it does not; there is no handshake step the oracle operator participates in.
  • PriceTick::verify re-checks the oracle’s self-quote that was folded into the bond at subscription time. It does not re-quote per tick; TDX quotes are bond-level. Per-tick tamper detection is covered by the oracle’s signing key (see publication).

What the consumer can and cannot assume

  • Prices are post-aggregation. The tick is what the oracle’s declared AggregationPolicyId produced after reading its sources (aggregation). The consumer does not see per-source quotes; only the aggregate, the source_set_digest, and the staleness flag.
  • Cadence is nominal. The oracle publishes at the nominal cadence when all sources are fresh and quorate. When a source degrades, the oracle may skip a tick or publish a stale-flagged tick; both are covered under aggregation.
  • Trust reduces to one set of bytes. The consumer’s trust in the oracle is the admission of ORACLE_MEASUREMENTS. No reputation layer, no chain of signers, no multi-party commit. A consumer who wants reputation-gated oracle admission composes that on top — but the minimal shape does not require it.

Forward

Chapter 3 (source-reads) inverts the viewpoint: what the oracle itself reads, inside its enclave, to produce the ticks the consumer just subscribed to.