ddk_messages/
oracle_msgs.rs

1//! Structs containing oracle information.
2
3use crate::ser_impls::{
4    read_as_tlv, read_i32, read_schnorr_pubkey, read_schnorrsig, read_strings_u16, write_as_tlv,
5    write_i32, write_schnorr_pubkey, write_schnorrsig, write_strings_u16,
6};
7use bitcoin::hashes::Hash;
8use ddk_dlc::{Error, OracleInfo as DlcOracleInfo};
9use lightning::ln::msgs::DecodeError;
10use lightning::ln::wire::Type;
11use lightning::util::ser::{Readable, Writeable, Writer};
12use secp256k1_zkp::Verification;
13use secp256k1_zkp::{schnorr::Signature, Message, Secp256k1, XOnlyPublicKey};
14#[cfg(feature = "use-serde")]
15use serde::{Deserialize, Serialize};
16
17/// The type of the announcement struct.
18pub const ANNOUNCEMENT_TYPE: u16 = 55332;
19/// The type of the attestation struct.
20pub const ATTESTATION_TYPE: u16 = 55400;
21
22#[derive(Clone, Eq, PartialEq, Debug)]
23#[cfg_attr(
24    feature = "use-serde",
25    derive(serde::Serialize, serde::Deserialize),
26    serde(rename_all = "camelCase")
27)]
28/// Information about an oracle used in a contract.
29pub enum OracleInfo {
30    /// Used when a contract uses a single oracle.
31    Single(SingleOracleInfo),
32    /// Used when a contract uses multiple oracles.
33    Multi(MultiOracleInfo),
34}
35
36impl<'a> OracleInfo {
37    /// Returns the first event descriptor.
38    pub fn get_first_event_descriptor(&'a self) -> &'a EventDescriptor {
39        match self {
40            OracleInfo::Single(single) => &single.oracle_announcement.oracle_event.event_descriptor,
41            OracleInfo::Multi(multi) => {
42                &multi.oracle_announcements[0].oracle_event.event_descriptor
43            }
44        }
45    }
46}
47
48impl OracleInfo {
49    /// Returns the closest maturity date amongst all events
50    pub fn get_closest_maturity_date(&self) -> u32 {
51        match self {
52            OracleInfo::Single(s) => s.oracle_announcement.oracle_event.event_maturity_epoch,
53            OracleInfo::Multi(m) => m
54                .oracle_announcements
55                .iter()
56                .map(|x| x.oracle_event.event_maturity_epoch)
57                .min()
58                .expect("to have at least one event"),
59        }
60    }
61
62    /// Checks that the info satisfies the validity conditions.
63    pub fn validate<C: Verification>(&self, secp: &Secp256k1<C>) -> Result<(), Error> {
64        match self {
65            OracleInfo::Single(s) => s.oracle_announcement.validate(secp)?,
66            OracleInfo::Multi(m) => {
67                for o in &m.oracle_announcements {
68                    o.validate(secp)?;
69                }
70            }
71        };
72
73        Ok(())
74    }
75}
76
77impl_dlc_writeable_enum!(
78    OracleInfo, (0, Single), (1, Multi);;;
79);
80
81#[derive(Clone, Eq, PartialEq, Debug)]
82#[cfg_attr(
83    feature = "use-serde",
84    derive(serde::Serialize, serde::Deserialize),
85    serde(rename_all = "camelCase")
86)]
87/// Structure containing information about an oracle to be used as external
88/// data source for a DLC contract.
89pub struct SingleOracleInfo {
90    /// The oracle announcement from the oracle.
91    pub oracle_announcement: OracleAnnouncement,
92}
93
94impl_dlc_writeable!(SingleOracleInfo, {
95    (oracle_announcement, {cb_writeable, write_as_tlv, read_as_tlv })
96});
97
98#[derive(Clone, Eq, PartialEq, Debug)]
99#[cfg_attr(
100    feature = "use-serde",
101    derive(serde::Serialize, serde::Deserialize),
102    serde(rename_all = "camelCase")
103)]
104/// Information about oracles used in multi oracle based contracts.
105pub struct MultiOracleInfo {
106    /// The threshold to be used for the contract (e.g. 2 of 3).
107    pub threshold: u16,
108    /// The set of oracle announcements.
109    pub oracle_announcements: Vec<OracleAnnouncement>,
110    /// The parameters to be used when allowing differences between oracle
111    /// outcomes in numerical outcome contracts.
112    pub oracle_params: Option<OracleParams>,
113}
114
115impl_dlc_writeable!(MultiOracleInfo, {
116    (threshold, writeable),
117    (oracle_announcements, {vec_cb, write_as_tlv, read_as_tlv}),
118    (oracle_params, option)
119});
120
121#[derive(Clone, Eq, PartialEq, Debug)]
122#[cfg_attr(
123    feature = "use-serde",
124    derive(serde::Serialize, serde::Deserialize),
125    serde(rename_all = "camelCase")
126)]
127/// Parameter describing allowed differences between oracles in numerical outcome
128/// contracts.
129pub struct OracleParams {
130    /// The maximum allowed difference between oracle expressed as a power of 2.
131    pub max_error_exp: u16,
132    /// The minimum allowed difference that should be supported by the contract
133    /// expressed as a power of 2.
134    pub min_fail_exp: u16,
135    /// Whether to maximize coverage of the interval between [`Self::max_error_exp`]
136    /// and [`Self::min_fail_exp`].
137    pub maximize_coverage: bool,
138}
139
140impl_dlc_writeable!(OracleParams, {
141    (max_error_exp, writeable),
142    (min_fail_exp, writeable),
143    (maximize_coverage, writeable)
144});
145
146#[derive(Clone, Eq, PartialEq, Debug)]
147#[cfg_attr(
148    feature = "use-serde",
149    derive(Serialize, Deserialize),
150    serde(rename_all = "camelCase")
151)]
152/// An oracle announcement that describe an event and the way that an oracle will
153/// attest to it.
154pub struct OracleAnnouncement {
155    /// The signature enabling verifying the origin of the announcement.
156    pub announcement_signature: Signature,
157    /// The public key of the oracle.
158    pub oracle_public_key: XOnlyPublicKey,
159    /// The description of the event and attesting.
160    pub oracle_event: OracleEvent,
161}
162
163impl Type for OracleAnnouncement {
164    fn type_id(&self) -> u16 {
165        ANNOUNCEMENT_TYPE
166    }
167}
168
169impl OracleAnnouncement {
170    /// Returns whether the announcement satisfy validity checks.
171    pub fn validate<C: Verification>(&self, secp: &Secp256k1<C>) -> Result<(), Error> {
172        let mut event_hex = Vec::new();
173        self.oracle_event
174            .write(&mut event_hex)
175            .expect("Error writing oracle event");
176
177        let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
178        let msg = Message::from_digest(hash.to_byte_array());
179        secp.verify_schnorr(&self.announcement_signature, &msg, &self.oracle_public_key)?;
180        self.oracle_event.validate()
181    }
182}
183
184impl_dlc_writeable!(OracleAnnouncement, {
185    (announcement_signature, {cb_writeable, write_schnorrsig, read_schnorrsig}),
186    (oracle_public_key, {cb_writeable, write_schnorr_pubkey, read_schnorr_pubkey}),
187    (oracle_event, {cb_writeable, write_as_tlv, read_as_tlv})
188});
189
190impl From<&OracleAnnouncement> for DlcOracleInfo {
191    fn from(input: &OracleAnnouncement) -> DlcOracleInfo {
192        DlcOracleInfo {
193            public_key: input.oracle_public_key,
194            nonces: input.oracle_event.oracle_nonces.clone(),
195        }
196    }
197}
198
199#[derive(Clone, Eq, PartialEq, Debug)]
200#[cfg_attr(
201    feature = "use-serde",
202    derive(Serialize, Deserialize),
203    serde(rename_all = "camelCase")
204)]
205/// Information about an event and the way that the oracle will attest to it.
206pub struct OracleEvent {
207    /// The nonces that the oracle will use to attest to the event outcome.
208    pub oracle_nonces: Vec<XOnlyPublicKey>,
209    /// The expected maturity of the contract.
210    // TODO(tibo): should validate that with the contract maturity.
211    pub event_maturity_epoch: u32,
212    /// The description of the event.
213    pub event_descriptor: EventDescriptor,
214    /// The id of the event.
215    pub event_id: String,
216}
217
218impl OracleEvent {
219    /// Returns whether the event passes validity checks.
220    pub fn validate(&self) -> Result<(), Error> {
221        let expected_nb_nonces = match &self.event_descriptor {
222            EventDescriptor::EnumEvent(_) => 1,
223            EventDescriptor::DigitDecompositionEvent(d) => {
224                if d.is_signed {
225                    d.nb_digits as usize + 1
226                } else {
227                    d.nb_digits as usize
228                }
229            }
230        };
231
232        if expected_nb_nonces == self.oracle_nonces.len() {
233            Ok(())
234        } else {
235            Err(Error::InvalidArgument)
236        }
237    }
238}
239
240impl Type for OracleEvent {
241    fn type_id(&self) -> u16 {
242        55330
243    }
244}
245
246impl_dlc_writeable!(OracleEvent, {
247    (oracle_nonces, {vec_u16_cb, write_schnorr_pubkey, read_schnorr_pubkey}),
248    (event_maturity_epoch, writeable),
249    (event_descriptor, writeable),
250    (event_id, string)
251});
252
253#[derive(Clone, PartialEq, Eq, Debug)]
254#[cfg_attr(
255    feature = "use-serde",
256    derive(Serialize, Deserialize),
257    serde(rename_all = "camelCase")
258)]
259/// Description of an event.
260pub enum EventDescriptor {
261    /// Used for events based on enumerated outcomes.
262    EnumEvent(EnumEventDescriptor),
263    /// Used for event based on numerical outcomes.
264    DigitDecompositionEvent(DigitDecompositionEventDescriptor),
265}
266
267impl_dlc_writeable_enum_as_tlv!(EventDescriptor, (55302, EnumEvent), (55306, DigitDecompositionEvent););
268
269#[derive(Clone, PartialEq, Eq, Debug)]
270#[cfg_attr(
271    feature = "use-serde",
272    derive(Serialize, Deserialize),
273    serde(rename_all = "camelCase")
274)]
275/// Describes the outcomes of an event as an enumeration.
276pub struct EnumEventDescriptor {
277    /// The possible outcomes of the event.
278    pub outcomes: Vec<String>,
279}
280
281impl_dlc_writeable!(EnumEventDescriptor, {
282    (outcomes, {cb_writeable, write_strings_u16, read_strings_u16})
283});
284
285#[derive(Clone, PartialEq, Eq, Debug)]
286#[cfg_attr(
287    feature = "use-serde",
288    derive(Serialize, Deserialize),
289    serde(rename_all = "camelCase")
290)]
291/// Describes the outcomes of a numerical outcome event.
292pub struct DigitDecompositionEventDescriptor {
293    /// The base in which the outcome will be represented.
294    pub base: u16,
295    /// Whether the outcome value is signed.
296    pub is_signed: bool,
297    /// The unit in which the outcome is represented.
298    pub unit: String,
299    /// The precision used to represent the event outcome.
300    pub precision: i32,
301    /// The number of digits used to represent the event outcome.
302    // TODO:(tibo) should validate that nb_digits == nb_nonces
303    pub nb_digits: u16,
304}
305
306impl_dlc_writeable!(DigitDecompositionEventDescriptor, {
307    (base, writeable),
308    (is_signed, writeable),
309    (unit, string),
310    (precision, {cb_writeable, write_i32, read_i32}),
311    (nb_digits, writeable)
312});
313
314/// An attestation from an oracle providing signatures over an outcome value.
315#[derive(Clone, Debug, PartialEq, Eq)]
316#[cfg_attr(
317    feature = "use-serde",
318    derive(Serialize, Deserialize),
319    serde(rename_all = "camelCase")
320)]
321pub struct OracleAttestation {
322    /// The identifier of the announcement.
323    pub event_id: String,
324    /// The public key of the oracle.
325    pub oracle_public_key: XOnlyPublicKey,
326    /// The signatures over the event outcome.
327    pub signatures: Vec<Signature>,
328    /// The set of strings representing the outcome value.
329    pub outcomes: Vec<String>,
330}
331
332impl OracleAttestation {
333    /// Returns whether the attestation satisfy validity checks.
334    pub fn validate<C: Verification>(
335        &self,
336        secp: &Secp256k1<C>,
337        announcement: &OracleAnnouncement,
338    ) -> Result<(), Error> {
339        if self.outcomes.len() != self.signatures.len() {
340            return Err(Error::InvalidArgument);
341        }
342
343        if self.oracle_public_key != announcement.oracle_public_key {
344            return Err(Error::InvalidArgument);
345        }
346
347        self.signatures
348            .iter()
349            .zip(self.outcomes.iter())
350            .try_for_each(|(sig, outcome)| {
351                let hash = bitcoin::hashes::sha256::Hash::hash(outcome.as_bytes());
352                let msg = Message::from_digest(hash.to_byte_array());
353                secp.verify_schnorr(sig, &msg, &self.oracle_public_key)
354                    .map_err(|_| Error::InvalidArgument)?;
355
356                Ok::<(), ddk_dlc::Error>(())
357            })?;
358
359        if !self
360            .signatures
361            .iter()
362            .zip(announcement.oracle_event.oracle_nonces.iter())
363            .all(|(sig, nonce)| sig.encode()[..32] == nonce.serialize())
364        {
365            return Err(Error::InvalidArgument);
366        }
367
368        Ok(())
369    }
370    /// Returns the nonces used by the oracle to sign the event outcome.
371    /// This is used for finding the matching oracle announcement.
372    pub fn nonces(&self) -> Vec<XOnlyPublicKey> {
373        self.signatures
374            .iter()
375            .map(|s| XOnlyPublicKey::from_slice(&s[0..32]).expect("valid signature"))
376            .collect()
377    }
378}
379
380impl Type for OracleAttestation {
381    fn type_id(&self) -> u16 {
382        ATTESTATION_TYPE
383    }
384}
385
386impl_dlc_writeable!(OracleAttestation, {
387    (event_id, string),
388    (oracle_public_key, {cb_writeable, write_schnorr_pubkey, read_schnorr_pubkey}),
389    (signatures, {vec_u16_cb, write_schnorrsig, read_schnorrsig}),
390    (outcomes, {cb_writeable, write_strings_u16, read_strings_u16})
391});
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396    use bitcoin::bip32::{ChildNumber, Xpriv};
397    use bitcoin::Network;
398    use secp256k1_zkp::rand::Fill;
399    use secp256k1_zkp::SecretKey;
400    use secp256k1_zkp::{rand::thread_rng, Message, SECP256K1};
401    use secp256k1_zkp::{schnorr::Signature as SchnorrSignature, Keypair, XOnlyPublicKey};
402
403    fn enum_descriptor() -> EnumEventDescriptor {
404        EnumEventDescriptor {
405            outcomes: vec!["1".to_string(), "2".to_string(), "3".to_string()],
406        }
407    }
408
409    fn digit_descriptor() -> DigitDecompositionEventDescriptor {
410        DigitDecompositionEventDescriptor {
411            base: 2,
412            is_signed: false,
413            unit: "kg/sats".to_string(),
414            precision: 1,
415            nb_digits: 10,
416        }
417    }
418
419    fn signed_digit_descriptor() -> DigitDecompositionEventDescriptor {
420        DigitDecompositionEventDescriptor {
421            base: 2,
422            is_signed: true,
423            unit: "kg/sats".to_string(),
424            precision: 1,
425            nb_digits: 10,
426        }
427    }
428
429    fn some_schnorr_pubkey() -> XOnlyPublicKey {
430        let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
431        XOnlyPublicKey::from_keypair(&key_pair).0
432    }
433
434    fn digit_event(nb_nonces: usize) -> OracleEvent {
435        OracleEvent {
436            oracle_nonces: (0..nb_nonces).map(|_| some_schnorr_pubkey()).collect(),
437            event_maturity_epoch: 10,
438            event_descriptor: EventDescriptor::DigitDecompositionEvent(digit_descriptor()),
439            event_id: "test".to_string(),
440        }
441    }
442
443    fn signed_digit_event(nb_nonces: usize) -> OracleEvent {
444        OracleEvent {
445            oracle_nonces: (0..nb_nonces).map(|_| some_schnorr_pubkey()).collect(),
446            event_maturity_epoch: 10,
447            event_descriptor: EventDescriptor::DigitDecompositionEvent(signed_digit_descriptor()),
448            event_id: "test-signed".to_string(),
449        }
450    }
451
452    fn enum_event(nb_nonces: usize) -> OracleEvent {
453        OracleEvent {
454            oracle_nonces: (0..nb_nonces).map(|_| some_schnorr_pubkey()).collect(),
455            event_maturity_epoch: 10,
456            event_descriptor: EventDescriptor::EnumEvent(enum_descriptor()),
457            event_id: "test".to_string(),
458        }
459    }
460
461    fn create_nonce_key() -> (SecretKey, XOnlyPublicKey) {
462        let mut nonce_seed = [0u8; 32];
463        nonce_seed.try_fill(&mut thread_rng()).unwrap();
464        let nonce_priv = Xpriv::new_master(Network::Bitcoin, &nonce_seed)
465            .unwrap()
466            .derive_priv(SECP256K1, &[ChildNumber::from_normal_idx(1).unwrap()])
467            .unwrap()
468            .private_key;
469
470        let nonce_xpub = nonce_priv.x_only_public_key(SECP256K1).0;
471
472        (nonce_priv, nonce_xpub)
473    }
474
475    #[test]
476    fn valid_oracle_announcement_passes_validation_test() {
477        let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
478        let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
479        let events = [digit_event(10), signed_digit_event(11), enum_event(1)];
480        for event in events {
481            let mut event_hex = Vec::new();
482            event
483                .write(&mut event_hex)
484                .expect("Error writing oracle event");
485            let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
486            let msg = Message::from_digest(hash.to_byte_array());
487            let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
488            let valid_announcement = OracleAnnouncement {
489                announcement_signature: sig,
490                oracle_public_key: oracle_pubkey,
491                oracle_event: event,
492            };
493
494            valid_announcement
495                .validate(SECP256K1)
496                .expect("a valid announcement.");
497        }
498    }
499
500    #[test]
501    fn invalid_oracle_announcement_fails_validation_test() {
502        let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
503        let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
504        let events = [digit_event(9), signed_digit_event(10), enum_event(2)];
505        for event in events {
506            let mut event_hex = Vec::new();
507            event
508                .write(&mut event_hex)
509                .expect("Error writing oracle event");
510            let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
511            let msg = Message::from_digest(hash.to_byte_array());
512            let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
513            let invalid_announcement = OracleAnnouncement {
514                announcement_signature: sig,
515                oracle_public_key: oracle_pubkey,
516                oracle_event: event,
517            };
518
519            invalid_announcement
520                .validate(SECP256K1)
521                .expect_err("invalid announcement should fail validation.");
522        }
523    }
524
525    #[test]
526    fn invalid_oracle_announcement_signature_fails_validation_test() {
527        let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
528        let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
529        let event = digit_event(10);
530        let mut event_hex = Vec::new();
531        event
532            .write(&mut event_hex)
533            .expect("Error writing oracle event");
534        let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
535        let msg = Message::from_digest(hash.to_byte_array());
536        let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
537        let mut sig_hex = *sig.as_ref();
538        sig_hex[10] = sig_hex[10].checked_add(1).unwrap_or(0);
539        let sig = SchnorrSignature::from_slice(&sig_hex).unwrap();
540        let invalid_announcement = OracleAnnouncement {
541            announcement_signature: sig,
542            oracle_public_key: oracle_pubkey,
543            oracle_event: event,
544        };
545
546        assert!(invalid_announcement.validate(SECP256K1).is_err());
547    }
548
549    #[test]
550    fn valid_oracle_attestation() {
551        let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
552        let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
553        let (nonce_secret, nonce_xpub) = create_nonce_key();
554
555        let oracle_event = OracleEvent {
556            event_id: "test".to_string(),
557            event_maturity_epoch: 10,
558            oracle_nonces: vec![nonce_xpub],
559            event_descriptor: EventDescriptor::EnumEvent(enum_descriptor()),
560        };
561
562        let mut event_hex = Vec::new();
563        oracle_event
564            .write(&mut event_hex)
565            .expect("Error writing oracle event");
566        let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
567        let msg = Message::from_digest(hash.to_byte_array());
568        let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
569
570        let valid_announcement = OracleAnnouncement {
571            oracle_public_key: oracle_pubkey,
572            announcement_signature: sig,
573            oracle_event,
574        };
575
576        let hash = bitcoin::hashes::sha256::Hash::hash("1".as_bytes());
577        let msg = Message::from_digest(hash.to_byte_array());
578        let sig = ddk_dlc::secp_utils::schnorrsig_sign_with_nonce(
579            SECP256K1,
580            &msg,
581            &key_pair,
582            &nonce_secret.secret_bytes(),
583        );
584
585        let attestation = OracleAttestation {
586            event_id: "test".to_string(),
587            oracle_public_key: oracle_pubkey,
588            signatures: vec![sig],
589            outcomes: vec!["1".to_string()],
590        };
591
592        let validation = attestation.validate(SECP256K1, &valid_announcement);
593
594        assert!(validation.is_ok())
595    }
596
597    #[test]
598    fn invalid_attestation_incorrect_nonce() {
599        let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
600        let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
601        let (_, nonce_xpub) = create_nonce_key();
602        let (incorrect_nonce_secret, _) = create_nonce_key();
603
604        let oracle_event = OracleEvent {
605            event_id: "test".to_string(),
606            event_maturity_epoch: 10,
607            oracle_nonces: vec![nonce_xpub],
608            event_descriptor: EventDescriptor::EnumEvent(enum_descriptor()),
609        };
610
611        let mut event_hex = Vec::new();
612        oracle_event
613            .write(&mut event_hex)
614            .expect("Error writing oracle event");
615        let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
616        let msg = Message::from_digest(hash.to_byte_array());
617        let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
618
619        let valid_announcement = OracleAnnouncement {
620            oracle_public_key: oracle_pubkey,
621            announcement_signature: sig,
622            oracle_event,
623        };
624
625        let hash = bitcoin::hashes::sha256::Hash::hash("1".as_bytes());
626        let msg = Message::from_digest(hash.to_byte_array());
627        let sig = ddk_dlc::secp_utils::schnorrsig_sign_with_nonce(
628            SECP256K1,
629            &msg,
630            &key_pair,
631            &incorrect_nonce_secret.secret_bytes(),
632        );
633
634        let attestation = OracleAttestation {
635            event_id: "test".to_string(),
636            oracle_public_key: oracle_pubkey,
637            signatures: vec![sig],
638            outcomes: vec!["1".to_string()],
639        };
640
641        let validation = attestation.validate(SECP256K1, &valid_announcement);
642
643        assert!(validation.is_err())
644    }
645}