Skip to main content

ferrum_interfaces/vnext/
oracle.rs

1use serde::de::DeserializeOwned;
2use serde::{Deserialize, Serialize};
3use sha2::{Digest, Sha256};
4use std::collections::{BTreeMap, BTreeSet};
5use std::fmt;
6
7use super::{
8    AttributeId, CapabilityCatalog, ContractVersion, DimensionConstraint, ElementType,
9    OperationContract, OperationDescriptor, OperationId, OracleSpec, SemanticValue, VNextError,
10    MAX_REFERENCE_ORACLE_DEPTH,
11};
12
13/// Maximum rank of one canonical host tensor passed through an oracle.
14pub const MAX_ORACLE_TENSOR_RANK: usize = 16;
15/// Maximum logical elements in one canonical host tensor.
16pub const MAX_ORACLE_TENSOR_ELEMENTS: usize = 16 * 1024 * 1024;
17/// Maximum encoded bytes in one canonical host tensor.
18pub const MAX_ORACLE_TENSOR_BYTES: usize = 64 * 1024 * 1024;
19/// Maximum number of input or output tensors in one oracle call.
20pub const MAX_ORACLE_TENSORS: usize = 64;
21/// Maximum cumulative tensor bytes in an oracle request or result.
22pub const MAX_ORACLE_CALL_BYTES: usize = 64 * 1024 * 1024;
23/// Maximum number of typed attributes in an oracle request.
24pub const MAX_ORACLE_ATTRIBUTES: usize = 256;
25/// Maximum canonical JSON bytes occupied by oracle request attributes.
26pub const MAX_ORACLE_ATTRIBUTE_BYTES: usize = 1024 * 1024;
27/// Maximum JSON wire bytes accepted before decoding any unvalidated oracle type.
28pub const MAX_ORACLE_WIRE_BYTES: usize = 16 * 1024 * 1024;
29
30fn invalid_oracle(reason: impl Into<String>) -> VNextError {
31    VNextError::InvalidExecutionPlan {
32        reason: reason.into(),
33    }
34}
35
36/// Validates the raw availability boundary shared by all oracle wire decoders.
37pub fn validate_oracle_wire_byte_length(byte_length: usize) -> Result<(), VNextError> {
38    if byte_length > MAX_ORACLE_WIRE_BYTES {
39        return Err(invalid_oracle(format!(
40            "oracle wire bytes exceed {MAX_ORACLE_WIRE_BYTES}"
41        )));
42    }
43    Ok(())
44}
45
46fn decode_untrusted_oracle_wire<T: DeserializeOwned>(
47    bytes: &[u8],
48    context: &'static str,
49) -> Result<T, VNextError> {
50    validate_oracle_wire_byte_length(bytes.len())?;
51    serde_json::from_slice(bytes).map_err(|error| VNextError::Serialization {
52        context,
53        message: error.to_string(),
54    })
55}
56
57fn is_canonical_sha256(value: &str) -> bool {
58    value.len() == 64
59        && value
60            .bytes()
61            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
62}
63
64fn validate_oracle_identity(value: &str) -> Result<(), VNextError> {
65    if value.is_empty() || value.len() > 160 {
66        return Err(VNextError::InvalidIdentity {
67            kind: "operation oracle",
68            value: value.to_owned(),
69            reason: "identity must contain between 1 and 160 bytes",
70        });
71    }
72    if !value.bytes().all(|byte| {
73        byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-' | b':' | b'/')
74    }) {
75        return Err(VNextError::InvalidIdentity {
76            kind: "operation oracle",
77            value: value.to_owned(),
78            reason: "identity contains a non-portable character",
79        });
80    }
81    Ok(())
82}
83
84/// Stable identity of one checked-in oracle implementation contract.
85#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
86#[serde(try_from = "String", into = "String")]
87pub struct OperationOracleId(String);
88
89impl OperationOracleId {
90    pub fn new(value: impl Into<String>) -> Result<Self, VNextError> {
91        let value = value.into();
92        validate_oracle_identity(&value)?;
93        Ok(Self(value))
94    }
95
96    pub fn as_str(&self) -> &str {
97        &self.0
98    }
99}
100
101impl TryFrom<String> for OperationOracleId {
102    type Error = VNextError;
103
104    fn try_from(value: String) -> Result<Self, Self::Error> {
105        Self::new(value)
106    }
107}
108
109impl From<OperationOracleId> for String {
110    fn from(value: OperationOracleId) -> Self {
111        value.0
112    }
113}
114
115impl fmt::Display for OperationOracleId {
116    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
117        formatter.write_str(&self.0)
118    }
119}
120
121/// Trusted, Serialize-only identity of an executable operation oracle.
122#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
123pub struct OperationOracleDescriptor {
124    oracle_id: OperationOracleId,
125    version: ContractVersion,
126    implementation_fingerprint: String,
127    operation_id: OperationId,
128    operation_fingerprint: String,
129}
130
131/// Untrusted wire shape for an oracle descriptor. It is never executable until
132/// reconstructed and matched to a registry-owned trait object.
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct UnvalidatedOperationOracleDescriptor {
135    pub oracle_id: OperationOracleId,
136    pub version: ContractVersion,
137    pub implementation_fingerprint: String,
138    pub operation_id: OperationId,
139    pub operation_fingerprint: String,
140}
141
142#[derive(Deserialize)]
143#[serde(deny_unknown_fields)]
144struct OperationOracleDescriptorWire {
145    oracle_id: OperationOracleId,
146    version: ContractVersion,
147    implementation_fingerprint: String,
148    operation_id: OperationId,
149    operation_fingerprint: String,
150}
151
152impl UnvalidatedOperationOracleDescriptor {
153    pub fn revalidate(self) -> Result<OperationOracleDescriptor, VNextError> {
154        OperationOracleDescriptor::new(
155            self.oracle_id,
156            self.version,
157            self.implementation_fingerprint,
158            self.operation_id,
159            self.operation_fingerprint,
160        )
161    }
162}
163
164impl OperationOracleDescriptor {
165    pub fn new(
166        oracle_id: OperationOracleId,
167        version: ContractVersion,
168        implementation_fingerprint: impl Into<String>,
169        operation_id: OperationId,
170        operation_fingerprint: impl Into<String>,
171    ) -> Result<Self, VNextError> {
172        let implementation_fingerprint = implementation_fingerprint.into();
173        let operation_fingerprint = operation_fingerprint.into();
174        if version.major == 0 {
175            return Err(invalid_oracle(format!(
176                "oracle `{oracle_id}` has a zero contract major version"
177            )));
178        }
179        if !is_canonical_sha256(&implementation_fingerprint) {
180            return Err(invalid_oracle(format!(
181                "oracle `{oracle_id}` implementation fingerprint is not canonical SHA-256"
182            )));
183        }
184        if !is_canonical_sha256(&operation_fingerprint) {
185            return Err(invalid_oracle(format!(
186                "oracle `{oracle_id}` operation fingerprint is not canonical SHA-256"
187            )));
188        }
189        Ok(Self {
190            oracle_id,
191            version,
192            implementation_fingerprint,
193            operation_id,
194            operation_fingerprint,
195        })
196    }
197
198    pub fn oracle_id(&self) -> &OperationOracleId {
199        &self.oracle_id
200    }
201
202    pub const fn version(&self) -> ContractVersion {
203        self.version
204    }
205
206    pub fn implementation_fingerprint(&self) -> &str {
207        &self.implementation_fingerprint
208    }
209
210    pub fn operation_id(&self) -> &OperationId {
211        &self.operation_id
212    }
213
214    pub fn operation_fingerprint(&self) -> &str {
215        &self.operation_fingerprint
216    }
217
218    pub fn fingerprint(&self) -> Result<String, VNextError> {
219        let bytes = serde_json::to_vec(self).map_err(|error| VNextError::Serialization {
220            context: "serialize operation oracle descriptor",
221            message: error.to_string(),
222        })?;
223        Ok(format!("{:x}", Sha256::digest(bytes)))
224    }
225
226    pub fn decode_untrusted(
227        bytes: &[u8],
228    ) -> Result<UnvalidatedOperationOracleDescriptor, VNextError> {
229        let wire: OperationOracleDescriptorWire =
230            decode_untrusted_oracle_wire(bytes, "decode untrusted operation oracle descriptor")?;
231        Ok(UnvalidatedOperationOracleDescriptor {
232            oracle_id: wire.oracle_id,
233            version: wire.version,
234            implementation_fingerprint: wire.implementation_fingerprint,
235            operation_id: wire.operation_id,
236            operation_fingerprint: wire.operation_fingerprint,
237        })
238    }
239}
240
241/// Canonical row-major, little-endian host tensor used by all oracles. Float
242/// encodings must be finite; exact comparison is bit-exact, including distinct
243/// positive and negative zero encodings.
244#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
245pub struct OracleTensor {
246    dimensions: Vec<u64>,
247    element_type: ElementType,
248    bytes: Vec<u8>,
249}
250
251/// Untrusted wire tensor. `revalidate` checks all rank, extent, byte and scalar
252/// encoding bounds before producing a trusted host tensor.
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub struct UnvalidatedOracleTensor {
255    pub dimensions: Vec<u64>,
256    pub element_type: ElementType,
257    pub bytes: Vec<u8>,
258}
259
260#[derive(Deserialize)]
261#[serde(deny_unknown_fields)]
262struct OracleTensorWire {
263    dimensions: Vec<u64>,
264    element_type: ElementType,
265    bytes: Vec<u8>,
266}
267
268impl UnvalidatedOracleTensor {
269    pub fn revalidate(self) -> Result<OracleTensor, VNextError> {
270        OracleTensor::new(self.dimensions, self.element_type, self.bytes)
271    }
272}
273
274impl OracleTensor {
275    pub fn new(
276        dimensions: Vec<u64>,
277        element_type: ElementType,
278        bytes: Vec<u8>,
279    ) -> Result<Self, VNextError> {
280        if dimensions.len() > MAX_ORACLE_TENSOR_RANK {
281            return Err(invalid_oracle(format!(
282                "oracle tensor rank exceeds {MAX_ORACLE_TENSOR_RANK}"
283            )));
284        }
285        if dimensions.iter().any(|extent| *extent == 0) {
286            return Err(invalid_oracle("oracle tensor has a zero extent"));
287        }
288        let elements = dimensions.iter().try_fold(1usize, |elements, extent| {
289            let extent = usize::try_from(*extent)
290                .map_err(|_| invalid_oracle("oracle tensor extent does not fit usize"))?;
291            elements
292                .checked_mul(extent)
293                .ok_or_else(|| invalid_oracle("oracle tensor element count overflows usize"))
294        })?;
295        if elements > MAX_ORACLE_TENSOR_ELEMENTS {
296            return Err(invalid_oracle(format!(
297                "oracle tensor elements exceed {MAX_ORACLE_TENSOR_ELEMENTS}"
298            )));
299        }
300        let element_bytes = usize::try_from(element_type.size_bytes())
301            .map_err(|_| invalid_oracle("oracle tensor element width does not fit usize"))?;
302        let expected_bytes = elements
303            .checked_mul(element_bytes)
304            .ok_or_else(|| invalid_oracle("oracle tensor byte count overflows usize"))?;
305        if expected_bytes > MAX_ORACLE_TENSOR_BYTES || bytes.len() != expected_bytes {
306            return Err(invalid_oracle(format!(
307                "oracle tensor requires exactly {expected_bytes} bytes within the {MAX_ORACLE_TENSOR_BYTES} byte limit"
308            )));
309        }
310        validate_scalar_encodings(element_type, &bytes)?;
311        Ok(Self {
312            dimensions,
313            element_type,
314            bytes,
315        })
316    }
317
318    pub fn dimensions(&self) -> &[u64] {
319        &self.dimensions
320    }
321
322    pub const fn element_type(&self) -> ElementType {
323        self.element_type
324    }
325
326    pub fn bytes(&self) -> &[u8] {
327        &self.bytes
328    }
329
330    pub fn element_count(&self) -> usize {
331        self.bytes.len() / self.element_type.size_bytes() as usize
332    }
333
334    pub fn decode_untrusted(bytes: &[u8]) -> Result<UnvalidatedOracleTensor, VNextError> {
335        let wire: OracleTensorWire =
336            decode_untrusted_oracle_wire(bytes, "decode untrusted oracle tensor")?;
337        Ok(UnvalidatedOracleTensor {
338            dimensions: wire.dimensions,
339            element_type: wire.element_type,
340            bytes: wire.bytes,
341        })
342    }
343
344    fn numeric_value(&self, index: usize) -> Result<f64, VNextError> {
345        if index >= self.element_count() {
346            return Err(invalid_oracle(
347                "oracle tensor element index is out of bounds",
348            ));
349        }
350        let width = self.element_type.size_bytes() as usize;
351        let offset = index * width;
352        let bytes = &self.bytes[offset..offset + width];
353        let value = match self.element_type {
354            ElementType::Bool => {
355                return Err(invalid_oracle(
356                    "tolerance comparison is not defined for boolean tensors",
357                ));
358            }
359            ElementType::U8 => f64::from(bytes[0]),
360            ElementType::I8 => f64::from(i8::from_le_bytes([bytes[0]])),
361            ElementType::U32 => f64::from(u32::from_le_bytes(
362                bytes.try_into().expect("validated width"),
363            )),
364            ElementType::I32 => f64::from(i32::from_le_bytes(
365                bytes.try_into().expect("validated width"),
366            )),
367            ElementType::F16 => f16_to_f64(u16::from_le_bytes(
368                bytes.try_into().expect("validated width"),
369            )),
370            ElementType::Bf16 => f64::from(f32::from_bits(
371                u32::from(u16::from_le_bytes(
372                    bytes.try_into().expect("validated width"),
373                )) << 16,
374            )),
375            ElementType::F32 => f64::from(f32::from_le_bytes(
376                bytes.try_into().expect("validated width"),
377            )),
378        };
379        Ok(value)
380    }
381}
382
383fn validate_scalar_encodings(element_type: ElementType, bytes: &[u8]) -> Result<(), VNextError> {
384    match element_type {
385        ElementType::Bool if bytes.iter().any(|value| !matches!(value, 0 | 1)) => Err(
386            invalid_oracle("canonical boolean oracle tensors contain only 0 or 1"),
387        ),
388        ElementType::F16
389            if bytes.chunks_exact(2).any(|bytes| {
390                u16::from_le_bytes(bytes.try_into().expect("two-byte chunk")) & 0x7c00 == 0x7c00
391            }) =>
392        {
393            Err(invalid_oracle(
394                "oracle tensors reject non-finite f16 values",
395            ))
396        }
397        ElementType::Bf16
398            if bytes.chunks_exact(2).any(|bytes| {
399                u16::from_le_bytes(bytes.try_into().expect("two-byte chunk")) & 0x7f80 == 0x7f80
400            }) =>
401        {
402            Err(invalid_oracle(
403                "oracle tensors reject non-finite bf16 values",
404            ))
405        }
406        ElementType::F32
407            if bytes.chunks_exact(4).any(|bytes| {
408                !f32::from_le_bytes(bytes.try_into().expect("four-byte chunk")).is_finite()
409            }) =>
410        {
411            Err(invalid_oracle(
412                "oracle tensors reject non-finite f32 values",
413            ))
414        }
415        _ => Ok(()),
416    }
417}
418
419fn f16_to_f64(bits: u16) -> f64 {
420    let sign = if bits & 0x8000 == 0 { 1.0 } else { -1.0 };
421    let exponent = i32::from((bits >> 10) & 0x1f);
422    let fraction = f64::from(bits & 0x03ff) / 1024.0;
423    if exponent == 0 {
424        sign * fraction * 2.0_f64.powi(-14)
425    } else {
426        sign * (1.0 + fraction) * 2.0_f64.powi(exponent - 15)
427    }
428}
429
430/// Canonical, bounded request delivered to an `OperationOracle`.
431#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
432pub struct OperationOracleRequest {
433    operation_id: OperationId,
434    operation_fingerprint: String,
435    inputs: Vec<OracleTensor>,
436    attributes: BTreeMap<AttributeId, SemanticValue>,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq)]
440pub struct UnvalidatedOperationOracleRequest {
441    pub operation_id: OperationId,
442    pub operation_fingerprint: String,
443    pub inputs: Vec<UnvalidatedOracleTensor>,
444    pub attributes: BTreeMap<AttributeId, SemanticValue>,
445}
446
447#[derive(Deserialize)]
448#[serde(deny_unknown_fields)]
449struct OperationOracleRequestWire {
450    operation_id: OperationId,
451    operation_fingerprint: String,
452    inputs: Vec<OracleTensorWire>,
453    attributes: BTreeMap<AttributeId, SemanticValue>,
454}
455
456impl UnvalidatedOperationOracleRequest {
457    pub fn revalidate(self) -> Result<OperationOracleRequest, VNextError> {
458        OperationOracleRequest::new(
459            self.operation_id,
460            self.operation_fingerprint,
461            self.inputs
462                .into_iter()
463                .map(UnvalidatedOracleTensor::revalidate)
464                .collect::<Result<Vec<_>, _>>()?,
465            self.attributes,
466        )
467    }
468}
469
470impl OperationOracleRequest {
471    pub fn new(
472        operation_id: OperationId,
473        operation_fingerprint: impl Into<String>,
474        inputs: Vec<OracleTensor>,
475        attributes: BTreeMap<AttributeId, SemanticValue>,
476    ) -> Result<Self, VNextError> {
477        let operation_fingerprint = operation_fingerprint.into();
478        if !is_canonical_sha256(&operation_fingerprint) {
479            return Err(invalid_oracle(
480                "oracle request operation fingerprint is not canonical SHA-256",
481            ));
482        }
483        validate_tensor_collection("oracle request inputs", &inputs, false)?;
484        validate_oracle_attributes(&attributes)?;
485        Ok(Self {
486            operation_id,
487            operation_fingerprint,
488            inputs,
489            attributes,
490        })
491    }
492
493    pub fn operation_id(&self) -> &OperationId {
494        &self.operation_id
495    }
496
497    pub fn operation_fingerprint(&self) -> &str {
498        &self.operation_fingerprint
499    }
500
501    pub fn inputs(&self) -> &[OracleTensor] {
502        &self.inputs
503    }
504
505    pub fn attributes(&self) -> &BTreeMap<AttributeId, SemanticValue> {
506        &self.attributes
507    }
508
509    pub fn decode_untrusted(bytes: &[u8]) -> Result<UnvalidatedOperationOracleRequest, VNextError> {
510        let wire: OperationOracleRequestWire =
511            decode_untrusted_oracle_wire(bytes, "decode untrusted operation oracle request")?;
512        Ok(UnvalidatedOperationOracleRequest {
513            operation_id: wire.operation_id,
514            operation_fingerprint: wire.operation_fingerprint,
515            inputs: wire
516                .inputs
517                .into_iter()
518                .map(|tensor| UnvalidatedOracleTensor {
519                    dimensions: tensor.dimensions,
520                    element_type: tensor.element_type,
521                    bytes: tensor.bytes,
522                })
523                .collect(),
524            attributes: wire.attributes,
525        })
526    }
527}
528
529/// Canonical, bounded outputs produced by an oracle implementation.
530#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
531pub struct OperationOracleResult {
532    outputs: Vec<OracleTensor>,
533}
534
535#[derive(Debug, Clone, PartialEq, Eq)]
536pub struct UnvalidatedOperationOracleResult {
537    pub outputs: Vec<UnvalidatedOracleTensor>,
538}
539
540#[derive(Deserialize)]
541#[serde(deny_unknown_fields)]
542struct OperationOracleResultWire {
543    outputs: Vec<OracleTensorWire>,
544}
545
546impl UnvalidatedOperationOracleResult {
547    pub fn revalidate(self) -> Result<OperationOracleResult, VNextError> {
548        OperationOracleResult::new(
549            self.outputs
550                .into_iter()
551                .map(UnvalidatedOracleTensor::revalidate)
552                .collect::<Result<Vec<_>, _>>()?,
553        )
554    }
555}
556
557impl OperationOracleResult {
558    pub fn new(outputs: Vec<OracleTensor>) -> Result<Self, VNextError> {
559        validate_tensor_collection("oracle result outputs", &outputs, true)?;
560        Ok(Self { outputs })
561    }
562
563    pub fn outputs(&self) -> &[OracleTensor] {
564        &self.outputs
565    }
566
567    pub fn decode_untrusted(bytes: &[u8]) -> Result<UnvalidatedOperationOracleResult, VNextError> {
568        let wire: OperationOracleResultWire =
569            decode_untrusted_oracle_wire(bytes, "decode untrusted operation oracle result")?;
570        Ok(UnvalidatedOperationOracleResult {
571            outputs: wire
572                .outputs
573                .into_iter()
574                .map(|tensor| UnvalidatedOracleTensor {
575                    dimensions: tensor.dimensions,
576                    element_type: tensor.element_type,
577                    bytes: tensor.bytes,
578                })
579                .collect(),
580        })
581    }
582}
583
584fn validate_tensor_collection(
585    context: &str,
586    tensors: &[OracleTensor],
587    require_nonempty: bool,
588) -> Result<(), VNextError> {
589    if tensors.len() > MAX_ORACLE_TENSORS || (require_nonempty && tensors.is_empty()) {
590        return Err(invalid_oracle(format!(
591            "{context} must contain {} to {MAX_ORACLE_TENSORS} tensors",
592            usize::from(require_nonempty)
593        )));
594    }
595    let total_bytes = tensors.iter().try_fold(0usize, |total, tensor| {
596        total
597            .checked_add(tensor.bytes.len())
598            .ok_or_else(|| invalid_oracle(format!("{context} byte count overflows usize")))
599    })?;
600    if total_bytes > MAX_ORACLE_CALL_BYTES {
601        return Err(invalid_oracle(format!(
602            "{context} exceeds {MAX_ORACLE_CALL_BYTES} cumulative bytes"
603        )));
604    }
605    Ok(())
606}
607
608fn validate_oracle_attributes(
609    attributes: &BTreeMap<AttributeId, SemanticValue>,
610) -> Result<(), VNextError> {
611    if attributes.len() > MAX_ORACLE_ATTRIBUTES {
612        return Err(invalid_oracle(format!(
613            "oracle request exceeds {MAX_ORACLE_ATTRIBUTES} attributes"
614        )));
615    }
616    for value in attributes.values() {
617        value.validate("oracle request attributes")?;
618    }
619    let bytes = serde_json::to_vec(attributes).map_err(|error| VNextError::Serialization {
620        context: "serialize operation oracle attributes",
621        message: error.to_string(),
622    })?;
623    if bytes.len() > MAX_ORACLE_ATTRIBUTE_BYTES {
624        return Err(invalid_oracle(format!(
625            "oracle request attributes exceed {MAX_ORACLE_ATTRIBUTE_BYTES} canonical bytes"
626        )));
627    }
628    Ok(())
629}
630
631/// Object-safe executable correctness oracle. Runtime callers receive a
632/// registry-bound handle and never supply an oracle implementation per call.
633pub trait OperationOracle: Send + Sync {
634    fn descriptor(&self) -> &OperationOracleDescriptor;
635
636    fn invoke(&self, request: &OperationOracleRequest)
637        -> Result<OperationOracleResult, VNextError>;
638}
639
640/// Composition-root registration that independently anchors the expected
641/// descriptor before an implementation can enter the trusted registry.
642pub struct OperationOracleRegistration {
643    expected_descriptor: OperationOracleDescriptor,
644    oracle: Box<dyn OperationOracle>,
645}
646
647impl OperationOracleRegistration {
648    pub fn new(
649        expected_descriptor: OperationOracleDescriptor,
650        oracle: Box<dyn OperationOracle>,
651    ) -> Result<Self, VNextError> {
652        if oracle.descriptor() != &expected_descriptor {
653            return Err(invalid_oracle(
654                "operation oracle implementation differs from its trusted registration descriptor",
655            ));
656        }
657        Ok(Self {
658            expected_descriptor,
659            oracle,
660        })
661    }
662
663    pub fn descriptor(&self) -> &OperationOracleDescriptor {
664        &self.expected_descriptor
665    }
666}
667
668struct RegisteredOracle {
669    descriptor: OperationOracleDescriptor,
670    oracle: Box<dyn OperationOracle>,
671}
672
673/// Trusted concrete registry that owns contracts and exact oracle objects for
674/// the lifetime of every bound invocation handle.
675pub struct OperationOracleRegistry {
676    catalog_fingerprint: String,
677    operations: BTreeMap<OperationId, OperationDescriptor>,
678    contracts: BTreeMap<OperationId, Box<dyn OperationContract>>,
679    terminal_operations: BTreeMap<OperationId, OperationId>,
680    oracles: BTreeMap<OperationId, RegisteredOracle>,
681}
682
683impl OperationOracleRegistry {
684    pub fn new(
685        catalog: &CapabilityCatalog,
686        contracts: Vec<Box<dyn OperationContract>>,
687        registrations: Vec<OperationOracleRegistration>,
688    ) -> Result<Self, VNextError> {
689        let operations = catalog.operations().clone();
690        let mut contract_map = BTreeMap::new();
691        for contract in contracts {
692            let descriptor = contract.descriptor();
693            descriptor.validate()?;
694            let catalog_descriptor = operations.get(&descriptor.id).ok_or_else(|| {
695                invalid_oracle(format!(
696                    "oracle registry contract `{}` is absent from the capability catalog",
697                    descriptor.id
698                ))
699            })?;
700            if descriptor != catalog_descriptor
701                || descriptor.fingerprint()? != catalog_descriptor.fingerprint()?
702            {
703                return Err(invalid_oracle(format!(
704                    "oracle registry contract `{}` differs from the capability catalog",
705                    descriptor.id
706                )));
707            }
708            contract.validate_signature(&catalog_descriptor.inputs, &catalog_descriptor.outputs)?;
709            let operation_id = descriptor.id.clone();
710            if contract_map
711                .insert(operation_id.clone(), contract)
712                .is_some()
713            {
714                return Err(invalid_oracle(format!(
715                    "oracle registry has duplicate contract `{operation_id}`"
716                )));
717            }
718        }
719        if contract_map.keys().collect::<BTreeSet<_>>()
720            != operations.keys().collect::<BTreeSet<_>>()
721        {
722            return Err(invalid_oracle(
723                "oracle registry requires exactly one contract for every catalog operation",
724            ));
725        }
726
727        let terminal_operations = resolve_terminal_operations(&operations)?;
728        let terminal_ids = terminal_operations
729            .values()
730            .cloned()
731            .collect::<BTreeSet<_>>();
732        let mut oracle_ids = BTreeSet::new();
733        let mut oracle_map = BTreeMap::new();
734        for registration in registrations {
735            let OperationOracleRegistration {
736                expected_descriptor,
737                oracle,
738            } = registration;
739            if oracle.descriptor() != &expected_descriptor {
740                return Err(invalid_oracle(
741                    "operation oracle descriptor changed after trusted registration",
742                ));
743            }
744            let operation = operations
745                .get(expected_descriptor.operation_id())
746                .ok_or_else(|| {
747                    invalid_oracle(format!(
748                        "oracle `{}` targets an operation absent from the capability catalog",
749                        expected_descriptor.oracle_id()
750                    ))
751                })?;
752            if matches!(operation.oracle, OracleSpec::ReferenceOperation { .. }) {
753                return Err(invalid_oracle(format!(
754                    "reference operation `{}` cannot register a direct oracle",
755                    operation.id
756                )));
757            }
758            if expected_descriptor.operation_fingerprint() != operation.fingerprint()? {
759                return Err(invalid_oracle(format!(
760                    "oracle `{}` operation fingerprint differs from `{}`",
761                    expected_descriptor.oracle_id(),
762                    operation.id
763                )));
764            }
765            if !oracle_ids.insert(expected_descriptor.oracle_id().clone()) {
766                return Err(invalid_oracle(format!(
767                    "oracle registry has duplicate identity `{}`",
768                    expected_descriptor.oracle_id()
769                )));
770            }
771            let operation_id = operation.id.clone();
772            if oracle_map
773                .insert(
774                    operation_id.clone(),
775                    RegisteredOracle {
776                        descriptor: expected_descriptor,
777                        oracle,
778                    },
779                )
780                .is_some()
781            {
782                return Err(invalid_oracle(format!(
783                    "terminal operation `{operation_id}` has multiple oracles"
784                )));
785            }
786        }
787        if oracle_map.keys().cloned().collect::<BTreeSet<_>>() != terminal_ids {
788            return Err(invalid_oracle(
789                "every terminal non-reference operation must have exactly one oracle",
790            ));
791        }
792
793        Ok(Self {
794            catalog_fingerprint: catalog.fingerprint()?,
795            operations,
796            contracts: contract_map,
797            terminal_operations,
798            oracles: oracle_map,
799        })
800    }
801
802    pub fn catalog_fingerprint(&self) -> &str {
803        &self.catalog_fingerprint
804    }
805
806    pub fn contract(
807        &self,
808        operation_id: &OperationId,
809    ) -> Result<&dyn OperationContract, VNextError> {
810        self.contracts
811            .get(operation_id)
812            .map(Box::as_ref)
813            .ok_or_else(|| invalid_oracle(format!("operation `{operation_id}` is not registered")))
814    }
815
816    pub fn bind<'registry>(
817        &'registry self,
818        operation_id: &OperationId,
819    ) -> Result<BoundOperationOracle<'registry>, VNextError> {
820        let requested_operation = self.operations.get(operation_id).ok_or_else(|| {
821            invalid_oracle(format!("operation `{operation_id}` is not registered"))
822        })?;
823        let terminal_id = self.terminal_operations.get(operation_id).ok_or_else(|| {
824            invalid_oracle(format!(
825                "operation `{operation_id}` has no validated terminal oracle"
826            ))
827        })?;
828        let terminal_operation = self
829            .operations
830            .get(terminal_id)
831            .ok_or_else(|| invalid_oracle("validated terminal operation disappeared"))?;
832        let registered = self
833            .oracles
834            .get(terminal_id)
835            .ok_or_else(|| invalid_oracle("validated terminal oracle disappeared"))?;
836        if registered.oracle.descriptor() != &registered.descriptor {
837            return Err(invalid_oracle(
838                "registered oracle descriptor changed before binding",
839            ));
840        }
841        Ok(BoundOperationOracle {
842            requested_operation,
843            terminal_operation,
844            registered,
845        })
846    }
847}
848
849fn resolve_terminal_operations(
850    operations: &BTreeMap<OperationId, OperationDescriptor>,
851) -> Result<BTreeMap<OperationId, OperationId>, VNextError> {
852    let mut resolved = BTreeMap::new();
853    for (root_id, root) in operations {
854        let mut current = root;
855        let mut visited = BTreeSet::new();
856        for _ in 0..MAX_REFERENCE_ORACLE_DEPTH {
857            if !visited.insert(current.id.clone()) {
858                return Err(invalid_oracle(format!(
859                    "reference oracle chain from `{root_id}` contains a cycle"
860                )));
861            }
862            let OracleSpec::ReferenceOperation {
863                operation_id,
864                version,
865            } = &current.oracle
866            else {
867                resolved.insert(root_id.clone(), current.id.clone());
868                break;
869            };
870            let reference = operations.get(operation_id).ok_or_else(|| {
871                invalid_oracle(format!(
872                    "reference oracle `{operation_id}` for `{}` is missing",
873                    current.id
874                ))
875            })?;
876            if !reference.version.satisfies(*version)
877                || current.inputs != reference.inputs
878                || current.outputs != reference.outputs
879                || current.attributes != reference.attributes
880            {
881                return Err(invalid_oracle(format!(
882                    "reference oracle `{operation_id}` is incompatible with `{}`",
883                    current.id
884                )));
885            }
886            current = reference;
887        }
888        if !resolved.contains_key(root_id) {
889            return Err(invalid_oracle(format!(
890                "reference oracle chain from `{root_id}` exceeds depth {MAX_REFERENCE_ORACLE_DEPTH}"
891            )));
892        }
893    }
894    Ok(resolved)
895}
896
897/// Non-cloneable authority borrowed from one registry-owned oracle object.
898pub struct BoundOperationOracle<'registry> {
899    requested_operation: &'registry OperationDescriptor,
900    terminal_operation: &'registry OperationDescriptor,
901    registered: &'registry RegisteredOracle,
902}
903
904impl BoundOperationOracle<'_> {
905    pub fn requested_operation_id(&self) -> &OperationId {
906        &self.requested_operation.id
907    }
908
909    pub fn terminal_operation_id(&self) -> &OperationId {
910        &self.terminal_operation.id
911    }
912
913    pub fn descriptor(&self) -> &OperationOracleDescriptor {
914        &self.registered.descriptor
915    }
916
917    pub fn comparison_policy(&self) -> &OracleSpec {
918        &self.terminal_operation.oracle
919    }
920
921    pub fn invoke(
922        &self,
923        inputs: Vec<OracleTensor>,
924        attributes: BTreeMap<AttributeId, SemanticValue>,
925    ) -> Result<OperationOracleResult, VNextError> {
926        self.invoke_internal(inputs, attributes)
927            .map(|(result, _)| result)
928    }
929
930    pub fn invoke_and_compare(
931        &self,
932        inputs: Vec<OracleTensor>,
933        attributes: BTreeMap<AttributeId, SemanticValue>,
934        actual: &OperationOracleResult,
935    ) -> Result<bool, VNextError> {
936        let (reference, mut symbols) = self.invoke_internal(inputs, attributes)?;
937        validate_tensors_against_contracts(
938            "actual oracle comparison outputs",
939            actual.outputs(),
940            &self.requested_operation.outputs,
941            &mut symbols,
942        )?;
943        compare_oracle_results(self.comparison_policy(), actual, &reference)
944    }
945
946    fn invoke_internal(
947        &self,
948        inputs: Vec<OracleTensor>,
949        attributes: BTreeMap<AttributeId, SemanticValue>,
950    ) -> Result<(OperationOracleResult, BTreeMap<String, u64>), VNextError> {
951        if self.registered.oracle.descriptor() != &self.registered.descriptor {
952            return Err(invalid_oracle(
953                "registered oracle descriptor changed before invocation",
954            ));
955        }
956        let mut symbols = BTreeMap::new();
957        validate_tensors_against_contracts(
958            "oracle request inputs",
959            &inputs,
960            &self.requested_operation.inputs,
961            &mut symbols,
962        )?;
963        self.requested_operation.validate_attributes(&attributes)?;
964        let request = OperationOracleRequest::new(
965            self.terminal_operation.id.clone(),
966            self.terminal_operation.fingerprint()?,
967            inputs,
968            attributes,
969        )?;
970        let result = self.registered.oracle.invoke(&request)?;
971        if self.registered.oracle.descriptor() != &self.registered.descriptor {
972            return Err(invalid_oracle(
973                "registered oracle descriptor changed during invocation",
974            ));
975        }
976        validate_tensors_against_contracts(
977            "oracle result outputs",
978            result.outputs(),
979            &self.terminal_operation.outputs,
980            &mut symbols,
981        )?;
982        Ok((result, symbols))
983    }
984}
985
986fn validate_tensors_against_contracts(
987    context: &str,
988    tensors: &[OracleTensor],
989    contracts: &[super::TensorContract],
990    symbols: &mut BTreeMap<String, u64>,
991) -> Result<(), VNextError> {
992    if tensors.len() != contracts.len() {
993        return Err(invalid_oracle(format!(
994            "{context} count {} differs from contract count {}",
995            tensors.len(),
996            contracts.len()
997        )));
998    }
999    for (index, (tensor, contract)) in tensors.iter().zip(contracts).enumerate() {
1000        if tensor.dimensions.len() != contract.dimensions().len()
1001            || !contract.element_types().contains(&tensor.element_type)
1002        {
1003            return Err(invalid_oracle(format!(
1004                "{context}[{index}] rank or dtype differs from the operation contract"
1005            )));
1006        }
1007        for (axis, (extent, constraint)) in tensor
1008            .dimensions
1009            .iter()
1010            .zip(contract.dimensions())
1011            .enumerate()
1012        {
1013            let accepted = match constraint {
1014                DimensionConstraint::Exact(expected) => extent == expected,
1015                DimensionConstraint::Range { minimum, maximum } => {
1016                    minimum <= extent && extent <= maximum
1017                }
1018                DimensionConstraint::Symbol(symbol) => match symbols.get(symbol) {
1019                    Some(expected) => expected == extent,
1020                    None => {
1021                        symbols.insert(symbol.clone(), *extent);
1022                        true
1023                    }
1024                },
1025            };
1026            if !accepted {
1027                return Err(invalid_oracle(format!(
1028                    "{context}[{index}] axis {axis} violates the operation contract"
1029                )));
1030            }
1031        }
1032    }
1033    Ok(())
1034}
1035
1036/// Applies a terminal operation's exact, absolute or relative `OracleSpec` to
1037/// two already canonical result sets. Relative tolerance is
1038/// `abs(actual-reference) <= tolerance * abs(reference)`, so a zero reference
1039/// accepts only an exact zero difference.
1040pub fn compare_oracle_results(
1041    policy: &OracleSpec,
1042    actual: &OperationOracleResult,
1043    reference: &OperationOracleResult,
1044) -> Result<bool, VNextError> {
1045    if actual.outputs.len() != reference.outputs.len() {
1046        return Err(invalid_oracle(
1047            "oracle comparison result counts are different",
1048        ));
1049    }
1050    for (index, (actual, reference)) in actual.outputs.iter().zip(&reference.outputs).enumerate() {
1051        if actual.dimensions != reference.dimensions
1052            || actual.element_type != reference.element_type
1053        {
1054            return Err(invalid_oracle(format!(
1055                "oracle comparison tensor {index} shape or dtype is different"
1056            )));
1057        }
1058    }
1059
1060    match policy {
1061        OracleSpec::Exact => Ok(actual
1062            .outputs
1063            .iter()
1064            .zip(&reference.outputs)
1065            .all(|(actual, reference)| actual.bytes == reference.bytes)),
1066        OracleSpec::AbsoluteTolerance { tolerance } => {
1067            compare_with_tolerance(actual, reference, rational_to_f64(*tolerance)?, false)
1068        }
1069        OracleSpec::RelativeTolerance { tolerance } => {
1070            compare_with_tolerance(actual, reference, rational_to_f64(*tolerance)?, true)
1071        }
1072        OracleSpec::ReferenceOperation { .. } => Err(invalid_oracle(
1073            "reference operation policy must be resolved to a terminal oracle before comparison",
1074        )),
1075    }
1076}
1077
1078fn rational_to_f64(value: super::CanonicalRational) -> Result<f64, VNextError> {
1079    if value.numerator() < 0 {
1080        return Err(invalid_oracle("oracle tolerance must not be negative"));
1081    }
1082    let tolerance = value.numerator() as f64 / value.denominator() as f64;
1083    if !tolerance.is_finite() {
1084        return Err(invalid_oracle("oracle tolerance must be finite"));
1085    }
1086    Ok(tolerance)
1087}
1088
1089fn compare_with_tolerance(
1090    actual: &OperationOracleResult,
1091    reference: &OperationOracleResult,
1092    tolerance: f64,
1093    relative: bool,
1094) -> Result<bool, VNextError> {
1095    for (actual, reference) in actual.outputs.iter().zip(&reference.outputs) {
1096        for index in 0..actual.element_count() {
1097            let actual = actual.numeric_value(index)?;
1098            let reference = reference.numeric_value(index)?;
1099            let difference = (actual - reference).abs();
1100            let allowed = if relative {
1101                tolerance * reference.abs()
1102            } else {
1103                tolerance
1104            };
1105            if difference > allowed {
1106                return Ok(false);
1107            }
1108        }
1109    }
1110    Ok(true)
1111}