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