use crate::ser_impls::{
read_as_tlv, read_i32, read_schnorr_pubkey, read_schnorrsig, read_strings_u16, write_as_tlv,
write_i32, write_schnorr_pubkey, write_schnorrsig, write_strings_u16,
};
use bitcoin::hashes::Hash;
use dlc::{Error, OracleInfo as DlcOracleInfo};
use lightning::ln::msgs::DecodeError;
use lightning::ln::wire::Type;
use lightning::util::ser::{Readable, Writeable, Writer};
use secp256k1_zkp::Verification;
use secp256k1_zkp::{schnorr::Signature, Message, Secp256k1, XOnlyPublicKey};
#[cfg(feature = "use-serde")]
use serde::{Deserialize, Serialize};
pub const ANNOUNCEMENT_TYPE: u16 = 55332;
pub const ATTESTATION_TYPE: u16 = 55400;
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
pub enum OracleInfo {
Single(SingleOracleInfo),
Multi(MultiOracleInfo),
}
impl<'a> OracleInfo {
pub fn get_first_event_descriptor(&'a self) -> &'a EventDescriptor {
match self {
OracleInfo::Single(single) => &single.oracle_announcement.oracle_event.event_descriptor,
OracleInfo::Multi(multi) => {
&multi.oracle_announcements[0].oracle_event.event_descriptor
}
}
}
}
impl OracleInfo {
pub fn get_closest_maturity_date(&self) -> u32 {
match self {
OracleInfo::Single(s) => s.oracle_announcement.oracle_event.event_maturity_epoch,
OracleInfo::Multi(m) => m
.oracle_announcements
.iter()
.map(|x| x.oracle_event.event_maturity_epoch)
.min()
.expect("to have at least one event"),
}
}
pub fn validate<C: Verification>(&self, secp: &Secp256k1<C>) -> Result<(), Error> {
match self {
OracleInfo::Single(s) => s.oracle_announcement.validate(secp)?,
OracleInfo::Multi(m) => {
for o in &m.oracle_announcements {
o.validate(secp)?;
}
}
};
Ok(())
}
}
impl_dlc_writeable_enum!(
OracleInfo, (0, Single), (1, Multi);;;
);
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
pub struct SingleOracleInfo {
pub oracle_announcement: OracleAnnouncement,
}
impl_dlc_writeable!(SingleOracleInfo, {
(oracle_announcement, {cb_writeable, write_as_tlv, read_as_tlv })
});
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
pub struct MultiOracleInfo {
pub threshold: u16,
pub oracle_announcements: Vec<OracleAnnouncement>,
pub oracle_params: Option<OracleParams>,
}
impl_dlc_writeable!(MultiOracleInfo, {
(threshold, writeable),
(oracle_announcements, {vec_cb, write_as_tlv, read_as_tlv}),
(oracle_params, option)
});
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
pub struct OracleParams {
pub max_error_exp: u16,
pub min_fail_exp: u16,
pub maximize_coverage: bool,
}
impl_dlc_writeable!(OracleParams, {
(max_error_exp, writeable),
(min_fail_exp, writeable),
(maximize_coverage, writeable)
});
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct OracleAnnouncement {
pub announcement_signature: Signature,
pub oracle_public_key: XOnlyPublicKey,
pub oracle_event: OracleEvent,
}
impl Type for OracleAnnouncement {
fn type_id(&self) -> u16 {
ANNOUNCEMENT_TYPE
}
}
impl OracleAnnouncement {
pub fn validate<C: Verification>(&self, secp: &Secp256k1<C>) -> Result<(), Error> {
let mut event_hex = Vec::new();
self.oracle_event
.write(&mut event_hex)
.expect("Error writing oracle event");
let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
let msg = Message::from_digest(hash.to_byte_array());
secp.verify_schnorr(&self.announcement_signature, &msg, &self.oracle_public_key)?;
self.oracle_event.validate()
}
}
impl_dlc_writeable!(OracleAnnouncement, {
(announcement_signature, {cb_writeable, write_schnorrsig, read_schnorrsig}),
(oracle_public_key, {cb_writeable, write_schnorr_pubkey, read_schnorr_pubkey}),
(oracle_event, {cb_writeable, write_as_tlv, read_as_tlv})
});
impl From<&OracleAnnouncement> for DlcOracleInfo {
fn from(input: &OracleAnnouncement) -> DlcOracleInfo {
DlcOracleInfo {
public_key: input.oracle_public_key,
nonces: input.oracle_event.oracle_nonces.clone(),
}
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct OracleEvent {
pub oracle_nonces: Vec<XOnlyPublicKey>,
pub event_maturity_epoch: u32,
pub event_descriptor: EventDescriptor,
pub event_id: String,
}
impl OracleEvent {
pub fn validate(&self) -> Result<(), Error> {
let expected_nb_nonces = match &self.event_descriptor {
EventDescriptor::EnumEvent(_) => 1,
EventDescriptor::DigitDecompositionEvent(d) => d.nb_digits as usize,
};
if expected_nb_nonces == self.oracle_nonces.len() {
Ok(())
} else {
Err(Error::InvalidArgument)
}
}
}
impl Type for OracleEvent {
fn type_id(&self) -> u16 {
55330
}
}
impl_dlc_writeable!(OracleEvent, {
(oracle_nonces, {vec_u16_cb, write_schnorr_pubkey, read_schnorr_pubkey}),
(event_maturity_epoch, writeable),
(event_descriptor, writeable),
(event_id, string)
});
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub enum EventDescriptor {
EnumEvent(EnumEventDescriptor),
DigitDecompositionEvent(DigitDecompositionEventDescriptor),
}
impl_dlc_writeable_enum_as_tlv!(EventDescriptor, (55302, EnumEvent), (55306, DigitDecompositionEvent););
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct EnumEventDescriptor {
pub outcomes: Vec<String>,
}
impl_dlc_writeable!(EnumEventDescriptor, {
(outcomes, {cb_writeable, write_strings_u16, read_strings_u16})
});
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct DigitDecompositionEventDescriptor {
pub base: u16,
pub is_signed: bool,
pub unit: String,
pub precision: i32,
pub nb_digits: u16,
}
impl_dlc_writeable!(DigitDecompositionEventDescriptor, {
(base, writeable),
(is_signed, writeable),
(unit, string),
(precision, {cb_writeable, write_i32, read_i32}),
(nb_digits, writeable)
});
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct OracleAttestation {
pub oracle_public_key: XOnlyPublicKey,
pub signatures: Vec<Signature>,
pub outcomes: Vec<String>,
}
impl OracleAttestation {
pub fn nonces(&self) -> Vec<XOnlyPublicKey> {
self.signatures
.iter()
.map(|s| XOnlyPublicKey::from_slice(&s[0..32]).expect("valid signature"))
.collect()
}
}
impl Type for OracleAttestation {
fn type_id(&self) -> u16 {
ATTESTATION_TYPE
}
}
impl_dlc_writeable!(OracleAttestation, {
(oracle_public_key, {cb_writeable, write_schnorr_pubkey, read_schnorr_pubkey}),
(signatures, {vec_u16_cb, write_schnorrsig, read_schnorrsig}),
(outcomes, {cb_writeable, write_strings_u16, read_strings_u16})
});
#[cfg(test)]
mod tests {
use super::*;
use secp256k1_zkp::{rand::thread_rng, Message, SECP256K1};
use secp256k1_zkp::{schnorr::Signature as SchnorrSignature, Keypair, XOnlyPublicKey};
fn enum_descriptor() -> EnumEventDescriptor {
EnumEventDescriptor {
outcomes: vec!["1".to_string(), "2".to_string(), "3".to_string()],
}
}
fn digit_descriptor() -> DigitDecompositionEventDescriptor {
DigitDecompositionEventDescriptor {
base: 2,
is_signed: false,
unit: "kg/sats".to_string(),
precision: 1,
nb_digits: 10,
}
}
fn some_schnorr_pubkey() -> XOnlyPublicKey {
let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
XOnlyPublicKey::from_keypair(&key_pair).0
}
fn digit_event(nb_nonces: usize) -> OracleEvent {
OracleEvent {
oracle_nonces: (0..nb_nonces).map(|_| some_schnorr_pubkey()).collect(),
event_maturity_epoch: 10,
event_descriptor: EventDescriptor::DigitDecompositionEvent(digit_descriptor()),
event_id: "test".to_string(),
}
}
fn enum_event(nb_nonces: usize) -> OracleEvent {
OracleEvent {
oracle_nonces: (0..nb_nonces).map(|_| some_schnorr_pubkey()).collect(),
event_maturity_epoch: 10,
event_descriptor: EventDescriptor::EnumEvent(enum_descriptor()),
event_id: "test".to_string(),
}
}
#[test]
fn valid_oracle_announcement_passes_validation_test() {
let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
let events = [digit_event(10), enum_event(1)];
for event in events {
let mut event_hex = Vec::new();
event
.write(&mut event_hex)
.expect("Error writing oracle event");
let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
let msg = Message::from_digest(hash.to_byte_array());
let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
let valid_announcement = OracleAnnouncement {
announcement_signature: sig,
oracle_public_key: oracle_pubkey,
oracle_event: event,
};
valid_announcement
.validate(SECP256K1)
.expect("a valid announcement.");
}
}
#[test]
fn invalid_oracle_announcement_fails_validation_test() {
let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
let events = [digit_event(9), enum_event(2)];
for event in events {
let mut event_hex = Vec::new();
event
.write(&mut event_hex)
.expect("Error writing oracle event");
let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
let msg = Message::from_digest(hash.to_byte_array());
let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
let invalid_announcement = OracleAnnouncement {
announcement_signature: sig,
oracle_public_key: oracle_pubkey,
oracle_event: event,
};
invalid_announcement
.validate(SECP256K1)
.expect_err("invalid announcement should fail validation.");
}
}
#[test]
fn invalid_oracle_announcement_signature_fails_validation_test() {
let key_pair = Keypair::new(SECP256K1, &mut thread_rng());
let oracle_pubkey = XOnlyPublicKey::from_keypair(&key_pair).0;
let event = digit_event(10);
let mut event_hex = Vec::new();
event
.write(&mut event_hex)
.expect("Error writing oracle event");
let hash = bitcoin::hashes::sha256::Hash::hash(&event_hex);
let msg = Message::from_digest(hash.to_byte_array());
let sig = SECP256K1.sign_schnorr(&msg, &key_pair);
let mut sig_hex = *sig.as_ref();
sig_hex[10] = sig_hex[10].checked_add(1).unwrap_or(0);
let sig = SchnorrSignature::from_slice(&sig_hex).unwrap();
let invalid_announcement = OracleAnnouncement {
announcement_signature: sig,
oracle_public_key: oracle_pubkey,
oracle_event: event,
};
assert!(invalid_announcement.validate(SECP256K1).is_err());
}
}