Skip to main content

kormir/
lib.rs

1#![allow(async_fn_in_trait)]
2
3pub mod error;
4#[cfg(feature = "nostr")]
5pub mod nostr_events;
6pub mod storage;
7
8use crate::error::Error;
9use crate::storage::Storage;
10use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
11use bitcoin::hashes::{sha256, Hash};
12use bitcoin::key::XOnlyPublicKey;
13use bitcoin::secp256k1::{All, Secp256k1, SecretKey};
14use bitcoin::Network;
15use secp256k1_zkp::Keypair;
16use std::cmp::{max, min};
17use std::str::FromStr;
18
19pub use bitcoin;
20pub use bitcoin::secp256k1::schnorr::Signature;
21pub use ddk_messages::oracle_msgs::{
22    DigitDecompositionEventDescriptor, EnumEventDescriptor, EventDescriptor, OracleAnnouncement,
23    OracleAttestation, OracleEvent,
24};
25pub use lightning;
26pub use lightning::util::ser::{Readable, Writeable};
27#[cfg(feature = "nostr")]
28pub use nostr;
29
30// first key for taproot address
31/// Derivation path used to derive the Taproot signing key from an `Xpriv`.
32///
33/// Follows BIP-86 single-sig Taproot path: `m/86'/0'/0'/0/0`.
34const SIGNING_KEY_PATH: &str = "m/86'/0'/0'/0/0";
35
36/// Creates an enum event announcement for oracle events with discrete outcomes.
37///
38/// This function creates an `OracleAnnouncement` for events where the outcome is one of
39/// a predefined set of discrete options (e.g., "heads" or "tails" for a coin flip).
40///
41/// # Arguments
42/// * `secp` - Secp256k1 context for cryptographic operations
43/// * `key_pair` - Oracle's key pair for signing the announcement
44/// * `event_id` - Unique identifier for this event
45/// * `outcomes` - List of possible outcomes for this event
46/// * `event_maturity_epoch` - Unix timestamp when the event matures
47/// * `nonce` - Public key for the nonce used in this event
48///
49/// # Returns
50/// * `Ok(OracleAnnouncement)` - The signed announcement if successful
51/// * `Err(Error::InvalidEventId)` - If the event_id is empty
52/// * `Err(Error::InvalidOutcomes)` - If the outcomes list is empty
53/// * `Err(Error::Internal)` - If cryptographic operations fail
54///
55/// # Example
56/// ```rust
57/// use kormir::*;
58/// use bitcoin::secp256k1::{rand, Secp256k1, SecretKey};
59/// use secp256k1_zkp::Keypair;
60///
61/// let secp = Secp256k1::new();
62/// let key_pair = Keypair::new(&secp, &mut rand::thread_rng());
63/// let nonce_key = SecretKey::from_keypair(&Keypair::new(&secp, &mut rand::thread_rng()));
64/// let nonce = nonce_key.x_only_public_key(&secp).0;
65///
66/// let announcement = create_enum_event(
67///     &secp,
68///     &key_pair,
69///     "coin_flip",
70///     &vec!["heads".to_string(), "tails".to_string()],
71///     1640995200, // 2022-01-01 00:00:00 UTC
72///     &nonce,
73/// ).unwrap();
74/// ```
75///
76/// # DLC Spec
77/// * [Simple Enumeration](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#simple-enumeration)
78/// * [Oracle Announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_announcement-type)
79/// * [Oracle Event](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_event-type)
80/// * [Enum Event Descriptor](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#enum_event_descriptor)
81pub fn create_enum_event(
82    secp: &Secp256k1<All>,
83    key_pair: &Keypair,
84    event_id: &str,
85    outcomes: &[String],
86    event_maturity_epoch: u32,
87    nonce: &XOnlyPublicKey,
88) -> Result<OracleAnnouncement, Error> {
89    if event_id.is_empty() {
90        return Err(Error::InvalidEventId);
91    }
92    if outcomes.is_empty() {
93        return Err(Error::InvalidOutcomes);
94    }
95    let oracle_nonces = vec![*nonce];
96    let event_descriptor = EventDescriptor::EnumEvent(EnumEventDescriptor {
97        outcomes: outcomes.to_owned(),
98    });
99    let oracle_event = OracleEvent {
100        oracle_nonces,
101        event_id: event_id.to_owned(),
102        event_maturity_epoch,
103        event_descriptor,
104    };
105    oracle_event.validate().map_err(|_| Error::Internal)?;
106
107    // create signature
108    let msg = ddk_messages::oracle_msgs::tagged_announcement_msg(&oracle_event);
109    let announcement_signature = secp.sign_schnorr_no_aux_rand(&msg, key_pair);
110
111    let ann = OracleAnnouncement {
112        oracle_event,
113        oracle_public_key: key_pair.public_key().x_only_public_key().0,
114        announcement_signature,
115    };
116    ann.validate(secp).map_err(|_| Error::Internal)?;
117    Ok(ann)
118}
119
120/// Signs an enum event with a specific outcome.
121///
122/// This function creates an `OracleAttestation` by signing the chosen outcome
123/// for a previously announced enum event. The signature uses the oracle's private key
124/// and the nonce key to ensure cryptographic security.
125///
126/// # Arguments
127/// * `secp` - Secp256k1 context for cryptographic operations
128/// * `key_pair` - Oracle's key pair for signing
129/// * `announcement` - The original event announcement
130/// * `outcome` - The specific outcome to sign (must be one of the announced outcomes)
131/// * `nonce_key` - The private key corresponding to the nonce used in the announcement
132///
133/// # Returns
134/// * `Ok(OracleAttestation)` - The signed attestation if successful
135/// * `Err(Error::InvalidEventDescriptor)` - If the event descriptor is not an EnumEvent
136/// * `Err(Error::InvalidOutcome)` - If the outcome is not in the announced list
137/// * `Err(Error::InvalidAnnouncement)` - If our public key doesn't match the announcement's oracle_public_key
138/// * `Err(Error::InvalidNonces)` - If the nonce_key don't match the announcement's nonce
139/// * `Err(Error::Internal)` - If cryptographic operations fail
140///
141/// # Example
142/// ```rust
143/// use kormir::*;
144/// use bitcoin::secp256k1::{rand, Secp256k1, SecretKey};
145/// use secp256k1_zkp::Keypair;
146///
147/// let secp = Secp256k1::new();
148/// let key_pair = Keypair::new(&secp, &mut rand::thread_rng());
149/// let nonce_key = SecretKey::from_keypair(&Keypair::new(&secp, &mut rand::thread_rng()));
150/// let nonce = nonce_key.x_only_public_key(&secp).0;
151///
152/// // First create the announcement
153/// let announcement = create_enum_event(
154///     &secp,
155///     &key_pair,
156///     "coin_flip",
157///     &vec!["heads".to_string(), "tails".to_string()],
158///     1640995200,
159///     &nonce,
160/// ).unwrap();
161///
162/// // Then sign the outcome
163/// let attestation = sign_enum_event(
164///     &secp,
165///     &key_pair,
166///     &announcement,
167///     &"heads".to_string(),
168///     &nonce_key,
169/// ).unwrap();
170/// ```
171///
172/// # DLC Spec
173/// * [Simple Enumeration](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#simple-enumeration)
174/// * [Oracle Attestation](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_attestation-type)
175/// * [Signing Algorithm](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#signing-algorithm)
176pub fn sign_enum_event(
177    secp: &Secp256k1<All>,
178    key_pair: &Keypair,
179    announcement: &OracleAnnouncement,
180    outcome: &str,
181    nonce_key: &SecretKey,
182) -> Result<OracleAttestation, Error> {
183    let descriptor = match &announcement.oracle_event.event_descriptor {
184        EventDescriptor::EnumEvent(desc) => desc,
185        _ => return Err(Error::InvalidEventDescriptor),
186    };
187    if !descriptor.outcomes.contains(&outcome.to_owned()) {
188        return Err(Error::InvalidOutcome);
189    }
190    if key_pair.x_only_public_key().0 != announcement.oracle_public_key {
191        return Err(Error::InvalidAnnouncement);
192    }
193
194    if announcement.oracle_event.oracle_nonces.is_empty()
195        || nonce_key.x_only_public_key(secp).0 != announcement.oracle_event.oracle_nonces[0]
196    {
197        return Err(Error::InvalidNonces);
198    }
199
200    let msg = ddk_messages::oracle_msgs::tagged_attestation_msg(outcome);
201
202    let sig = ddk_dlc::secp_utils::schnorrsig_sign_with_nonce(
203        secp,
204        &msg,
205        key_pair,
206        &nonce_key.secret_bytes(),
207    );
208
209    // verify our signature
210    if secp
211        .verify_schnorr(&sig, &msg, &key_pair.x_only_public_key().0)
212        .is_err()
213    {
214        return Err(Error::Internal);
215    };
216
217    let attestation = OracleAttestation {
218        event_id: announcement.oracle_event.event_id.clone(),
219        oracle_public_key: key_pair.public_key().x_only_public_key().0,
220        signatures: vec![sig],
221        outcomes: vec![outcome.to_owned()],
222    };
223
224    Ok(attestation)
225}
226
227/// Creates a numeric event announcement for oracle events with numeric outcomes.
228///
229/// This function creates an `OracleAnnouncement` for events where the outcome is a numeric
230/// value that can be decomposed into digits. The value is represented in a specified base
231/// (currently only base 2 is supported) and can be signed or unsigned.
232///
233/// # Arguments
234/// * `secp` - Secp256k1 context for cryptographic operations
235/// * `key_pair` - Oracle's key pair for signing the announcement
236/// * `event_id` - Unique identifier for this event
237/// * `base` - Numeric base for digit decomposition (must be 2)
238/// * `num_digits` - Number of digits in the numeric representation
239/// * `is_signed` - Whether the numeric value can be negative
240/// * `precision` - Decimal precision for the numeric value
241/// * `unit` - Unit of measurement for the numeric value
242/// * `event_maturity_epoch` - Unix timestamp when the event matures
243/// * `nonces` - Vector of public keys for nonces (length must match required nonces)
244///
245/// # Returns
246/// * `Ok(OracleAnnouncement)` - The signed announcement if successful
247/// * `Err(Error::InvalidEventId)` - If the event_id is empty
248/// * `Err(Error::InvalidBase)` - If base is not 2
249/// * `Err(Error::InvalidNumberOfDigits)` - If num_digits is 0 or more than 63
250/// * `Err(Error::InvalidNonces)` - If the number of nonces doesn't match the required count
251/// * `Err(Error::Internal)` - If cryptographic operations fail
252///
253/// # Example
254/// ```rust
255/// use kormir::*;
256/// use bitcoin::secp256k1::{rand, Secp256k1, SecretKey};
257/// use secp256k1_zkp::Keypair;
258///
259/// let secp = Secp256k1::new();
260/// let key_pair = Keypair::new(&secp, &mut rand::thread_rng());
261/// let nonce_keys: Vec<SecretKey> = (0..6)
262///     .map(|_| SecretKey::from_keypair(&Keypair::new(&secp, &mut rand::thread_rng())))
263///     .collect();
264/// let nonces = nonce_keys.iter().map(|k| k.x_only_public_key(&secp).0).collect::<Vec<bitcoin::XOnlyPublicKey>>();
265///
266/// let announcement = create_numeric_event(
267///     &secp,
268///     &key_pair,
269///     &"temperature".to_string(),
270///     2, // base 2
271///     5, // 5 digits
272///     true, // signed
273///     1, // 1 decimal place
274///     &"°C".to_string(),
275///     1640995200, // 2022-01-01 00:00:00 UTC
276///     &nonces,
277/// ).unwrap();
278/// ```
279///
280/// # DLC Spec
281/// * [Digit Decomposition](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#digit-decomposition)
282/// * [Oracle Announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_announcement-type)
283/// * [Oracle Event](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_event-type)
284/// * [Digit Decomposition Event Descriptor](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#digit_decomposition_event_descriptor)
285#[allow(clippy::too_many_arguments)]
286pub fn create_numeric_event(
287    secp: &Secp256k1<All>,
288    key_pair: &Keypair,
289    event_id: &str,
290    base: u16,
291    num_digits: u16,
292    is_signed: bool,
293    precision: i32,
294    unit: &str,
295    event_maturity_epoch: u32,
296    nonces: &[XOnlyPublicKey],
297) -> Result<OracleAnnouncement, Error> {
298    if event_id.is_empty() {
299        return Err(Error::InvalidEventId);
300    }
301    if base != 2 {
302        return Err(Error::InvalidBase);
303    }
304    if num_digits == 0 || num_digits > 63 {
305        return Err(Error::InvalidNumberOfDigits);
306    }
307
308    let num_nonces = if is_signed {
309        num_digits as usize + 1
310    } else {
311        num_digits as usize
312    };
313
314    if nonces.len() != num_nonces {
315        return Err(Error::InvalidNonces);
316    }
317
318    let event_descriptor =
319        EventDescriptor::DigitDecompositionEvent(DigitDecompositionEventDescriptor {
320            base,
321            is_signed,
322            unit: unit.to_owned(),
323            precision,
324            nb_digits: num_digits,
325        });
326    let oracle_event = OracleEvent {
327        oracle_nonces: nonces.to_owned(),
328        event_id: event_id.to_owned(),
329        event_maturity_epoch,
330        event_descriptor,
331    };
332    oracle_event.validate().map_err(|_| Error::Internal)?;
333
334    // create signature
335    let msg = ddk_messages::oracle_msgs::tagged_announcement_msg(&oracle_event);
336    let announcement_signature = secp.sign_schnorr_no_aux_rand(&msg, key_pair);
337
338    let ann = OracleAnnouncement {
339        oracle_event,
340        oracle_public_key: key_pair.x_only_public_key().0,
341        announcement_signature,
342    };
343    ann.validate(secp).map_err(|_| Error::Internal)?;
344
345    Ok(ann)
346}
347
348/// Signs a numeric event with a specific numeric outcome.
349///
350/// This function creates an `OracleAttestation` by signing a numeric outcome
351/// for a previously announced numeric event. The numeric value is decomposed into
352/// individual digits, each signed with its corresponding nonce key.
353///
354/// The function includes special clamping logic as described in the DLC spec:
355/// - For unsigned events: negative values are clamped to 0, values exceeding the maximum are clamped to the maximum
356/// - For signed events: values are clamped to the valid range [-max_value, +max_value]
357///
358/// # Arguments
359/// * `secp` - Secp256k1 context for cryptographic operations
360/// * `key_pair` - Oracle's key pair for signing
361/// * `announcement` - The original event announcement
362/// * `outcome` - The numeric outcome to sign (will be clamped if out of range)
363/// * `nonce_keys` - Vector of private keys corresponding to the nonces used in the announcement
364///
365/// # Returns
366/// * `Ok(OracleAttestation)` - The signed attestation if successful
367/// * `Err(Error::InvalidEventDescriptor)` - If the event descriptor is not a DigitDecompositionEvent
368/// * `Err(Error::InvalidBase)` - If base is not 2
369/// * `Err(Error::InvalidNumberOfDigits)` - If nb_digits is 0 or more than 63
370/// * `Err(Error::InvalidAnnouncement)` - If our public key doesn't match the announcement's oracle_public_key
371/// * `Err(Error::InvalidOutcome)` - If the outcome is out of range and clamp_outcome is false
372/// * `Err(Error::InvalidNonces)` - If the number of nonce_keys doesn't match the number of digits, nonce_keys don't match the announcement's nonces
373/// * `Err(Error::Internal)` - If cryptographic operations fail
374///
375/// # Example
376/// ```rust
377/// use kormir::*;
378/// use bitcoin::secp256k1::{rand, Secp256k1, SecretKey};
379/// use secp256k1_zkp::Keypair;
380///
381/// let secp = Secp256k1::new();
382/// let key_pair = Keypair::new(&secp, &mut rand::thread_rng());
383/// let nonce_keys: Vec<SecretKey> = (0..5)
384///     .map(|_| SecretKey::from_keypair(&Keypair::new(&secp, &mut rand::thread_rng())))
385///     .collect();
386/// let nonces = nonce_keys.iter().map(|k| k.x_only_public_key(&secp).0).collect::<Vec<bitcoin::XOnlyPublicKey>>();
387///
388/// // First create the announcement
389/// let announcement = create_numeric_event(
390///     &secp,
391///     &key_pair,
392///     &"temperature".to_string(),
393///     2, // base 2
394///     4, // 4 digits
395///     true, // signed
396///     0, // 0 decimal places
397///     &"°C".to_string(),
398///     1640995200,
399///     &nonces,
400/// ).unwrap();
401///
402/// // Then sign the outcome (will be clamped to valid range)
403/// let attestation = sign_numeric_event(
404///     &secp,
405///     &key_pair,
406///     &announcement,
407///     15, // This will be decomposed into binary digits
408///     &nonce_keys,
409/// ).unwrap();
410/// ```
411///
412/// # DLC Spec
413/// * [Digit Decomposition](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#digit-decomposition)
414/// * [Oracle Attestation](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_attestation-type)
415/// * [Signing Algorithm](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#signing-algorithm)
416pub fn sign_numeric_event(
417    secp: &Secp256k1<All>,
418    key_pair: &Keypair,
419    announcement: &OracleAnnouncement,
420    outcome: i64,
421    nonce_keys: &[SecretKey],
422) -> Result<OracleAttestation, Error> {
423    let descriptor = match &announcement.oracle_event.event_descriptor {
424        EventDescriptor::DigitDecompositionEvent(desc) => desc,
425        _ => return Err(Error::InvalidEventDescriptor),
426    };
427    if descriptor.base != 2 {
428        return Err(Error::InvalidBase);
429    }
430    if key_pair.x_only_public_key().0 != announcement.oracle_public_key {
431        return Err(Error::InvalidAnnouncement);
432    }
433    nonce_keys
434        .iter()
435        .zip(&announcement.oracle_event.oracle_nonces)
436        .try_for_each(|(nonce_key, nonce)| {
437            if nonce_key.x_only_public_key(secp).0 != *nonce {
438                Err(Error::InvalidNonces)
439            } else {
440                Ok(())
441            }
442        })?;
443    let max_value = get_max_value(descriptor)?;
444    let min_value = get_min_value(descriptor)?;
445    let outcome_to_sign = if outcome < min_value || outcome > max_value {
446        max(min(outcome, max_value), min_value)
447    } else {
448        outcome
449    };
450    let digits = format!(
451        "{:0width$b}",
452        outcome_to_sign.abs(),
453        width = descriptor.nb_digits as usize
454    )
455    .chars()
456    .map(|char| char.to_string())
457    .collect::<Vec<_>>();
458
459    let outcomes = if descriptor.is_signed {
460        let mut sign = vec![if outcome_to_sign < 0 {
461            "-".to_string()
462        } else {
463            "+".to_string()
464        }];
465        sign.extend(digits);
466        sign
467    } else {
468        digits
469    };
470
471    if nonce_keys.len() != outcomes.len() {
472        return Err(Error::InvalidNonces);
473    }
474
475    let signatures = outcomes
476        .iter()
477        .zip(nonce_keys)
478        .map(|(outcome, nonce_key)| {
479            let msg = ddk_messages::oracle_msgs::tagged_attestation_msg(outcome);
480            let sig = ddk_dlc::secp_utils::schnorrsig_sign_with_nonce(
481                secp,
482                &msg,
483                key_pair,
484                &nonce_key.secret_bytes(),
485            );
486            // verify our signature
487            if secp
488                .verify_schnorr(&sig, &msg, &key_pair.x_only_public_key().0)
489                .is_err()
490            {
491                return Err(Error::Internal);
492            };
493            Ok(sig)
494        })
495        .collect::<Result<Vec<_>, Error>>()?;
496
497    let attestation = OracleAttestation {
498        event_id: announcement.oracle_event.event_id.clone(),
499        oracle_public_key: key_pair.x_only_public_key().0,
500        signatures,
501        outcomes,
502    };
503
504    Ok(attestation)
505}
506
507/// Returns the minimum representable outcome for the provided digit decomposition descriptor.
508///
509/// For unsigned descriptors, the minimum is 0; for signed descriptors, the minimum
510/// is the negation of the maximum representable magnitude.
511pub fn get_min_value(descriptor: &DigitDecompositionEventDescriptor) -> Result<i64, Error> {
512    if descriptor.is_signed {
513        get_max_value(descriptor).map(|x| -x)
514    } else {
515        Ok(0)
516    }
517}
518
519/// Returns the maximum representable (absolute) magnitude for the descriptor.
520///
521/// Computed as `(base^nb_digits) - 1`. For unsigned descriptors this is the maximum
522/// value; for signed descriptors, this is the maximum magnitude.
523pub fn get_max_value(descriptor: &DigitDecompositionEventDescriptor) -> Result<i64, Error> {
524    if descriptor.nb_digits == 0 || descriptor.nb_digits > 63 {
525        Err(Error::InvalidNumberOfDigits)
526    } else {
527        Ok((descriptor.base as i64).pow(descriptor.nb_digits as u32) - 1)
528    }
529}
530
531/// Oracle encapsulates the oracle's signing key, nonce derivation and persistence layer
532/// to create announcements and produce attestations for enum and numeric events.
533#[derive(Debug, Clone)]
534pub struct Oracle<S: Storage> {
535    pub storage: S,
536    key_pair: Keypair,
537    nonce_xpriv: Xpriv,
538    secp: Secp256k1<All>,
539}
540
541impl<S: Storage> Oracle<S> {
542    /// Creates a new `Oracle` from a signing key and nonce master `Xpriv`.
543    ///
544    /// The `nonce_xpriv` is used to derive hardened per-event nonce keys.
545    pub fn new(storage: S, signing_key: SecretKey, nonce_xpriv: Xpriv) -> Self {
546        let secp = Secp256k1::new();
547        Self {
548            storage,
549            key_pair: Keypair::from_secret_key(&secp, &signing_key),
550            nonce_xpriv,
551            secp,
552        }
553    }
554
555    /// Constructs an `Oracle` from a master `Xpriv` by deriving the Taproot signing key
556    /// at `SIGNING_KEY_PATH`, and creating a deterministic `nonce_xpriv` used for nonces.
557    pub fn from_xpriv(storage: S, xpriv: Xpriv) -> Result<Self, Error> {
558        let secp = Secp256k1::new();
559
560        let signing_key = derive_signing_key(&secp, xpriv)?;
561        Self::from_signing_key(storage, signing_key)
562    }
563
564    /// Constructs an `Oracle` from a provided signing key. The `nonce_xpriv` is
565    /// deterministically derived from the SHA256 of the signing key bytes.
566    pub fn from_signing_key(storage: S, signing_key: SecretKey) -> Result<Self, Error> {
567        let secp = Secp256k1::new();
568
569        let xpriv_bytes = sha256::Hash::hash(&signing_key.secret_bytes()).to_byte_array();
570        let nonce_xpriv =
571            Xpriv::new_master(Network::Bitcoin, &xpriv_bytes).map_err(|_| Error::Internal)?;
572
573        Ok(Self {
574            storage,
575            key_pair: Keypair::from_secret_key(&secp, &signing_key),
576            nonce_xpriv,
577            secp,
578        })
579    }
580
581    /// Returns the oracle's x-only public key, used in announcements and attestations.
582    pub fn public_key(&self) -> XOnlyPublicKey {
583        self.key_pair.x_only_public_key().0
584    }
585
586    /// Returns the keys for the oracle, used for Nostr.
587    #[cfg(feature = "nostr")]
588    pub fn nostr_keys(&self) -> nostr::Keys {
589        let sec = nostr::key::SecretKey::from_slice(&self.key_pair.secret_key().secret_bytes()[..])
590            .expect("just converting types");
591        nostr::Keys::new(sec)
592    }
593
594    /// Derives the hardened nonce private key at `index` from the oracle's `nonce_xpriv`.
595    fn get_nonce_key(&self, index: u32) -> SecretKey {
596        self.nonce_xpriv
597            .derive_priv(
598                &self.secp,
599                &[ChildNumber::from_hardened_idx(index).unwrap()],
600            )
601            .unwrap()
602            .private_key
603    }
604
605    /// Creates an enum event announcement with a fresh nonce and persists it to `storage`.
606    pub async fn create_enum_event(
607        &self,
608        event_id: String,
609        outcomes: Vec<String>,
610        event_maturity_epoch: u32,
611    ) -> Result<OracleAnnouncement, Error> {
612        let nonce_indexes = self.storage.get_next_nonce_indexes(1).await?;
613        if nonce_indexes.len() != 1 {
614            return Err(Error::Internal);
615        }
616        let nonce_key = self.get_nonce_key(nonce_indexes[0]);
617        let nonce = nonce_key.x_only_public_key(&self.secp).0;
618        let ann = create_enum_event(
619            &self.secp,
620            &self.key_pair,
621            &event_id,
622            &outcomes,
623            event_maturity_epoch,
624            &nonce,
625        )?;
626        let _ = self
627            .storage
628            .save_announcement(ann.clone(), nonce_indexes)
629            .await?;
630        Ok(ann)
631    }
632
633    /// Signs an enum event outcome for an existing stored event and persists the signature.
634    pub async fn sign_enum_event(
635        &self,
636        event_id: String,
637        outcome: String,
638    ) -> Result<OracleAttestation, Error> {
639        let Some(data) = self.storage.get_event(event_id.clone()).await? else {
640            return Err(Error::NotFound);
641        };
642        if !data.signatures.is_empty() {
643            return Err(Error::EventAlreadySigned);
644        }
645        if data.indexes.len() != 1 {
646            return Err(Error::Internal);
647        }
648
649        let nonce_index = data.indexes[0];
650        let nonce_key = self.get_nonce_key(nonce_index);
651
652        let attestation = sign_enum_event(
653            &self.secp,
654            &self.key_pair,
655            &data.announcement,
656            &outcome,
657            &nonce_key,
658        )?;
659
660        let sigs = vec![(outcome.clone(), attestation.signatures.clone()[0])];
661
662        self.storage
663            .save_signatures(event_id.to_string(), sigs)
664            .await?;
665
666        Ok(attestation)
667    }
668
669    /// Creates a numeric event announcement with fresh nonces and persists it to `storage`.
670    pub async fn create_numeric_event(
671        &self,
672        event_id: String,
673        num_digits: u16,
674        is_signed: bool,
675        precision: i32,
676        unit: String,
677        event_maturity_epoch: u32,
678    ) -> Result<OracleAnnouncement, Error> {
679        let num_nonces = if is_signed {
680            num_digits as usize + 1
681        } else {
682            num_digits as usize
683        };
684
685        let indexes = self.storage.get_next_nonce_indexes(num_nonces).await?;
686        let oracle_nonces = indexes
687            .iter()
688            .map(|i| {
689                let nonce_key = self.get_nonce_key(*i);
690                nonce_key.x_only_public_key(&self.secp).0
691            })
692            .collect::<Vec<XOnlyPublicKey>>();
693
694        let ann = create_numeric_event(
695            &self.secp,
696            &self.key_pair,
697            &event_id,
698            2,
699            num_digits,
700            is_signed,
701            precision,
702            &unit,
703            event_maturity_epoch,
704            &oracle_nonces,
705        )?;
706
707        let _ = self.storage.save_announcement(ann.clone(), indexes).await?;
708
709        Ok(ann)
710    }
711
712    /// Signs a numeric event outcome (with clamping) and persists the signatures to `storage`.
713    pub async fn sign_numeric_event(
714        &self,
715        event_id: String,
716        outcome: i64,
717    ) -> Result<OracleAttestation, Error> {
718        let Some(data) = self.storage.get_event(event_id.clone()).await? else {
719            return Err(Error::NotFound);
720        };
721        if !data.signatures.is_empty() {
722            return Err(Error::EventAlreadySigned);
723        }
724
725        let nonce_keys = data
726            .indexes
727            .iter()
728            .map(|i| self.get_nonce_key(*i))
729            .collect::<Vec<SecretKey>>();
730
731        let attestation = sign_numeric_event(
732            &self.secp,
733            &self.key_pair,
734            &data.announcement,
735            outcome,
736            &nonce_keys,
737        )?;
738
739        let sigs = attestation
740            .outcomes
741            .iter()
742            .cloned()
743            .zip(attestation.signatures.clone())
744            .collect();
745
746        self.storage.save_signatures(event_id, sigs).await?;
747
748        Ok(attestation)
749    }
750}
751
752/// Derives the Taproot signing `SecretKey` from a master `Xpriv` using `SIGNING_KEY_PATH`.
753///
754/// # Arguments
755/// * `secp` - Secp256k1 context used for derivation
756/// * `xpriv` - Master extended private key
757///
758/// # Returns
759/// * `Ok(SecretKey)` - The derived private key
760/// * `Err(Error::Internal)` - If the derivation path or key derivation fails
761pub fn derive_signing_key(secp: &Secp256k1<All>, xpriv: Xpriv) -> Result<SecretKey, Error> {
762    let signing_key = xpriv
763        .derive_priv(
764            secp,
765            &DerivationPath::from_str(SIGNING_KEY_PATH).map_err(|_| Error::Internal)?,
766        )
767        .map_err(|_| Error::Internal)?
768        .private_key;
769    Ok(signing_key)
770}
771
772#[cfg(test)]
773mod test {
774    use super::*;
775    use crate::storage::MemoryStorage;
776    use bitcoin::secp256k1::rand::{thread_rng, Rng};
777
778    fn setup_test_oracle() -> Oracle<MemoryStorage> {
779        let mut seed: [u8; 64] = [0; 64];
780        thread_rng().fill(&mut seed);
781        let xpriv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
782        Oracle::from_xpriv(MemoryStorage::default(), xpriv).unwrap()
783    }
784
785    fn setup_test_keypair() -> (Secp256k1<All>, Keypair) {
786        let mut seed: [u8; 64] = [0; 64];
787        thread_rng().fill(&mut seed);
788        let xpriv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
789        let secp = Secp256k1::new();
790        let signing_key = derive_signing_key(&secp, xpriv).unwrap();
791        let key_pair = Keypair::from_secret_key(&secp, &signing_key);
792        (secp, key_pair)
793    }
794
795    fn setup_test_nonce(secp: &Secp256k1<All>) -> (SecretKey, XOnlyPublicKey) {
796        let mut bytes: [u8; 32] = [0; 32];
797        thread_rng().fill(&mut bytes);
798        let nonce_key = SecretKey::from_slice(&bytes).unwrap();
799        (nonce_key, nonce_key.x_only_public_key(&secp).0)
800    }
801
802    fn setup_test_nonces(secp: &Secp256k1<All>, n: u16) -> (Vec<SecretKey>, Vec<XOnlyPublicKey>) {
803        let nonce_keys: Vec<SecretKey> = (0..n)
804            .map(|_| {
805                let mut bytes: [u8; 32] = [0; 32];
806                thread_rng().fill(&mut bytes);
807                SecretKey::from_slice(&bytes).unwrap()
808            })
809            .collect();
810        let nonces: Vec<XOnlyPublicKey> = nonce_keys
811            .iter()
812            .map(|nonce_key| nonce_key.x_only_public_key(&secp).0)
813            .collect();
814        (nonce_keys, nonces)
815    }
816
817    #[test]
818    fn test_create_enum_event() {
819        let (secp, key_pair) = setup_test_keypair();
820
821        let event_id = "enum".to_string();
822        let outcomes = vec!["x".to_string(), "y".to_string()];
823        let event_maturity_epoch = 12345u32;
824
825        let (_, nonce) = setup_test_nonce(&secp);
826
827        let ann = create_enum_event(
828            &secp,
829            &key_pair,
830            &event_id,
831            &outcomes,
832            event_maturity_epoch,
833            &nonce,
834        )
835        .unwrap();
836
837        assert!(ann.validate(&secp).is_ok());
838        assert_eq!(ann.oracle_event.event_id, event_id);
839        assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
840        assert_eq!(ann.oracle_event.oracle_nonces, vec![nonce]);
841        match ann.oracle_event.event_descriptor {
842            EventDescriptor::EnumEvent(d) => {
843                assert_eq!(d.outcomes, outcomes);
844            }
845            EventDescriptor::DigitDecompositionEvent(_) => {
846                assert!(false, "invalid event descriptor type")
847            }
848        }
849    }
850
851    #[test]
852    fn test_sign_enum_event() {
853        let (secp, key_pair) = setup_test_keypair();
854
855        let event_id = "enum_sign".to_string();
856        let outcomes = vec!["a".to_string(), "b".to_string()];
857        let event_maturity_epoch = 67890u32;
858
859        let (nonce_key, nonce) = setup_test_nonce(&secp);
860
861        let ann = create_enum_event(
862            &secp,
863            &key_pair,
864            &event_id,
865            &outcomes,
866            event_maturity_epoch,
867            &nonce,
868        )
869        .unwrap();
870
871        let attestation =
872            sign_enum_event(&secp, &key_pair, &ann, &"a".to_string(), &nonce_key).unwrap();
873
874        assert!(attestation.outcomes.contains(&"a".to_string()));
875        assert_eq!(
876            attestation.oracle_public_key,
877            key_pair.x_only_public_key().0
878        );
879        assert_eq!(attestation.signatures.len(), 1);
880
881        // verify our nonce is the same as the one in the announcement
882        assert_eq!(
883            attestation.signatures[0].encode()[..32],
884            ann.oracle_event.oracle_nonces[0].serialize()
885        );
886    }
887
888    #[test]
889    fn test_create_numeric_event() {
890        let (secp, key_pair) = setup_test_keypair();
891
892        let event_id = "numeric".to_string();
893        let num_digits = 8u16;
894        let is_signed = false;
895        let precision = 0i32;
896        let unit = "m/s".to_string();
897        let event_maturity_epoch = 1111u32;
898
899        let (_, nonces) = setup_test_nonces(&secp, num_digits);
900
901        let ann = create_numeric_event(
902            &secp,
903            &key_pair,
904            &event_id,
905            2,
906            num_digits,
907            is_signed,
908            precision,
909            &unit,
910            event_maturity_epoch,
911            &nonces,
912        )
913        .unwrap();
914
915        assert!(ann.validate(&secp).is_ok());
916        assert_eq!(ann.oracle_event.event_id, event_id);
917        assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
918        assert_eq!(ann.oracle_event.oracle_nonces, nonces);
919        match ann.oracle_event.event_descriptor {
920            EventDescriptor::EnumEvent(_) => {
921                assert!(false, "invalid event descriptor type")
922            }
923            EventDescriptor::DigitDecompositionEvent(d) => {
924                assert_eq!(d.base, 2);
925                assert_eq!(d.is_signed, is_signed);
926                assert_eq!(d.unit, unit);
927                assert_eq!(d.precision, precision);
928                assert_eq!(d.nb_digits, num_digits);
929            }
930        }
931    }
932
933    #[test]
934    fn test_sign_numeric_event() {
935        let (secp, key_pair) = setup_test_keypair();
936
937        let event_id = "numeric_sign".to_string();
938        let num_digits = 4u16;
939        let is_signed = true;
940        let precision = 0i32;
941        let unit = "m/s".to_string();
942        let event_maturity_epoch = 2222u32;
943
944        let (nonce_keys, nonces) = setup_test_nonces(&secp, num_digits + 1);
945
946        let ann = create_numeric_event(
947            &secp,
948            &key_pair,
949            &event_id,
950            2,
951            num_digits,
952            is_signed,
953            precision,
954            &unit,
955            event_maturity_epoch,
956            &nonces,
957        )
958        .unwrap();
959
960        let attestation = sign_numeric_event(&secp, &key_pair, &ann, -0b1010, &nonce_keys).unwrap();
961
962        assert_eq!(
963            attestation.outcomes,
964            vec!["-", "1", "0", "1", "0"]
965                .iter()
966                .map(|s| s.to_string())
967                .collect::<Vec<_>>()
968        );
969        assert_eq!(attestation.outcomes.len(), (num_digits as usize) + 1);
970        assert_eq!(attestation.signatures.len(), (num_digits as usize) + 1);
971        assert_eq!(
972            attestation.oracle_public_key,
973            key_pair.x_only_public_key().0
974        );
975        // verify our nonces are the same as the one in the announcement
976        attestation
977            .signatures
978            .iter()
979            .zip(ann.oracle_event.oracle_nonces)
980            .for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
981    }
982
983    #[test]
984    fn test_sign_numeric_event_clamping_unsigned() {
985        let (secp, key_pair) = setup_test_keypair();
986
987        let event_id = "unsigned".to_string();
988        let num_digits = 4u16; // base 2 -> range 0..=15
989        let is_signed = false;
990        let precision = 0i32;
991        let unit = "m/s".to_string();
992        let event_maturity_epoch = 3333u32;
993
994        let (nonce_keys, nonces) = setup_test_nonces(&secp, num_digits);
995
996        let ann = create_numeric_event(
997            &secp,
998            &key_pair,
999            &event_id,
1000            2,
1001            num_digits,
1002            is_signed,
1003            precision,
1004            &unit,
1005            event_maturity_epoch,
1006            &nonces,
1007        )
1008        .unwrap();
1009
1010        let att_big = sign_numeric_event(&secp, &key_pair, &ann, 1_000_000, &nonce_keys).unwrap();
1011        assert_eq!(
1012            att_big.outcomes,
1013            vec!["1", "1", "1", "1"]
1014                .iter()
1015                .map(|s| s.to_string())
1016                .collect::<Vec<_>>()
1017        );
1018        // verify our nonces are the same as the one in the announcement
1019        att_big
1020            .signatures
1021            .iter()
1022            .zip(ann.oracle_event.oracle_nonces.clone())
1023            .for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
1024
1025        let att_neg = sign_numeric_event(&secp, &key_pair, &ann, -42, &nonce_keys).unwrap();
1026        assert_eq!(
1027            att_neg.outcomes,
1028            vec!["0", "0", "0", "0"]
1029                .iter()
1030                .map(|s| s.to_string())
1031                .collect::<Vec<_>>()
1032        );
1033        // verify our nonces are the same as the one in the announcement
1034        att_neg
1035            .signatures
1036            .iter()
1037            .zip(ann.oracle_event.oracle_nonces.clone())
1038            .for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
1039    }
1040
1041    #[test]
1042    fn test_sign_numeric_event_clamping_signed() {
1043        let (secp, key_pair) = setup_test_keypair();
1044
1045        let event_id = "signed".to_string();
1046        let num_digits = 3u16; // base 2 -> magnitude range 0..=7; signed adds sign nonce
1047        let is_signed = true;
1048        let precision = 0i32;
1049        let unit = "m/s".to_string();
1050        let event_maturity_epoch = 4444u32;
1051
1052        let (nonce_keys, nonces) = setup_test_nonces(&secp, num_digits + 1);
1053
1054        let ann = create_numeric_event(
1055            &secp,
1056            &key_pair,
1057            &event_id,
1058            2,
1059            num_digits,
1060            is_signed,
1061            precision,
1062            &unit,
1063            event_maturity_epoch,
1064            &nonces,
1065        )
1066        .unwrap();
1067
1068        let att_big = sign_numeric_event(&secp, &key_pair, &ann, 10_000, &nonce_keys).unwrap();
1069        assert_eq!(
1070            att_big.outcomes,
1071            vec!["+", "1", "1", "1"]
1072                .iter()
1073                .map(|s| s.to_string())
1074                .collect::<Vec<_>>()
1075        );
1076        // verify our nonces are the same as the one in the announcement
1077        att_big
1078            .signatures
1079            .iter()
1080            .zip(ann.oracle_event.oracle_nonces.clone())
1081            .for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
1082
1083        let att_small = sign_numeric_event(&secp, &key_pair, &ann, -10_000, &nonce_keys).unwrap();
1084        assert_eq!(
1085            att_small.outcomes,
1086            vec!["-", "1", "1", "1"]
1087                .iter()
1088                .map(|s| s.to_string())
1089                .collect::<Vec<_>>()
1090        );
1091        // verify our nonces are the same as the one in the announcement
1092        att_small
1093            .signatures
1094            .iter()
1095            .zip(ann.oracle_event.oracle_nonces)
1096            .for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
1097    }
1098
1099    #[test]
1100    fn test_error_invalid_event_id() {
1101        let (secp, key_pair) = setup_test_keypair();
1102        let (_, nonce) = setup_test_nonce(&secp);
1103
1104        // Test empty event_id in create_enum_event
1105        let result = create_enum_event(
1106            &secp,
1107            &key_pair,
1108            "",
1109            &vec!["a".to_string(), "b".to_string()],
1110            12345,
1111            &nonce,
1112        );
1113        assert!(matches!(result, Err(Error::InvalidEventId)));
1114
1115        // Test empty event_id in create_numeric_event
1116        let (_, nonces) = setup_test_nonces(&secp, 4);
1117        let result =
1118            create_numeric_event(&secp, &key_pair, "", 2, 4, false, 0, "m/s", 12345, &nonces);
1119        assert!(matches!(result, Err(Error::InvalidEventId)));
1120    }
1121
1122    #[test]
1123    fn test_error_invalid_outcomes() {
1124        let (secp, key_pair) = setup_test_keypair();
1125        let (_, nonce) = setup_test_nonce(&secp);
1126
1127        // Test empty outcomes
1128        let result = create_enum_event(&secp, &key_pair, "test", &vec![], 12345, &nonce);
1129        assert!(matches!(result, Err(Error::InvalidOutcomes)));
1130    }
1131
1132    #[test]
1133    fn test_error_invalid_base() {
1134        let (secp, key_pair) = setup_test_keypair();
1135        let (nonce_keys, nonces) = setup_test_nonces(&secp, 4);
1136
1137        // Test base != 2 in create_numeric_event
1138        let result = create_numeric_event(
1139            &secp, &key_pair, "test", 3, // Invalid base
1140            4, false, 0, "m/s", 12345, &nonces,
1141        );
1142        assert!(matches!(result, Err(Error::InvalidBase)));
1143
1144        // Test base != 2 in sign_numeric_event (by creating announcement with wrong descriptor)
1145        // We can't easily create an announcement with base != 2 since create_numeric_event rejects it,
1146        // but sign_numeric_event also checks base != 2, so we test that path by manually constructing
1147        // an announcement with wrong base in the descriptor
1148        let mut ann = create_numeric_event(
1149            &secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
1150        )
1151        .unwrap();
1152
1153        // Modify the descriptor to have wrong base
1154        if let EventDescriptor::DigitDecompositionEvent(ref mut desc) =
1155            ann.oracle_event.event_descriptor
1156        {
1157            desc.base = 3; // Invalid base
1158        }
1159
1160        let result = sign_numeric_event(&secp, &key_pair, &ann, 5, &nonce_keys);
1161        assert!(matches!(result, Err(Error::InvalidBase)));
1162    }
1163
1164    #[test]
1165    fn test_error_invalid_number_of_digits() {
1166        let (secp, key_pair) = setup_test_keypair();
1167
1168        // Test num_digits == 0
1169        let nonces: Vec<XOnlyPublicKey> = vec![];
1170        let result = create_numeric_event(
1171            &secp, &key_pair, "test", 2, 0, // Invalid: zero digits
1172            false, 0, "m/s", 12345, &nonces,
1173        );
1174        assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
1175
1176        // Test num_digits > 63
1177        let (_, nonces) = setup_test_nonces(&secp, 64);
1178        let result = create_numeric_event(
1179            &secp, &key_pair, "test", 2, 64, // Invalid: > 63
1180            false, 0, "m/s", 12345, &nonces,
1181        );
1182        assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
1183
1184        // Test invalid number of digits in sign_numeric_event
1185        let (nonce_keys, nonces) = setup_test_nonces(&secp, 4);
1186        let mut ann = create_numeric_event(
1187            &secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
1188        )
1189        .unwrap();
1190
1191        // Modify the descriptor to have invalid number of digits
1192        if let EventDescriptor::DigitDecompositionEvent(ref mut desc) =
1193            ann.oracle_event.event_descriptor
1194        {
1195            desc.nb_digits = 0; // Invalid: zero digits
1196        }
1197
1198        let result = sign_numeric_event(&secp, &key_pair, &ann, 5, &nonce_keys);
1199        assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
1200
1201        // Test num_digits > 63 in sign_numeric_event
1202        if let EventDescriptor::DigitDecompositionEvent(ref mut desc) =
1203            ann.oracle_event.event_descriptor
1204        {
1205            desc.nb_digits = 64; // Invalid: > 63
1206        }
1207
1208        let result = sign_numeric_event(&secp, &key_pair, &ann, 5, &nonce_keys);
1209        assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
1210    }
1211
1212    #[test]
1213    fn test_error_invalid_nonces() {
1214        let (secp, key_pair) = setup_test_keypair();
1215
1216        // Test wrong number of nonces for unsigned event
1217        let (_, wrong_nonces) = setup_test_nonces(&secp, 2);
1218
1219        let result = create_numeric_event(
1220            &secp,
1221            &key_pair,
1222            "test",
1223            2,
1224            4,
1225            false,
1226            0,
1227            "m/s",
1228            12345,
1229            &wrong_nonces,
1230        );
1231        assert!(matches!(result, Err(Error::InvalidNonces)));
1232
1233        // Test wrong number of nonces for signed event
1234        let (_, wrong_nonces) = setup_test_nonces(&secp, 1); // 1 nonce for 4-digit signed (should be 5: 4 digits + 1 sign)
1235        let result = create_numeric_event(
1236            &secp,
1237            &key_pair,
1238            "test",
1239            2,
1240            4,
1241            true,
1242            0,
1243            "m/s",
1244            12345,
1245            &wrong_nonces,
1246        );
1247        assert!(matches!(result, Err(Error::InvalidNonces)));
1248
1249        // Test wrong set of nonces
1250        let (_, nonce1) = setup_test_nonce(&secp);
1251        let (nonce_key2, _) = setup_test_nonce(&secp);
1252
1253        let ann = create_enum_event(
1254            &secp,
1255            &key_pair,
1256            "test",
1257            &vec!["a".to_string(), "b".to_string()],
1258            12345,
1259            &nonce1,
1260        )
1261        .unwrap();
1262
1263        let result = sign_enum_event(&secp, &key_pair, &ann, &"a".to_string(), &nonce_key2);
1264
1265        assert!(matches!(result, Err(Error::InvalidNonces)));
1266
1267        let (_, nonces1) = setup_test_nonces(&secp, 4);
1268        let (nonce_keys2, _) = setup_test_nonces(&secp, 4);
1269
1270        let ann = create_numeric_event(
1271            &secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces1,
1272        )
1273        .unwrap();
1274
1275        let result = sign_numeric_event(&secp, &key_pair, &ann, -0b1010, &nonce_keys2);
1276
1277        assert!(matches!(result, Err(Error::InvalidNonces)));
1278    }
1279
1280    #[test]
1281    fn test_error_invalid_outcome() {
1282        let (secp, key_pair) = setup_test_keypair();
1283        let (nonce_key, nonce) = setup_test_nonce(&secp);
1284
1285        // Create an enum event
1286        let ann = create_enum_event(
1287            &secp,
1288            &key_pair,
1289            "test",
1290            &vec!["a".to_string(), "b".to_string()],
1291            12345,
1292            &nonce,
1293        )
1294        .unwrap();
1295
1296        // Try to sign with invalid outcome
1297        let result = sign_enum_event(&secp, &key_pair, &ann, "c", &nonce_key);
1298        assert!(matches!(result, Err(Error::InvalidOutcome)));
1299
1300        // Create a numeric event
1301        let (_, nonces) = setup_test_nonces(&secp, 4);
1302        let _ = create_numeric_event(
1303            &secp, &key_pair, "test", 2, 4, // 4 digits = range 0..=15
1304            false, 0, "m/s", 12345, &nonces,
1305        )
1306        .unwrap();
1307    }
1308
1309    #[test]
1310    fn test_error_invalid_event_descriptor() {
1311        let (secp, key_pair) = setup_test_keypair();
1312
1313        // Create a numeric event
1314        let (_, nonces) = setup_test_nonces(&secp, 4);
1315        let numeric_ann = create_numeric_event(
1316            &secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
1317        )
1318        .unwrap();
1319
1320        // Try to use sign_enum_event on a numeric announcement (wrong event descriptor type)
1321        let (nonce_key, nonce) = setup_test_nonce(&secp);
1322        let result = sign_enum_event(&secp, &key_pair, &numeric_ann, "a", &nonce_key);
1323        assert!(matches!(result, Err(Error::InvalidEventDescriptor)));
1324
1325        // Create an enum event
1326        let enum_ann = create_enum_event(
1327            &secp,
1328            &key_pair,
1329            "test",
1330            &vec!["a".to_string(), "b".to_string()],
1331            12345,
1332            &nonce,
1333        )
1334        .unwrap();
1335
1336        // Try to use sign_numeric_event on an enum announcement (wrong event descriptor type)
1337        let result = sign_numeric_event(&secp, &key_pair, &enum_ann, 42, &vec![nonce_key]);
1338        assert!(matches!(result, Err(Error::InvalidEventDescriptor)));
1339    }
1340
1341    #[test]
1342    fn test_error_invalid_announcement() {
1343        let (secp, key_pair) = setup_test_keypair();
1344        let other_key_pair = Keypair::new(&secp, &mut thread_rng());
1345        let (nonce_key, nonce) = setup_test_nonce(&secp);
1346
1347        // Create an enum event with one key_pair
1348        let ann = create_enum_event(
1349            &secp,
1350            &key_pair,
1351            "test",
1352            &vec!["a".to_string(), "b".to_string()],
1353            12345,
1354            &nonce,
1355        )
1356        .unwrap();
1357
1358        // Try to sign with different key_pair (wrong oracle key)
1359        let result = sign_enum_event(&secp, &other_key_pair, &ann, "a", &nonce_key);
1360        assert!(matches!(result, Err(Error::InvalidAnnouncement)));
1361
1362        // Test wrong oracle key in sign_numeric_event
1363        let (nonce_keys, nonces) = setup_test_nonces(&secp, 4);
1364        let numeric_ann = create_numeric_event(
1365            &secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
1366        )
1367        .unwrap();
1368
1369        let result = sign_numeric_event(&secp, &other_key_pair, &numeric_ann, 5, &nonce_keys);
1370        assert!(matches!(result, Err(Error::InvalidAnnouncement)));
1371    }
1372
1373    #[tokio::test]
1374    async fn test_error_not_found() {
1375        let oracle = setup_test_oracle();
1376
1377        // Try to sign an event that doesn't exist
1378        let result = oracle
1379            .sign_enum_event("nonexistent".to_string(), "a".to_string())
1380            .await;
1381        assert!(matches!(result, Err(Error::NotFound)));
1382
1383        let result = oracle
1384            .sign_numeric_event("nonexistent".to_string(), 42)
1385            .await;
1386        assert!(matches!(result, Err(Error::NotFound)));
1387    }
1388
1389    #[tokio::test]
1390    async fn test_error_event_already_signed() {
1391        let oracle = setup_test_oracle();
1392
1393        // Create and sign an enum event
1394        let event_id = "test_event".to_string();
1395        let _ann = oracle
1396            .create_enum_event(
1397                event_id.clone(),
1398                vec!["a".to_string(), "b".to_string()],
1399                12345,
1400            )
1401            .await
1402            .unwrap();
1403
1404        let _attestation = oracle
1405            .sign_enum_event(event_id.clone(), "a".to_string())
1406            .await
1407            .unwrap();
1408
1409        // Try to sign again
1410        let result = oracle
1411            .sign_enum_event(event_id.clone(), "b".to_string())
1412            .await;
1413        assert!(matches!(result, Err(Error::EventAlreadySigned)));
1414
1415        // Create and sign a numeric event
1416        let numeric_event_id = "numeric_event".to_string();
1417        let _ann = oracle
1418            .create_numeric_event(
1419                numeric_event_id.clone(),
1420                4,
1421                false,
1422                0,
1423                "m/s".to_string(),
1424                12345,
1425            )
1426            .await
1427            .unwrap();
1428
1429        let _attestation = oracle
1430            .sign_numeric_event(numeric_event_id.clone(), 5)
1431            .await
1432            .unwrap();
1433
1434        // Try to sign again
1435        let result = oracle.sign_numeric_event(numeric_event_id, 6).await;
1436        assert!(matches!(result, Err(Error::EventAlreadySigned)));
1437    }
1438
1439    #[tokio::test]
1440    async fn test_error_storage_failure() {
1441        // Create a mock storage that returns StorageFailure
1442        struct FailingStorage;
1443
1444        impl crate::storage::Storage for FailingStorage {
1445            async fn get_next_nonce_indexes(&self, _num: usize) -> Result<Vec<u32>, Error> {
1446                Err(Error::StorageFailure)
1447            }
1448
1449            async fn save_announcement(
1450                &self,
1451                _announcement: OracleAnnouncement,
1452                _indexes: Vec<u32>,
1453            ) -> Result<String, Error> {
1454                Err(Error::StorageFailure)
1455            }
1456
1457            async fn save_signatures(
1458                &self,
1459                _event_id: String,
1460                _sigs: Vec<(String, Signature)>,
1461            ) -> Result<crate::storage::OracleEventData, Error> {
1462                Err(Error::StorageFailure)
1463            }
1464
1465            async fn get_event(
1466                &self,
1467                _event_id: String,
1468            ) -> Result<Option<crate::storage::OracleEventData>, Error> {
1469                Err(Error::StorageFailure)
1470            }
1471        }
1472
1473        let mut seed: [u8; 64] = [0; 64];
1474        thread_rng().fill(&mut seed);
1475        let xpriv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
1476        let oracle = Oracle::from_xpriv(FailingStorage, xpriv).unwrap();
1477
1478        // Test StorageFailure on create_enum_event
1479        let result = oracle
1480            .create_enum_event("test".to_string(), vec!["a".to_string()], 12345)
1481            .await;
1482        assert!(matches!(result, Err(Error::StorageFailure)));
1483
1484        // Test StorageFailure on create_numeric_event
1485        let result = oracle
1486            .create_numeric_event("test".to_string(), 4, false, 0, "m/s".to_string(), 12345)
1487            .await;
1488        assert!(matches!(result, Err(Error::StorageFailure)));
1489
1490        // Test StorageFailure on get_event (sign_enum_event)
1491        let result = oracle
1492            .sign_enum_event("test".to_string(), "a".to_string())
1493            .await;
1494        assert!(matches!(result, Err(Error::StorageFailure)));
1495
1496        // Test StorageFailure on get_event (sign_numeric_event)
1497        let result = oracle.sign_numeric_event("test".to_string(), 42).await;
1498        assert!(matches!(result, Err(Error::StorageFailure)));
1499    }
1500
1501    #[tokio::test]
1502    async fn test_kormir_create_enum_event() {
1503        let oracle = setup_test_oracle();
1504
1505        let event_id = "test".to_string();
1506        let outcomes = vec!["a".to_string(), "b".to_string()];
1507        let event_maturity_epoch = 100;
1508        let ann = oracle
1509            .create_enum_event(event_id.clone(), outcomes.clone(), event_maturity_epoch)
1510            .await
1511            .unwrap();
1512
1513        assert!(ann.validate(&oracle.secp).is_ok());
1514        assert_eq!(ann.oracle_event.event_id, event_id);
1515        assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
1516        assert_eq!(
1517            ann.oracle_event.event_descriptor,
1518            EventDescriptor::EnumEvent(EnumEventDescriptor { outcomes })
1519        );
1520    }
1521
1522    #[tokio::test]
1523    async fn test_kormir_sign_enum_event() {
1524        let oracle = setup_test_oracle();
1525
1526        let event_id = "test".to_string();
1527        let outcomes = vec!["a".to_string(), "b".to_string()];
1528        let event_maturity_epoch = std::time::SystemTime::now()
1529            .duration_since(std::time::UNIX_EPOCH)
1530            .unwrap()
1531            .as_secs() as u32
1532            + 86400;
1533        let ann = oracle
1534            .create_enum_event(event_id.clone(), outcomes.clone(), event_maturity_epoch)
1535            .await
1536            .unwrap();
1537
1538        let attestation = oracle
1539            .sign_enum_event(event_id, "a".to_string())
1540            .await
1541            .unwrap();
1542        assert!(attestation.outcomes.contains(&"a".to_string()));
1543        assert_eq!(attestation.oracle_public_key, oracle.public_key());
1544        assert_eq!(attestation.signatures.len(), 1);
1545        assert_eq!(attestation.outcomes.len(), 1);
1546        let sig = attestation.signatures.first().unwrap();
1547
1548        // check first 32 bytes of signature is expected nonce
1549        let expected_nonce = ann.oracle_event.oracle_nonces.first().unwrap().serialize();
1550        let bytes = sig.encode();
1551        let (rx, _sig) = bytes.split_at(32);
1552
1553        assert_eq!(rx, expected_nonce)
1554    }
1555
1556    #[tokio::test]
1557    async fn test_kormir_create_unsigned_numeric_event() {
1558        let oracle = setup_test_oracle();
1559
1560        let event_id = "test_unsigned_numeric".to_string();
1561        let num_digits = 20;
1562
1563        let event_maturity_epoch = 100;
1564        let ann = oracle
1565            .create_numeric_event(
1566                event_id.clone(),
1567                num_digits,
1568                false,
1569                0,
1570                "m/s".into(),
1571                event_maturity_epoch,
1572            )
1573            .await
1574            .unwrap();
1575
1576        assert!(ann.validate(&oracle.secp).is_ok());
1577        assert_eq!(ann.oracle_event.event_id, event_id);
1578        assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
1579        assert_eq!(
1580            ann.oracle_event.event_descriptor,
1581            EventDescriptor::DigitDecompositionEvent(DigitDecompositionEventDescriptor {
1582                base: 2,
1583                is_signed: false,
1584                unit: "m/s".into(),
1585                precision: 0,
1586                nb_digits: 20,
1587            })
1588        );
1589    }
1590
1591    #[tokio::test]
1592    async fn test_kormir_sign_unsigned_numeric_event() {
1593        let oracle = setup_test_oracle();
1594
1595        let event_id = "test_unsigned_numeric".to_string();
1596        let num_digits = 16;
1597
1598        let event_maturity_epoch = 100;
1599        let ann = oracle
1600            .create_numeric_event(
1601                event_id.clone(),
1602                num_digits,
1603                false,
1604                0,
1605                "m/s".into(),
1606                event_maturity_epoch,
1607            )
1608            .await
1609            .unwrap();
1610
1611        let attestation = oracle
1612            .sign_numeric_event(event_id.clone(), 0x5555)
1613            .await
1614            .unwrap();
1615        assert_eq!(
1616            attestation.outcomes,
1617            vec!["0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1"]
1618                .iter()
1619                .map(|x| x.to_string())
1620                .collect::<Vec<_>>()
1621        );
1622        assert_eq!(attestation.oracle_public_key, oracle.public_key());
1623        assert_eq!(attestation.signatures.len(), 16);
1624        assert_eq!(attestation.outcomes.len(), 16);
1625
1626        for i in 0..attestation.signatures.len() {
1627            let sig = attestation.signatures[i];
1628
1629            // check first 32 bytes of signature is expected nonce
1630            let expected_nonce = ann.oracle_event.oracle_nonces[i].serialize();
1631            let bytes = sig.encode();
1632            let (rx, _sig) = bytes.split_at(32);
1633
1634            assert_eq!(rx, expected_nonce)
1635        }
1636    }
1637
1638    #[tokio::test]
1639    async fn test_kormir_create_signed_numeric_event() {
1640        let oracle = setup_test_oracle();
1641
1642        let event_id = "test_signed_numeric".to_string();
1643        let num_digits = 20;
1644
1645        let event_maturity_epoch = 100;
1646        let ann = oracle
1647            .create_numeric_event(
1648                event_id.clone(),
1649                num_digits,
1650                true,
1651                0,
1652                "m/s".into(),
1653                event_maturity_epoch,
1654            )
1655            .await
1656            .unwrap();
1657
1658        assert!(ann.validate(&oracle.secp).is_ok());
1659        assert_eq!(ann.oracle_event.event_id, event_id);
1660        assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
1661        assert_eq!(
1662            ann.oracle_event.event_descriptor,
1663            EventDescriptor::DigitDecompositionEvent(DigitDecompositionEventDescriptor {
1664                base: 2,
1665                is_signed: true,
1666                unit: "m/s".into(),
1667                precision: 0,
1668                nb_digits: 20,
1669            })
1670        );
1671    }
1672
1673    #[tokio::test]
1674    async fn test_kormir_sign_signed_positive_numeric_event() {
1675        let oracle = setup_test_oracle();
1676
1677        let event_id = "test_signed_numeric".to_string();
1678        let num_digits = 16;
1679
1680        let event_maturity_epoch = 100;
1681        let ann = oracle
1682            .create_numeric_event(
1683                event_id.clone(),
1684                num_digits,
1685                true,
1686                0,
1687                "m/s".into(),
1688                event_maturity_epoch,
1689            )
1690            .await
1691            .unwrap();
1692
1693        let attestation = oracle.sign_numeric_event(event_id, 0x5555).await.unwrap();
1694        assert_eq!(
1695            attestation.outcomes,
1696            vec![
1697                "+", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1"
1698            ]
1699            .iter()
1700            .map(|x| x.to_string())
1701            .collect::<Vec<_>>()
1702        );
1703        assert_eq!(attestation.oracle_public_key, oracle.public_key());
1704        assert_eq!(attestation.signatures.len(), 16 + 1);
1705        assert_eq!(attestation.outcomes.len(), 16 + 1);
1706
1707        for i in 0..attestation.signatures.len() {
1708            let sig = attestation.signatures[i];
1709
1710            // check first 32 bytes of signature is expected nonce
1711            let expected_nonce = ann.oracle_event.oracle_nonces[i].serialize();
1712            let bytes = sig.encode();
1713            let (rx, _sig) = bytes.split_at(32);
1714
1715            assert_eq!(rx, expected_nonce)
1716        }
1717    }
1718
1719    #[tokio::test]
1720    async fn test_kormir_sign_signed_negative_numeric_event() {
1721        let oracle = setup_test_oracle();
1722
1723        let event_id = "test_signed_numeric".to_string();
1724        let num_digits = 16;
1725
1726        let event_maturity_epoch = 100;
1727        let ann = oracle
1728            .create_numeric_event(
1729                event_id.clone(),
1730                num_digits,
1731                true,
1732                0,
1733                "m/s".into(),
1734                event_maturity_epoch,
1735            )
1736            .await
1737            .unwrap();
1738
1739        let attestation = oracle.sign_numeric_event(event_id, -0x5555).await.unwrap();
1740        assert_eq!(
1741            attestation.outcomes,
1742            vec![
1743                "-", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1"
1744            ]
1745            .iter()
1746            .map(|x| x.to_string())
1747            .collect::<Vec<_>>()
1748        );
1749        assert_eq!(attestation.oracle_public_key, oracle.public_key());
1750        assert_eq!(attestation.signatures.len(), 16 + 1);
1751        assert_eq!(attestation.outcomes.len(), 16 + 1);
1752
1753        for i in 0..attestation.signatures.len() {
1754            let sig = attestation.signatures[i];
1755
1756            // check first 32 bytes of signature is expected nonce
1757            let expected_nonce = ann.oracle_event.oracle_nonces[i].serialize();
1758            let bytes = sig.encode();
1759            let (rx, _sig) = bytes.split_at(32);
1760
1761            assert_eq!(rx, expected_nonce)
1762        }
1763    }
1764}