use bitcoin::{Address, Network, OutPoint, PublicKey, ScriptBuf, Transaction, Txid, XOnlyPublicKey};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct EscrowId(pub String);
impl EscrowId {
pub fn new() -> Self {
Self(uuid::Uuid::new_v4().to_string())
}
pub fn from_string(s: String) -> Self {
Self(s)
}
}
impl Default for EscrowId {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for EscrowId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId(pub String);
impl AgentId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
}
impl fmt::Display for AgentId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OracleId(pub String);
impl OracleId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
}
impl fmt::Display for OracleId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
#[serde(rename_all = "snake_case")]
pub enum EscrowRole {
Buyer,
Seller,
Arbiter,
AIAgent,
}
impl fmt::Display for EscrowRole {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EscrowRole::Buyer => write!(f, "buyer"),
EscrowRole::Seller => write!(f, "seller"),
EscrowRole::Arbiter => write!(f, "arbiter"),
EscrowRole::AIAgent => write!(f, "ai_agent"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscrowParticipant {
pub id: String,
pub role: EscrowRole,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_key: Option<PublicKey>,
#[serde(skip_serializing_if = "Option::is_none")]
pub xonly_public_key: Option<XOnlyPublicKey>,
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "serialize_address_opt", deserialize_with = "deserialize_address_opt")]
pub address: Option<Address>,
pub signed: bool,
pub joined_at: DateTime<Utc>,
}
fn serialize_address_opt<S>(addr: &Option<Address>, s: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match addr {
Some(a) => s.serialize_str(&a.to_string()),
None => s.serialize_none(),
}
}
fn deserialize_address_opt<'de, D>(d: D) -> std::result::Result<Option<Address>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let opt: Option<String> = Option::deserialize(d)?;
match opt {
Some(s) => {
let addr = Address::from_str(&s).map_err(|e| D::Error::custom(e.to_string()))?;
Ok(Some(addr.assume_checked()))
}
None => Ok(None),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EscrowStatus {
Created,
Funded,
Evaluating,
PendingRelease,
Released,
Cancelled,
Disputed,
Expired,
}
impl fmt::Display for EscrowStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EscrowStatus::Created => write!(f, "created"),
EscrowStatus::Funded => write!(f, "funded"),
EscrowStatus::Evaluating => write!(f, "evaluating"),
EscrowStatus::PendingRelease => write!(f, "pending_release"),
EscrowStatus::Released => write!(f, "released"),
EscrowStatus::Cancelled => write!(f, "cancelled"),
EscrowStatus::Disputed => write!(f, "disputed"),
EscrowStatus::Expired => write!(f, "expired"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscrowOutput {
pub outpoint: OutPoint,
pub amount: u64,
pub script_pubkey: ScriptBuf,
}
impl EscrowOutput {
pub fn new(outpoint: OutPoint, amount: u64, script_pubkey: ScriptBuf) -> Self {
Self {
outpoint,
amount,
script_pubkey,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedTransaction {
pub transaction: Transaction,
pub txid: Txid,
pub signed_at: DateTime<Utc>,
pub signed_by: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscrowConfig {
pub network: Network,
pub threshold: usize,
pub total_participants: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub fee_rate: f32,
}
impl Default for EscrowConfig {
fn default() -> Self {
Self {
network: Network::Testnet,
threshold: 2,
total_participants: 3,
expires_at: None,
description: None,
fee_rate: 1.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ReleaseCondition {
AIDecision {
agent_id: AgentId,
decision_hash: String,
},
OracleTrigger {
oracle_id: OracleId,
query: String,
expected_value: String,
},
TimeLock {
unlock_after: DateTime<Utc>,
},
AllOf {
conditions: Vec<ReleaseCondition>,
},
AnyOf {
conditions: Vec<ReleaseCondition>,
},
ManualApproval {
required_roles: Vec<EscrowRole>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConditionResult {
pub satisfied: bool,
pub reason: String,
pub evaluated_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}