use std::error::Error;
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use bitcoin::secp256k1::{PublicKey, Signature};
use thiserror::Error;
use crate::common::error::AnyaResult;
#[derive(Debug, Clone)]
pub struct Oracle {
pub info: OracleInfo,
pub announcements: Vec<OracleAnnouncement>,
pub attestations: Vec<OracleAttestation>,
}
impl Oracle {
pub fn new(info: OracleInfo) -> Self {
Self {
info,
announcements: Vec::new(),
attestations: Vec::new(),
}
}
pub fn add_announcement(&mut self, announcement: OracleAnnouncement) {
self.announcements.push(announcement);
}
pub fn add_attestation(&mut self, attestation: OracleAttestation) {
self.attestations.push(attestation);
}
pub fn get_announcement(&self, event_id: &str) -> Option<&OracleAnnouncement> {
self.announcements.iter().find(|a| a.event_id == event_id)
}
pub fn get_attestation(&self, event_id: &str) -> Option<&OracleAttestation> {
self.attestations.iter().find(|a| a.event_id == event_id)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OracleInfo {
pub name: String,
pub public_key: PublicKey,
pub endpoint: String,
pub properties: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OracleAnnouncement {
pub event_id: String,
pub description: String,
pub public_r: PublicKey,
pub public_key: PublicKey,
pub created_at: DateTime<Utc>,
pub maturity_time: DateTime<Utc>,
pub announcement_time: DateTime<Utc>,
pub outcomes: Vec<String>,
pub metadata: HashMap<String, String>,
}
impl OracleAnnouncement {
pub fn new(
event_id: String,
description: String,
public_r: PublicKey,
public_key: PublicKey,
maturity_time: DateTime<Utc>,
announcement_time: DateTime<Utc>,
outcomes: Vec<String>,
) -> Self {
Self {
event_id,
description,
public_r,
public_key,
created_at: Utc::now(),
maturity_time,
announcement_time,
outcomes,
metadata: HashMap::new(),
}
}
pub fn add_metadata(&mut self, key: &str, value: &str) {
self.metadata.insert(key.to_string(), value.to_string());
}
pub fn verify_signature(&self) -> AnyaResult<bool> {
Ok(true)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OracleAttestation {
pub event_id: String,
pub outcome: String,
pub signature: Signature,
pub announcement_id: String,
pub created_at: DateTime<Utc>,
pub metadata: HashMap<String, String>,
}
impl OracleAttestation {
pub fn new(
event_id: String,
outcome: String,
signature: Signature,
announcement_id: String,
) -> Self {
Self {
event_id,
outcome,
signature,
announcement_id,
created_at: Utc::now(),
metadata: HashMap::new(),
}
}
pub fn verify(&self, announcement: &OracleAnnouncement) -> AnyaResult<bool> {
if self.event_id != announcement.event_id {
return Ok(false);
}
if !announcement.outcomes.contains(&self.outcome) {
return Ok(false);
}
Ok(true)
}
pub fn add_metadata(&mut self, key: &str, value: &str) {
self.metadata.insert(key.to_string(), value.to_string());
}
}
pub struct OracleClient {
base_url: String,
}
impl OracleClient {
pub fn new(base_url: &str) -> Self {
Self {
base_url: base_url.to_string(),
}
}
pub fn get_oracle_info(&self) -> AnyaResult<OracleInfo> {
use bitcoin::secp256k1::{Secp256k1, SecretKey};
use std::str::FromStr;
let secp = Secp256k1::new();
let mut hasher = std::collections::hash_map::DefaultHasher::new();
use std::hash::{Hash, Hasher};
self.base_url.hash(&mut hasher);
let hash = hasher.finish();
let secret_bytes = hash.to_be_bytes();
let mut key_bytes = [0u8; 32];
for (i, &b) in secret_bytes.iter().enumerate() {
key_bytes[i % 32] ^= b;
}
key_bytes[0] = key_bytes[0].max(1);
let secret_key = SecretKey::from_slice(&key_bytes)
.map_err(|e| crate::common::error::AnyaError::Crypto(e.to_string()))?;
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
let mut properties = HashMap::new();
properties.insert("version".to_string(), "1.0".to_string());
properties.insert("supported_events".to_string(), "price,weather,sports".to_string());
Ok(OracleInfo {
name: format!("Oracle-{}", self.base_url.chars().take(8).collect::<String>()),
public_key,
endpoint: self.base_url.clone(),
properties,
})
}
pub fn get_announcements(&self) -> AnyaResult<Vec<OracleAnnouncement>> {
use bitcoin::secp256k1::{Secp256k1, SecretKey};
use chrono::{Duration};
let secp = Secp256k1::new();
let mut announcements = Vec::new();
for i in 0..3 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
use std::hash::{Hash, Hasher};
format!("{}-{}", self.base_url, i).hash(&mut hasher);
let hash = hasher.finish();
let secret_bytes = hash.to_be_bytes();
let mut key_bytes = [0u8; 32];
for (j, &b) in secret_bytes.iter().enumerate() {
key_bytes[j % 32] ^= b;
}
key_bytes[0] = key_bytes[0].max(1);
let secret_key = SecretKey::from_slice(&key_bytes)
.map_err(|e| crate::common::error::AnyaError::Crypto(e.to_string()))?;
let public_r = PublicKey::from_secret_key(&secp, &secret_key);
key_bytes[31] = key_bytes[31].wrapping_add(1);
let oracle_secret = SecretKey::from_slice(&key_bytes)
.map_err(|e| crate::common::error::AnyaError::Crypto(e.to_string()))?;
let oracle_public = PublicKey::from_secret_key(&secp, &oracle_secret);
let now = Utc::now();
let announcement = OracleAnnouncement::new(
format!("event-{}-{}", self.base_url.chars().take(4).collect::<String>(), i),
format!("Event {} from oracle", i),
public_r,
oracle_public,
now + Duration::hours(24 * (i + 1) as i64), now + Duration::hours(12 * (i + 1) as i64), vec!["yes".to_string(), "no".to_string(), "uncertain".to_string()],
);
announcements.push(announcement);
}
Ok(announcements)
}
pub fn get_announcement(&self, event_id: &str) -> AnyaResult<Option<OracleAnnouncement>> {
let announcements = self.get_announcements()?;
let announcement = announcements.into_iter().find(|a| a.event_id == event_id);
Ok(announcement)
}
pub fn get_attestation(&self, event_id: &str) -> AnyaResult<Option<OracleAttestation>> {
if let Some(announcement) = self.get_announcement(event_id)? {
use bitcoin::secp256k1::{Secp256k1, SecretKey, Message};
let secp = Secp256k1::new();
let mut hasher = std::collections::hash_map::DefaultHasher::new();
use std::hash::{Hash, Hasher};
format!("{}-attestation-{}", self.base_url, event_id).hash(&mut hasher);
let hash = hasher.finish();
let secret_bytes = hash.to_be_bytes();
let mut key_bytes = [0u8; 32];
for (i, &b) in secret_bytes.iter().enumerate() {
key_bytes[i % 32] ^= b;
}
key_bytes[0] = key_bytes[0].max(1);
let secret_key = SecretKey::from_slice(&key_bytes)
.map_err(|e| crate::common::error::AnyaError::Crypto(e.to_string()))?;
let outcome = &announcement.outcomes[0]; let message_bytes = outcome.as_bytes();
let mut msg_array = [0u8; 32];
for (i, &b) in message_bytes.iter().enumerate() {
if i < 32 {
msg_array[i] = b;
}
}
let message = Message::from_slice(&msg_array)
.map_err(|e| crate::common::error::AnyaError::Crypto(e.to_string()))?;
let signature = secp.sign_ecdsa(&message, &secret_key);
let attestation = OracleAttestation::new(
event_id.to_string(),
outcome.clone(),
signature,
announcement.event_id.clone(),
);
Ok(Some(attestation))
} else {
Ok(None)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OracleAttestationParams {
pub commitment: String,
pub oracle_pubkey: PublicKey,
pub musig_pubkey: PublicKey,
pub schnorr_params: SchnorrParams,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchnorrParams {
pub r_value: PublicKey,
pub s_value: Vec<u8>,
}
#[derive(Debug, Error)]
pub enum Error {
#[error("Oracle error: {0}")]
OracleError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Signature error: {0}")]
SignatureError(String),
}
pub fn implement_non_interactive_oracle(
commitment: &str, oracle_pubkey: &PublicKey
) -> Result<OracleAttestationParams, Error> {
let taproot_commitment = commitment.to_string();
let r_point = create_r_point()?;
let secp = Secp256k1::new();
let mut rng = rand::thread_rng();
let temp_secret = SecretKey::new(&mut rng);
let s_value = temp_secret.secret_bytes().to_vec();
let schnorr_params = SchnorrParams {
r_value: r_point,
s_value,
};
let musig_pubkey = create_musig_key(oracle_pubkey)?;
Ok(OracleAttestationParams {
commitment: taproot_commitment,
oracle_pubkey: *oracle_pubkey,
musig_pubkey,
})
}