#![allow(async_fn_in_trait)]
pub mod error;
#[cfg(feature = "nostr")]
pub mod nostr_events;
pub mod storage;
use crate::error::Error;
use crate::storage::Storage;
use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv};
use bitcoin::hashes::{sha256, Hash};
use bitcoin::key::XOnlyPublicKey;
use bitcoin::secp256k1::{All, Secp256k1, SecretKey};
use bitcoin::Network;
use secp256k1_zkp::Keypair;
use std::cmp::{max, min};
use std::str::FromStr;
pub use bitcoin;
pub use bitcoin::secp256k1::schnorr::Signature;
pub use ddk_messages::oracle_msgs::{
DigitDecompositionEventDescriptor, EnumEventDescriptor, EventDescriptor, OracleAnnouncement,
OracleAttestation, OracleEvent,
};
pub use lightning;
pub use lightning::util::ser::{Readable, Writeable};
#[cfg(feature = "nostr")]
pub use nostr;
const SIGNING_KEY_PATH: &str = "m/86'/0'/0'/0/0";
pub fn create_enum_event(
secp: &Secp256k1<All>,
key_pair: &Keypair,
event_id: &str,
outcomes: &[String],
event_maturity_epoch: u32,
nonce: &XOnlyPublicKey,
) -> Result<OracleAnnouncement, Error> {
if event_id.is_empty() {
return Err(Error::InvalidEventId);
}
if outcomes.is_empty() {
return Err(Error::InvalidOutcomes);
}
let oracle_nonces = vec![*nonce];
let event_descriptor = EventDescriptor::EnumEvent(EnumEventDescriptor {
outcomes: outcomes.to_owned(),
});
let oracle_event = OracleEvent {
oracle_nonces,
event_id: event_id.to_owned(),
event_maturity_epoch,
event_descriptor,
};
oracle_event.validate().map_err(|_| Error::Internal)?;
let msg = ddk_messages::oracle_msgs::tagged_announcement_msg(&oracle_event);
let announcement_signature = secp.sign_schnorr_no_aux_rand(&msg, key_pair);
let ann = OracleAnnouncement {
oracle_event,
oracle_public_key: key_pair.public_key().x_only_public_key().0,
announcement_signature,
};
ann.validate(secp).map_err(|_| Error::Internal)?;
Ok(ann)
}
pub fn sign_enum_event(
secp: &Secp256k1<All>,
key_pair: &Keypair,
announcement: &OracleAnnouncement,
outcome: &str,
nonce_key: &SecretKey,
) -> Result<OracleAttestation, Error> {
let descriptor = match &announcement.oracle_event.event_descriptor {
EventDescriptor::EnumEvent(desc) => desc,
_ => return Err(Error::InvalidEventDescriptor),
};
if !descriptor.outcomes.contains(&outcome.to_owned()) {
return Err(Error::InvalidOutcome);
}
if key_pair.x_only_public_key().0 != announcement.oracle_public_key {
return Err(Error::InvalidAnnouncement);
}
if announcement.oracle_event.oracle_nonces.is_empty()
|| nonce_key.x_only_public_key(secp).0 != announcement.oracle_event.oracle_nonces[0]
{
return Err(Error::InvalidNonces);
}
let msg = ddk_messages::oracle_msgs::tagged_attestation_msg(outcome);
let sig = ddk_dlc::secp_utils::schnorrsig_sign_with_nonce(
secp,
&msg,
key_pair,
&nonce_key.secret_bytes(),
);
if secp
.verify_schnorr(&sig, &msg, &key_pair.x_only_public_key().0)
.is_err()
{
return Err(Error::Internal);
};
let attestation = OracleAttestation {
event_id: announcement.oracle_event.event_id.clone(),
oracle_public_key: key_pair.public_key().x_only_public_key().0,
signatures: vec![sig],
outcomes: vec![outcome.to_owned()],
};
Ok(attestation)
}
#[allow(clippy::too_many_arguments)]
pub fn create_numeric_event(
secp: &Secp256k1<All>,
key_pair: &Keypair,
event_id: &str,
base: u16,
num_digits: u16,
is_signed: bool,
precision: i32,
unit: &str,
event_maturity_epoch: u32,
nonces: &[XOnlyPublicKey],
) -> Result<OracleAnnouncement, Error> {
if event_id.is_empty() {
return Err(Error::InvalidEventId);
}
if base != 2 {
return Err(Error::InvalidBase);
}
if num_digits == 0 || num_digits > 63 {
return Err(Error::InvalidNumberOfDigits);
}
let num_nonces = if is_signed {
num_digits as usize + 1
} else {
num_digits as usize
};
if nonces.len() != num_nonces {
return Err(Error::InvalidNonces);
}
let event_descriptor =
EventDescriptor::DigitDecompositionEvent(DigitDecompositionEventDescriptor {
base,
is_signed,
unit: unit.to_owned(),
precision,
nb_digits: num_digits,
});
let oracle_event = OracleEvent {
oracle_nonces: nonces.to_owned(),
event_id: event_id.to_owned(),
event_maturity_epoch,
event_descriptor,
};
oracle_event.validate().map_err(|_| Error::Internal)?;
let msg = ddk_messages::oracle_msgs::tagged_announcement_msg(&oracle_event);
let announcement_signature = secp.sign_schnorr_no_aux_rand(&msg, key_pair);
let ann = OracleAnnouncement {
oracle_event,
oracle_public_key: key_pair.x_only_public_key().0,
announcement_signature,
};
ann.validate(secp).map_err(|_| Error::Internal)?;
Ok(ann)
}
pub fn sign_numeric_event(
secp: &Secp256k1<All>,
key_pair: &Keypair,
announcement: &OracleAnnouncement,
outcome: i64,
nonce_keys: &[SecretKey],
) -> Result<OracleAttestation, Error> {
let descriptor = match &announcement.oracle_event.event_descriptor {
EventDescriptor::DigitDecompositionEvent(desc) => desc,
_ => return Err(Error::InvalidEventDescriptor),
};
if descriptor.base != 2 {
return Err(Error::InvalidBase);
}
if key_pair.x_only_public_key().0 != announcement.oracle_public_key {
return Err(Error::InvalidAnnouncement);
}
nonce_keys
.iter()
.zip(&announcement.oracle_event.oracle_nonces)
.try_for_each(|(nonce_key, nonce)| {
if nonce_key.x_only_public_key(secp).0 != *nonce {
Err(Error::InvalidNonces)
} else {
Ok(())
}
})?;
let max_value = get_max_value(descriptor)?;
let min_value = get_min_value(descriptor)?;
let outcome_to_sign = if outcome < min_value || outcome > max_value {
max(min(outcome, max_value), min_value)
} else {
outcome
};
let digits = format!(
"{:0width$b}",
outcome_to_sign.abs(),
width = descriptor.nb_digits as usize
)
.chars()
.map(|char| char.to_string())
.collect::<Vec<_>>();
let outcomes = if descriptor.is_signed {
let mut sign = vec![if outcome_to_sign < 0 {
"-".to_string()
} else {
"+".to_string()
}];
sign.extend(digits);
sign
} else {
digits
};
if nonce_keys.len() != outcomes.len() {
return Err(Error::InvalidNonces);
}
let signatures = outcomes
.iter()
.zip(nonce_keys)
.map(|(outcome, nonce_key)| {
let msg = ddk_messages::oracle_msgs::tagged_attestation_msg(outcome);
let sig = ddk_dlc::secp_utils::schnorrsig_sign_with_nonce(
secp,
&msg,
key_pair,
&nonce_key.secret_bytes(),
);
if secp
.verify_schnorr(&sig, &msg, &key_pair.x_only_public_key().0)
.is_err()
{
return Err(Error::Internal);
};
Ok(sig)
})
.collect::<Result<Vec<_>, Error>>()?;
let attestation = OracleAttestation {
event_id: announcement.oracle_event.event_id.clone(),
oracle_public_key: key_pair.x_only_public_key().0,
signatures,
outcomes,
};
Ok(attestation)
}
pub fn get_min_value(descriptor: &DigitDecompositionEventDescriptor) -> Result<i64, Error> {
if descriptor.is_signed {
get_max_value(descriptor).map(|x| -x)
} else {
Ok(0)
}
}
pub fn get_max_value(descriptor: &DigitDecompositionEventDescriptor) -> Result<i64, Error> {
if descriptor.nb_digits == 0 || descriptor.nb_digits > 63 {
Err(Error::InvalidNumberOfDigits)
} else {
Ok((descriptor.base as i64).pow(descriptor.nb_digits as u32) - 1)
}
}
#[derive(Debug, Clone)]
pub struct Oracle<S: Storage> {
pub storage: S,
key_pair: Keypair,
nonce_xpriv: Xpriv,
secp: Secp256k1<All>,
}
impl<S: Storage> Oracle<S> {
pub fn new(storage: S, signing_key: SecretKey, nonce_xpriv: Xpriv) -> Self {
let secp = Secp256k1::new();
Self {
storage,
key_pair: Keypair::from_secret_key(&secp, &signing_key),
nonce_xpriv,
secp,
}
}
pub fn from_xpriv(storage: S, xpriv: Xpriv) -> Result<Self, Error> {
let secp = Secp256k1::new();
let signing_key = derive_signing_key(&secp, xpriv)?;
Self::from_signing_key(storage, signing_key)
}
pub fn from_signing_key(storage: S, signing_key: SecretKey) -> Result<Self, Error> {
let secp = Secp256k1::new();
let xpriv_bytes = sha256::Hash::hash(&signing_key.secret_bytes()).to_byte_array();
let nonce_xpriv =
Xpriv::new_master(Network::Bitcoin, &xpriv_bytes).map_err(|_| Error::Internal)?;
Ok(Self {
storage,
key_pair: Keypair::from_secret_key(&secp, &signing_key),
nonce_xpriv,
secp,
})
}
pub fn public_key(&self) -> XOnlyPublicKey {
self.key_pair.x_only_public_key().0
}
#[cfg(feature = "nostr")]
pub fn nostr_keys(&self) -> nostr::Keys {
let sec = nostr::key::SecretKey::from_slice(&self.key_pair.secret_key().secret_bytes()[..])
.expect("just converting types");
nostr::Keys::new(sec)
}
fn get_nonce_key(&self, index: u32) -> SecretKey {
self.nonce_xpriv
.derive_priv(
&self.secp,
&[ChildNumber::from_hardened_idx(index).unwrap()],
)
.unwrap()
.private_key
}
pub async fn create_enum_event(
&self,
event_id: String,
outcomes: Vec<String>,
event_maturity_epoch: u32,
) -> Result<OracleAnnouncement, Error> {
let nonce_indexes = self.storage.get_next_nonce_indexes(1).await?;
if nonce_indexes.len() != 1 {
return Err(Error::Internal);
}
let nonce_key = self.get_nonce_key(nonce_indexes[0]);
let nonce = nonce_key.x_only_public_key(&self.secp).0;
let ann = create_enum_event(
&self.secp,
&self.key_pair,
&event_id,
&outcomes,
event_maturity_epoch,
&nonce,
)?;
let _ = self
.storage
.save_announcement(ann.clone(), nonce_indexes)
.await?;
Ok(ann)
}
pub async fn sign_enum_event(
&self,
event_id: String,
outcome: String,
) -> Result<OracleAttestation, Error> {
let Some(data) = self.storage.get_event(event_id.clone()).await? else {
return Err(Error::NotFound);
};
if !data.signatures.is_empty() {
return Err(Error::EventAlreadySigned);
}
if data.indexes.len() != 1 {
return Err(Error::Internal);
}
let nonce_index = data.indexes[0];
let nonce_key = self.get_nonce_key(nonce_index);
let attestation = sign_enum_event(
&self.secp,
&self.key_pair,
&data.announcement,
&outcome,
&nonce_key,
)?;
let sigs = vec![(outcome.clone(), attestation.signatures.clone()[0])];
self.storage
.save_signatures(event_id.to_string(), sigs)
.await?;
Ok(attestation)
}
pub async fn create_numeric_event(
&self,
event_id: String,
num_digits: u16,
is_signed: bool,
precision: i32,
unit: String,
event_maturity_epoch: u32,
) -> Result<OracleAnnouncement, Error> {
let num_nonces = if is_signed {
num_digits as usize + 1
} else {
num_digits as usize
};
let indexes = self.storage.get_next_nonce_indexes(num_nonces).await?;
let oracle_nonces = indexes
.iter()
.map(|i| {
let nonce_key = self.get_nonce_key(*i);
nonce_key.x_only_public_key(&self.secp).0
})
.collect::<Vec<XOnlyPublicKey>>();
let ann = create_numeric_event(
&self.secp,
&self.key_pair,
&event_id,
2,
num_digits,
is_signed,
precision,
&unit,
event_maturity_epoch,
&oracle_nonces,
)?;
let _ = self.storage.save_announcement(ann.clone(), indexes).await?;
Ok(ann)
}
pub async fn sign_numeric_event(
&self,
event_id: String,
outcome: i64,
) -> Result<OracleAttestation, Error> {
let Some(data) = self.storage.get_event(event_id.clone()).await? else {
return Err(Error::NotFound);
};
if !data.signatures.is_empty() {
return Err(Error::EventAlreadySigned);
}
let nonce_keys = data
.indexes
.iter()
.map(|i| self.get_nonce_key(*i))
.collect::<Vec<SecretKey>>();
let attestation = sign_numeric_event(
&self.secp,
&self.key_pair,
&data.announcement,
outcome,
&nonce_keys,
)?;
let sigs = attestation
.outcomes
.iter()
.cloned()
.zip(attestation.signatures.clone())
.collect();
self.storage.save_signatures(event_id, sigs).await?;
Ok(attestation)
}
}
pub fn derive_signing_key(secp: &Secp256k1<All>, xpriv: Xpriv) -> Result<SecretKey, Error> {
let signing_key = xpriv
.derive_priv(
secp,
&DerivationPath::from_str(SIGNING_KEY_PATH).map_err(|_| Error::Internal)?,
)
.map_err(|_| Error::Internal)?
.private_key;
Ok(signing_key)
}
#[cfg(test)]
mod test {
use super::*;
use crate::storage::MemoryStorage;
use bitcoin::secp256k1::rand::{thread_rng, Rng};
fn setup_test_oracle() -> Oracle<MemoryStorage> {
let mut seed: [u8; 64] = [0; 64];
thread_rng().fill(&mut seed);
let xpriv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
Oracle::from_xpriv(MemoryStorage::default(), xpriv).unwrap()
}
fn setup_test_keypair() -> (Secp256k1<All>, Keypair) {
let mut seed: [u8; 64] = [0; 64];
thread_rng().fill(&mut seed);
let xpriv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
let secp = Secp256k1::new();
let signing_key = derive_signing_key(&secp, xpriv).unwrap();
let key_pair = Keypair::from_secret_key(&secp, &signing_key);
(secp, key_pair)
}
fn setup_test_nonce(secp: &Secp256k1<All>) -> (SecretKey, XOnlyPublicKey) {
let mut bytes: [u8; 32] = [0; 32];
thread_rng().fill(&mut bytes);
let nonce_key = SecretKey::from_slice(&bytes).unwrap();
(nonce_key, nonce_key.x_only_public_key(&secp).0)
}
fn setup_test_nonces(secp: &Secp256k1<All>, n: u16) -> (Vec<SecretKey>, Vec<XOnlyPublicKey>) {
let nonce_keys: Vec<SecretKey> = (0..n)
.map(|_| {
let mut bytes: [u8; 32] = [0; 32];
thread_rng().fill(&mut bytes);
SecretKey::from_slice(&bytes).unwrap()
})
.collect();
let nonces: Vec<XOnlyPublicKey> = nonce_keys
.iter()
.map(|nonce_key| nonce_key.x_only_public_key(&secp).0)
.collect();
(nonce_keys, nonces)
}
#[test]
fn test_create_enum_event() {
let (secp, key_pair) = setup_test_keypair();
let event_id = "enum".to_string();
let outcomes = vec!["x".to_string(), "y".to_string()];
let event_maturity_epoch = 12345u32;
let (_, nonce) = setup_test_nonce(&secp);
let ann = create_enum_event(
&secp,
&key_pair,
&event_id,
&outcomes,
event_maturity_epoch,
&nonce,
)
.unwrap();
assert!(ann.validate(&secp).is_ok());
assert_eq!(ann.oracle_event.event_id, event_id);
assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
assert_eq!(ann.oracle_event.oracle_nonces, vec![nonce]);
match ann.oracle_event.event_descriptor {
EventDescriptor::EnumEvent(d) => {
assert_eq!(d.outcomes, outcomes);
}
EventDescriptor::DigitDecompositionEvent(_) => {
assert!(false, "invalid event descriptor type")
}
}
}
#[test]
fn test_sign_enum_event() {
let (secp, key_pair) = setup_test_keypair();
let event_id = "enum_sign".to_string();
let outcomes = vec!["a".to_string(), "b".to_string()];
let event_maturity_epoch = 67890u32;
let (nonce_key, nonce) = setup_test_nonce(&secp);
let ann = create_enum_event(
&secp,
&key_pair,
&event_id,
&outcomes,
event_maturity_epoch,
&nonce,
)
.unwrap();
let attestation =
sign_enum_event(&secp, &key_pair, &ann, &"a".to_string(), &nonce_key).unwrap();
assert!(attestation.outcomes.contains(&"a".to_string()));
assert_eq!(
attestation.oracle_public_key,
key_pair.x_only_public_key().0
);
assert_eq!(attestation.signatures.len(), 1);
assert_eq!(
attestation.signatures[0].encode()[..32],
ann.oracle_event.oracle_nonces[0].serialize()
);
}
#[test]
fn test_create_numeric_event() {
let (secp, key_pair) = setup_test_keypair();
let event_id = "numeric".to_string();
let num_digits = 8u16;
let is_signed = false;
let precision = 0i32;
let unit = "m/s".to_string();
let event_maturity_epoch = 1111u32;
let (_, nonces) = setup_test_nonces(&secp, num_digits);
let ann = create_numeric_event(
&secp,
&key_pair,
&event_id,
2,
num_digits,
is_signed,
precision,
&unit,
event_maturity_epoch,
&nonces,
)
.unwrap();
assert!(ann.validate(&secp).is_ok());
assert_eq!(ann.oracle_event.event_id, event_id);
assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
assert_eq!(ann.oracle_event.oracle_nonces, nonces);
match ann.oracle_event.event_descriptor {
EventDescriptor::EnumEvent(_) => {
assert!(false, "invalid event descriptor type")
}
EventDescriptor::DigitDecompositionEvent(d) => {
assert_eq!(d.base, 2);
assert_eq!(d.is_signed, is_signed);
assert_eq!(d.unit, unit);
assert_eq!(d.precision, precision);
assert_eq!(d.nb_digits, num_digits);
}
}
}
#[test]
fn test_sign_numeric_event() {
let (secp, key_pair) = setup_test_keypair();
let event_id = "numeric_sign".to_string();
let num_digits = 4u16;
let is_signed = true;
let precision = 0i32;
let unit = "m/s".to_string();
let event_maturity_epoch = 2222u32;
let (nonce_keys, nonces) = setup_test_nonces(&secp, num_digits + 1);
let ann = create_numeric_event(
&secp,
&key_pair,
&event_id,
2,
num_digits,
is_signed,
precision,
&unit,
event_maturity_epoch,
&nonces,
)
.unwrap();
let attestation = sign_numeric_event(&secp, &key_pair, &ann, -0b1010, &nonce_keys).unwrap();
assert_eq!(
attestation.outcomes,
vec!["-", "1", "0", "1", "0"]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
);
assert_eq!(attestation.outcomes.len(), (num_digits as usize) + 1);
assert_eq!(attestation.signatures.len(), (num_digits as usize) + 1);
assert_eq!(
attestation.oracle_public_key,
key_pair.x_only_public_key().0
);
attestation
.signatures
.iter()
.zip(ann.oracle_event.oracle_nonces)
.for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
}
#[test]
fn test_sign_numeric_event_clamping_unsigned() {
let (secp, key_pair) = setup_test_keypair();
let event_id = "unsigned".to_string();
let num_digits = 4u16; let is_signed = false;
let precision = 0i32;
let unit = "m/s".to_string();
let event_maturity_epoch = 3333u32;
let (nonce_keys, nonces) = setup_test_nonces(&secp, num_digits);
let ann = create_numeric_event(
&secp,
&key_pair,
&event_id,
2,
num_digits,
is_signed,
precision,
&unit,
event_maturity_epoch,
&nonces,
)
.unwrap();
let att_big = sign_numeric_event(&secp, &key_pair, &ann, 1_000_000, &nonce_keys).unwrap();
assert_eq!(
att_big.outcomes,
vec!["1", "1", "1", "1"]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
);
att_big
.signatures
.iter()
.zip(ann.oracle_event.oracle_nonces.clone())
.for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
let att_neg = sign_numeric_event(&secp, &key_pair, &ann, -42, &nonce_keys).unwrap();
assert_eq!(
att_neg.outcomes,
vec!["0", "0", "0", "0"]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
);
att_neg
.signatures
.iter()
.zip(ann.oracle_event.oracle_nonces.clone())
.for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
}
#[test]
fn test_sign_numeric_event_clamping_signed() {
let (secp, key_pair) = setup_test_keypair();
let event_id = "signed".to_string();
let num_digits = 3u16; let is_signed = true;
let precision = 0i32;
let unit = "m/s".to_string();
let event_maturity_epoch = 4444u32;
let (nonce_keys, nonces) = setup_test_nonces(&secp, num_digits + 1);
let ann = create_numeric_event(
&secp,
&key_pair,
&event_id,
2,
num_digits,
is_signed,
precision,
&unit,
event_maturity_epoch,
&nonces,
)
.unwrap();
let att_big = sign_numeric_event(&secp, &key_pair, &ann, 10_000, &nonce_keys).unwrap();
assert_eq!(
att_big.outcomes,
vec!["+", "1", "1", "1"]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
);
att_big
.signatures
.iter()
.zip(ann.oracle_event.oracle_nonces.clone())
.for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
let att_small = sign_numeric_event(&secp, &key_pair, &ann, -10_000, &nonce_keys).unwrap();
assert_eq!(
att_small.outcomes,
vec!["-", "1", "1", "1"]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
);
att_small
.signatures
.iter()
.zip(ann.oracle_event.oracle_nonces)
.for_each(|(sig, nonce)| assert_eq!(sig.encode()[..32], nonce.serialize()));
}
#[test]
fn test_error_invalid_event_id() {
let (secp, key_pair) = setup_test_keypair();
let (_, nonce) = setup_test_nonce(&secp);
let result = create_enum_event(
&secp,
&key_pair,
"",
&vec!["a".to_string(), "b".to_string()],
12345,
&nonce,
);
assert!(matches!(result, Err(Error::InvalidEventId)));
let (_, nonces) = setup_test_nonces(&secp, 4);
let result =
create_numeric_event(&secp, &key_pair, "", 2, 4, false, 0, "m/s", 12345, &nonces);
assert!(matches!(result, Err(Error::InvalidEventId)));
}
#[test]
fn test_error_invalid_outcomes() {
let (secp, key_pair) = setup_test_keypair();
let (_, nonce) = setup_test_nonce(&secp);
let result = create_enum_event(&secp, &key_pair, "test", &vec![], 12345, &nonce);
assert!(matches!(result, Err(Error::InvalidOutcomes)));
}
#[test]
fn test_error_invalid_base() {
let (secp, key_pair) = setup_test_keypair();
let (nonce_keys, nonces) = setup_test_nonces(&secp, 4);
let result = create_numeric_event(
&secp, &key_pair, "test", 3, 4, false, 0, "m/s", 12345, &nonces,
);
assert!(matches!(result, Err(Error::InvalidBase)));
let mut ann = create_numeric_event(
&secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
)
.unwrap();
if let EventDescriptor::DigitDecompositionEvent(ref mut desc) =
ann.oracle_event.event_descriptor
{
desc.base = 3; }
let result = sign_numeric_event(&secp, &key_pair, &ann, 5, &nonce_keys);
assert!(matches!(result, Err(Error::InvalidBase)));
}
#[test]
fn test_error_invalid_number_of_digits() {
let (secp, key_pair) = setup_test_keypair();
let nonces: Vec<XOnlyPublicKey> = vec![];
let result = create_numeric_event(
&secp, &key_pair, "test", 2, 0, false, 0, "m/s", 12345, &nonces,
);
assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
let (_, nonces) = setup_test_nonces(&secp, 64);
let result = create_numeric_event(
&secp, &key_pair, "test", 2, 64, false, 0, "m/s", 12345, &nonces,
);
assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
let (nonce_keys, nonces) = setup_test_nonces(&secp, 4);
let mut ann = create_numeric_event(
&secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
)
.unwrap();
if let EventDescriptor::DigitDecompositionEvent(ref mut desc) =
ann.oracle_event.event_descriptor
{
desc.nb_digits = 0; }
let result = sign_numeric_event(&secp, &key_pair, &ann, 5, &nonce_keys);
assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
if let EventDescriptor::DigitDecompositionEvent(ref mut desc) =
ann.oracle_event.event_descriptor
{
desc.nb_digits = 64; }
let result = sign_numeric_event(&secp, &key_pair, &ann, 5, &nonce_keys);
assert!(matches!(result, Err(Error::InvalidNumberOfDigits)));
}
#[test]
fn test_error_invalid_nonces() {
let (secp, key_pair) = setup_test_keypair();
let (_, wrong_nonces) = setup_test_nonces(&secp, 2);
let result = create_numeric_event(
&secp,
&key_pair,
"test",
2,
4,
false,
0,
"m/s",
12345,
&wrong_nonces,
);
assert!(matches!(result, Err(Error::InvalidNonces)));
let (_, wrong_nonces) = setup_test_nonces(&secp, 1); let result = create_numeric_event(
&secp,
&key_pair,
"test",
2,
4,
true,
0,
"m/s",
12345,
&wrong_nonces,
);
assert!(matches!(result, Err(Error::InvalidNonces)));
let (_, nonce1) = setup_test_nonce(&secp);
let (nonce_key2, _) = setup_test_nonce(&secp);
let ann = create_enum_event(
&secp,
&key_pair,
"test",
&vec!["a".to_string(), "b".to_string()],
12345,
&nonce1,
)
.unwrap();
let result = sign_enum_event(&secp, &key_pair, &ann, &"a".to_string(), &nonce_key2);
assert!(matches!(result, Err(Error::InvalidNonces)));
let (_, nonces1) = setup_test_nonces(&secp, 4);
let (nonce_keys2, _) = setup_test_nonces(&secp, 4);
let ann = create_numeric_event(
&secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces1,
)
.unwrap();
let result = sign_numeric_event(&secp, &key_pair, &ann, -0b1010, &nonce_keys2);
assert!(matches!(result, Err(Error::InvalidNonces)));
}
#[test]
fn test_error_invalid_outcome() {
let (secp, key_pair) = setup_test_keypair();
let (nonce_key, nonce) = setup_test_nonce(&secp);
let ann = create_enum_event(
&secp,
&key_pair,
"test",
&vec!["a".to_string(), "b".to_string()],
12345,
&nonce,
)
.unwrap();
let result = sign_enum_event(&secp, &key_pair, &ann, "c", &nonce_key);
assert!(matches!(result, Err(Error::InvalidOutcome)));
let (_, nonces) = setup_test_nonces(&secp, 4);
let _ = create_numeric_event(
&secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
)
.unwrap();
}
#[test]
fn test_error_invalid_event_descriptor() {
let (secp, key_pair) = setup_test_keypair();
let (_, nonces) = setup_test_nonces(&secp, 4);
let numeric_ann = create_numeric_event(
&secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
)
.unwrap();
let (nonce_key, nonce) = setup_test_nonce(&secp);
let result = sign_enum_event(&secp, &key_pair, &numeric_ann, "a", &nonce_key);
assert!(matches!(result, Err(Error::InvalidEventDescriptor)));
let enum_ann = create_enum_event(
&secp,
&key_pair,
"test",
&vec!["a".to_string(), "b".to_string()],
12345,
&nonce,
)
.unwrap();
let result = sign_numeric_event(&secp, &key_pair, &enum_ann, 42, &vec![nonce_key]);
assert!(matches!(result, Err(Error::InvalidEventDescriptor)));
}
#[test]
fn test_error_invalid_announcement() {
let (secp, key_pair) = setup_test_keypair();
let other_key_pair = Keypair::new(&secp, &mut thread_rng());
let (nonce_key, nonce) = setup_test_nonce(&secp);
let ann = create_enum_event(
&secp,
&key_pair,
"test",
&vec!["a".to_string(), "b".to_string()],
12345,
&nonce,
)
.unwrap();
let result = sign_enum_event(&secp, &other_key_pair, &ann, "a", &nonce_key);
assert!(matches!(result, Err(Error::InvalidAnnouncement)));
let (nonce_keys, nonces) = setup_test_nonces(&secp, 4);
let numeric_ann = create_numeric_event(
&secp, &key_pair, "test", 2, 4, false, 0, "m/s", 12345, &nonces,
)
.unwrap();
let result = sign_numeric_event(&secp, &other_key_pair, &numeric_ann, 5, &nonce_keys);
assert!(matches!(result, Err(Error::InvalidAnnouncement)));
}
#[tokio::test]
async fn test_error_not_found() {
let oracle = setup_test_oracle();
let result = oracle
.sign_enum_event("nonexistent".to_string(), "a".to_string())
.await;
assert!(matches!(result, Err(Error::NotFound)));
let result = oracle
.sign_numeric_event("nonexistent".to_string(), 42)
.await;
assert!(matches!(result, Err(Error::NotFound)));
}
#[tokio::test]
async fn test_error_event_already_signed() {
let oracle = setup_test_oracle();
let event_id = "test_event".to_string();
let _ann = oracle
.create_enum_event(
event_id.clone(),
vec!["a".to_string(), "b".to_string()],
12345,
)
.await
.unwrap();
let _attestation = oracle
.sign_enum_event(event_id.clone(), "a".to_string())
.await
.unwrap();
let result = oracle
.sign_enum_event(event_id.clone(), "b".to_string())
.await;
assert!(matches!(result, Err(Error::EventAlreadySigned)));
let numeric_event_id = "numeric_event".to_string();
let _ann = oracle
.create_numeric_event(
numeric_event_id.clone(),
4,
false,
0,
"m/s".to_string(),
12345,
)
.await
.unwrap();
let _attestation = oracle
.sign_numeric_event(numeric_event_id.clone(), 5)
.await
.unwrap();
let result = oracle.sign_numeric_event(numeric_event_id, 6).await;
assert!(matches!(result, Err(Error::EventAlreadySigned)));
}
#[tokio::test]
async fn test_error_storage_failure() {
struct FailingStorage;
impl crate::storage::Storage for FailingStorage {
async fn get_next_nonce_indexes(&self, _num: usize) -> Result<Vec<u32>, Error> {
Err(Error::StorageFailure)
}
async fn save_announcement(
&self,
_announcement: OracleAnnouncement,
_indexes: Vec<u32>,
) -> Result<String, Error> {
Err(Error::StorageFailure)
}
async fn save_signatures(
&self,
_event_id: String,
_sigs: Vec<(String, Signature)>,
) -> Result<crate::storage::OracleEventData, Error> {
Err(Error::StorageFailure)
}
async fn get_event(
&self,
_event_id: String,
) -> Result<Option<crate::storage::OracleEventData>, Error> {
Err(Error::StorageFailure)
}
}
let mut seed: [u8; 64] = [0; 64];
thread_rng().fill(&mut seed);
let xpriv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
let oracle = Oracle::from_xpriv(FailingStorage, xpriv).unwrap();
let result = oracle
.create_enum_event("test".to_string(), vec!["a".to_string()], 12345)
.await;
assert!(matches!(result, Err(Error::StorageFailure)));
let result = oracle
.create_numeric_event("test".to_string(), 4, false, 0, "m/s".to_string(), 12345)
.await;
assert!(matches!(result, Err(Error::StorageFailure)));
let result = oracle
.sign_enum_event("test".to_string(), "a".to_string())
.await;
assert!(matches!(result, Err(Error::StorageFailure)));
let result = oracle.sign_numeric_event("test".to_string(), 42).await;
assert!(matches!(result, Err(Error::StorageFailure)));
}
#[tokio::test]
async fn test_kormir_create_enum_event() {
let oracle = setup_test_oracle();
let event_id = "test".to_string();
let outcomes = vec!["a".to_string(), "b".to_string()];
let event_maturity_epoch = 100;
let ann = oracle
.create_enum_event(event_id.clone(), outcomes.clone(), event_maturity_epoch)
.await
.unwrap();
assert!(ann.validate(&oracle.secp).is_ok());
assert_eq!(ann.oracle_event.event_id, event_id);
assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
assert_eq!(
ann.oracle_event.event_descriptor,
EventDescriptor::EnumEvent(EnumEventDescriptor { outcomes })
);
}
#[tokio::test]
async fn test_kormir_sign_enum_event() {
let oracle = setup_test_oracle();
let event_id = "test".to_string();
let outcomes = vec!["a".to_string(), "b".to_string()];
let event_maturity_epoch = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as u32
+ 86400;
let ann = oracle
.create_enum_event(event_id.clone(), outcomes.clone(), event_maturity_epoch)
.await
.unwrap();
let attestation = oracle
.sign_enum_event(event_id, "a".to_string())
.await
.unwrap();
assert!(attestation.outcomes.contains(&"a".to_string()));
assert_eq!(attestation.oracle_public_key, oracle.public_key());
assert_eq!(attestation.signatures.len(), 1);
assert_eq!(attestation.outcomes.len(), 1);
let sig = attestation.signatures.first().unwrap();
let expected_nonce = ann.oracle_event.oracle_nonces.first().unwrap().serialize();
let bytes = sig.encode();
let (rx, _sig) = bytes.split_at(32);
assert_eq!(rx, expected_nonce)
}
#[tokio::test]
async fn test_kormir_create_unsigned_numeric_event() {
let oracle = setup_test_oracle();
let event_id = "test_unsigned_numeric".to_string();
let num_digits = 20;
let event_maturity_epoch = 100;
let ann = oracle
.create_numeric_event(
event_id.clone(),
num_digits,
false,
0,
"m/s".into(),
event_maturity_epoch,
)
.await
.unwrap();
assert!(ann.validate(&oracle.secp).is_ok());
assert_eq!(ann.oracle_event.event_id, event_id);
assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
assert_eq!(
ann.oracle_event.event_descriptor,
EventDescriptor::DigitDecompositionEvent(DigitDecompositionEventDescriptor {
base: 2,
is_signed: false,
unit: "m/s".into(),
precision: 0,
nb_digits: 20,
})
);
}
#[tokio::test]
async fn test_kormir_sign_unsigned_numeric_event() {
let oracle = setup_test_oracle();
let event_id = "test_unsigned_numeric".to_string();
let num_digits = 16;
let event_maturity_epoch = 100;
let ann = oracle
.create_numeric_event(
event_id.clone(),
num_digits,
false,
0,
"m/s".into(),
event_maturity_epoch,
)
.await
.unwrap();
let attestation = oracle
.sign_numeric_event(event_id.clone(), 0x5555)
.await
.unwrap();
assert_eq!(
attestation.outcomes,
vec!["0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1"]
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
);
assert_eq!(attestation.oracle_public_key, oracle.public_key());
assert_eq!(attestation.signatures.len(), 16);
assert_eq!(attestation.outcomes.len(), 16);
for i in 0..attestation.signatures.len() {
let sig = attestation.signatures[i];
let expected_nonce = ann.oracle_event.oracle_nonces[i].serialize();
let bytes = sig.encode();
let (rx, _sig) = bytes.split_at(32);
assert_eq!(rx, expected_nonce)
}
}
#[tokio::test]
async fn test_kormir_create_signed_numeric_event() {
let oracle = setup_test_oracle();
let event_id = "test_signed_numeric".to_string();
let num_digits = 20;
let event_maturity_epoch = 100;
let ann = oracle
.create_numeric_event(
event_id.clone(),
num_digits,
true,
0,
"m/s".into(),
event_maturity_epoch,
)
.await
.unwrap();
assert!(ann.validate(&oracle.secp).is_ok());
assert_eq!(ann.oracle_event.event_id, event_id);
assert_eq!(ann.oracle_event.event_maturity_epoch, event_maturity_epoch);
assert_eq!(
ann.oracle_event.event_descriptor,
EventDescriptor::DigitDecompositionEvent(DigitDecompositionEventDescriptor {
base: 2,
is_signed: true,
unit: "m/s".into(),
precision: 0,
nb_digits: 20,
})
);
}
#[tokio::test]
async fn test_kormir_sign_signed_positive_numeric_event() {
let oracle = setup_test_oracle();
let event_id = "test_signed_numeric".to_string();
let num_digits = 16;
let event_maturity_epoch = 100;
let ann = oracle
.create_numeric_event(
event_id.clone(),
num_digits,
true,
0,
"m/s".into(),
event_maturity_epoch,
)
.await
.unwrap();
let attestation = oracle.sign_numeric_event(event_id, 0x5555).await.unwrap();
assert_eq!(
attestation.outcomes,
vec![
"+", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1"
]
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
);
assert_eq!(attestation.oracle_public_key, oracle.public_key());
assert_eq!(attestation.signatures.len(), 16 + 1);
assert_eq!(attestation.outcomes.len(), 16 + 1);
for i in 0..attestation.signatures.len() {
let sig = attestation.signatures[i];
let expected_nonce = ann.oracle_event.oracle_nonces[i].serialize();
let bytes = sig.encode();
let (rx, _sig) = bytes.split_at(32);
assert_eq!(rx, expected_nonce)
}
}
#[tokio::test]
async fn test_kormir_sign_signed_negative_numeric_event() {
let oracle = setup_test_oracle();
let event_id = "test_signed_numeric".to_string();
let num_digits = 16;
let event_maturity_epoch = 100;
let ann = oracle
.create_numeric_event(
event_id.clone(),
num_digits,
true,
0,
"m/s".into(),
event_maturity_epoch,
)
.await
.unwrap();
let attestation = oracle.sign_numeric_event(event_id, -0x5555).await.unwrap();
assert_eq!(
attestation.outcomes,
vec![
"-", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1", "0", "1"
]
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
);
assert_eq!(attestation.oracle_public_key, oracle.public_key());
assert_eq!(attestation.signatures.len(), 16 + 1);
assert_eq!(attestation.outcomes.len(), 16 + 1);
for i in 0..attestation.signatures.len() {
let sig = attestation.signatures[i];
let expected_nonce = ann.oracle_event.oracle_nonces[i].serialize();
let bytes = sig.encode();
let (rx, _sig) = bytes.split_at(32);
assert_eq!(rx, expected_nonce)
}
}
}