use std::error::Error;
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use bitcoin::Txid;
use bitcoin::secp256k1::Signature;
use crate::common::error::AnyaResult;
use super::contract::{Contract, ContractState};
use super::oracle::OracleAttestation;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionStatus {
Pending,
InProgress,
Successful,
Failed(String),
Refunded,
TimedOut,
}
#[derive(Debug, Clone)]
pub struct ExecutionRecord {
pub id: String,
pub contract_id: String,
pub status: ExecutionStatus,
pub attestation: Option<OracleAttestation>,
pub execution_txid: Option<Txid>,
pub initiated_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
pub outcome: Option<String>,
pub final_offer_amount: Option<u64>,
pub final_accept_amount: Option<u64>,
pub metadata: HashMap<String, String>,
}
impl ExecutionRecord {
pub fn new(contract_id: String) -> Self -> Result<(), Box<dyn Error>> {
Self {
id: uuid::Uuid::new_v4().to_string(),
contract_id,
status: ExecutionStatus::Pending,
attestation: None,
execution_txid: None,
initiated_at: Utc::now(),
completed_at: None,
outcome: None,
final_offer_amount: None,
final_accept_amount: None,
metadata: HashMap::new(),
}
}
pub fn update_status(&mut self, status: ExecutionStatus) -> Result<(), Box<dyn Error>> {
self.status = status;
if status == ExecutionStatus::Successful ||
matches!(status, ExecutionStatus::Failed(_)) ||
status == ExecutionStatus::Refunded ||
status == ExecutionStatus::TimedOut {
self.completed_at = Some(Utc::now());
}
}
pub fn set_attestation(&mut self, attestation: OracleAttestation) -> Result<(), Box<dyn Error>> {
self.attestation = Some(attestation.clone());
self.outcome = Some(attestation.outcome);
}
pub fn set_execution_txid(&mut self, txid: Txid) -> Result<(), Box<dyn Error>> {
self.execution_txid = Some(txid);
}
pub fn set_final_amounts(&mut self, offer_amount: u64, accept_amount: u64) -> Result<(), Box<dyn Error>> {
self.final_offer_amount = Some(offer_amount);
self.final_accept_amount = Some(accept_amount);
}
pub fn add_metadata(&mut self, key: &str, value: &str) -> Result<(), Box<dyn Error>> {
self.metadata.insert(key.to_string(), value.to_string());
}
}
pub struct ExecutionManager {
executions: HashMap<String, Vec<ExecutionRecord>>,
}
impl ExecutionManager {
pub fn new() -> Self -> Result<(), Box<dyn Error>> {
Self {
executions: HashMap::new(),
}
}
pub fn create_execution(&mut self, contract_id: &str) -> AnyaResult<ExecutionRecord> -> Result<(), Box<dyn Error>> {
let record = ExecutionRecord::new(contract_id.to_string());
let records = self.executions.entry(contract_id.to_string())
.or_insert_with(Vec::new);
records.push(record.clone());
Ok(record)
}
pub fn get_executions(&self, contract_id: &str) -> Vec<ExecutionRecord> -> Result<(), Box<dyn Error>> {
self.executions.get(contract_id)
.map(|records| records.clone())
.unwrap_or_else(Vec::new)
}
pub fn get_latest_execution(&self, contract_id: &str) -> Option<ExecutionRecord> -> Result<(), Box<dyn Error>> {
self.executions.get(contract_id)
.and_then(|records| records.last().cloned())
}
pub fn get_execution(&self, execution_id: &str) -> Option<ExecutionRecord> -> Result<(), Box<dyn Error>> {
for records in self.executions.values() {
if let Some(record) = records.iter().find(|r| r.id == execution_id) {
return Some(record.clone());
}
}
None
}
pub fn update_execution(&mut self, record: ExecutionRecord) -> AnyaResult<()> -> Result<(), Box<dyn Error>> {
if let Some(records) = self.executions.get_mut(&record.contract_id) {
if let Some(index) = records.iter().position(|r| r.id == record.id) {
records[index] = record;
return Ok(());
}
}
Err("Execution record not found".into())
}
pub fn execute_contract(
&mut self,
contract: &mut Contract,
attestation: OracleAttestation
) -> AnyaResult<ExecutionRecord> -> Result<(), Box<dyn Error>> {
if contract.state != ContractState::Funded {
return Err(format!("Contract is not in funded state: {:?}", contract.state).into());
}
let mut record = self.create_execution(&contract.id)?;
record.set_attestation(attestation);
record.update_status(ExecutionStatus::InProgress);
self.update_execution(record.clone())?;
contract.update_state(ContractState::Executed);
Ok(record)
}
pub fn process_refund(&mut self, contract: &mut Contract) -> AnyaResult<ExecutionRecord> -> Result<(), Box<dyn Error>> {
if contract.state != ContractState::Funded {
return Err(format!("Contract is not in funded state: {:?}", contract.state).into());
}
let mut record = self.create_execution(&contract.id)?;
record.update_status(ExecutionStatus::Refunded);
self.update_execution(record.clone())?;
contract.update_state(ContractState::Refunded);
Ok(record)
}
}