use crate::error::Error;
use crate::ContractId;
use bitcoin::{Amount, SignedAmount, Transaction, Txid};
use ddk_messages::{
oracle_msgs::{EventDescriptor, OracleAnnouncement, OracleAttestation},
AcceptDlc, SignDlc,
};
use ddk_trie::multi_oracle_trie::MultiOracleTrie;
use ddk_trie::multi_oracle_trie_with_diff::MultiOracleTrieWithDiff;
use secp256k1_zkp::PublicKey;
#[cfg(feature = "use-serde")]
use serde::{Deserialize, Serialize};
use signed_contract::SignedContract;
use std::fmt::Write;
use self::utils::unordered_equal;
pub mod accepted_contract;
pub mod contract_info;
pub mod contract_input;
pub mod enum_descriptor;
pub mod numerical_descriptor;
pub mod offered_contract;
pub mod ser;
pub mod signed_contract;
pub(crate) mod utils;
#[derive(Clone)]
pub enum Contract {
Offered(offered_contract::OfferedContract),
Accepted(accepted_contract::AcceptedContract),
Signed(signed_contract::SignedContract),
Confirmed(signed_contract::SignedContract),
PreClosed(PreClosedContract),
Closed(ClosedContract),
Refunded(signed_contract::SignedContract),
FailedAccept(FailedAcceptContract),
FailedSign(FailedSignContract),
Rejected(offered_contract::OfferedContract),
}
impl std::fmt::Debug for Contract {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let state = match self {
Contract::Offered(_) => "offered",
Contract::Accepted(_) => "accepted",
Contract::Signed(_) => "signed",
Contract::Confirmed(_) => "confirmed",
Contract::PreClosed(_) => "pre-closed",
Contract::Closed(_) => "closed",
Contract::Refunded(_) => "refunded",
Contract::FailedAccept(_) => "failed accept",
Contract::FailedSign(_) => "failed sign",
Contract::Rejected(_) => "rejected",
};
f.debug_struct("Contract").field("state", &state).finish()
}
}
impl Contract {
pub fn get_id(&self) -> ContractId {
match self {
Contract::Offered(o) | Contract::Rejected(o) => o.id,
Contract::Accepted(o) => o.get_contract_id(),
Contract::Signed(o) | Contract::Confirmed(o) | Contract::Refunded(o) => {
o.accepted_contract.get_contract_id()
}
Contract::FailedAccept(c) => c.offered_contract.id,
Contract::FailedSign(c) => c.accepted_contract.get_contract_id(),
Contract::PreClosed(c) => c.signed_contract.accepted_contract.get_contract_id(),
Contract::Closed(c) => c.contract_id,
}
}
pub fn get_id_string(&self) -> String {
let mut string_id = String::with_capacity(32 * 2 + 2);
string_id.push_str("0x");
let id = self.get_id();
for i in &id {
write!(string_id, "{i:02x}").unwrap();
}
string_id
}
pub fn get_temporary_id(&self) -> ContractId {
match self {
Contract::Offered(o) | Contract::Rejected(o) => o.id,
Contract::Accepted(o) => o.offered_contract.id,
Contract::Signed(o) | Contract::Confirmed(o) | Contract::Refunded(o) => {
o.accepted_contract.offered_contract.id
}
Contract::FailedAccept(c) => c.offered_contract.id,
Contract::FailedSign(c) => c.accepted_contract.offered_contract.id,
Contract::PreClosed(c) => c.signed_contract.accepted_contract.offered_contract.id,
Contract::Closed(c) => c.temporary_contract_id,
}
}
pub fn get_counter_party_id(&self) -> PublicKey {
match self {
Contract::Offered(o) | Contract::Rejected(o) => o.counter_party,
Contract::Accepted(a) => a.offered_contract.counter_party,
Contract::Signed(s) | Contract::Confirmed(s) | Contract::Refunded(s) => {
s.accepted_contract.offered_contract.counter_party
}
Contract::PreClosed(c) => {
c.signed_contract
.accepted_contract
.offered_contract
.counter_party
}
Contract::Closed(c) => c.counter_party_id,
Contract::FailedAccept(f) => f.offered_contract.counter_party,
Contract::FailedSign(f) => f.accepted_contract.offered_contract.counter_party,
}
}
pub fn is_offer_party(&self) -> bool {
match self {
Contract::Offered(o) | Contract::Rejected(o) => o.is_offer_party,
Contract::Accepted(a) => a.offered_contract.is_offer_party,
Contract::Signed(s) | Contract::Confirmed(s) | Contract::Refunded(s) => {
s.accepted_contract.offered_contract.is_offer_party
}
Contract::FailedAccept(f) => f.offered_contract.is_offer_party,
Contract::FailedSign(f) => f.accepted_contract.offered_contract.is_offer_party,
Contract::PreClosed(c) => {
c.signed_contract
.accepted_contract
.offered_contract
.is_offer_party
}
Contract::Closed(_) => false,
}
}
pub fn get_collateral(
&self,
) -> (
Amount, /* offer collateral */
Amount, /* accept collateral */
Amount, /* total collateral */
) {
match self {
Contract::Offered(o) => (
o.offer_params.collateral,
o.total_collateral - o.offer_params.collateral,
o.total_collateral,
),
Contract::Accepted(a) => (
a.offered_contract.offer_params.collateral,
a.accept_params.collateral,
a.offered_contract.total_collateral,
),
Contract::Signed(s) | Contract::Confirmed(s) | Contract::Refunded(s) => (
s.accepted_contract.offered_contract.offer_params.collateral,
s.accepted_contract.accept_params.collateral,
s.accepted_contract.offered_contract.total_collateral,
),
Contract::FailedAccept(f) => (
f.offered_contract.offer_params.collateral,
Amount::ZERO,
f.offered_contract.total_collateral,
),
Contract::FailedSign(f) => (
f.accepted_contract.offered_contract.offer_params.collateral,
f.accepted_contract.accept_params.collateral,
f.accepted_contract.offered_contract.total_collateral,
),
Contract::PreClosed(p) => (
p.signed_contract
.accepted_contract
.offered_contract
.offer_params
.collateral,
p.signed_contract.accepted_contract.accept_params.collateral,
p.signed_contract
.accepted_contract
.offered_contract
.total_collateral,
),
Contract::Closed(_) => (Amount::ZERO, Amount::ZERO, Amount::ZERO),
Contract::Rejected(_) => (Amount::ZERO, Amount::ZERO, Amount::ZERO),
}
}
pub fn get_cet_locktime(&self) -> u32 {
match self {
Contract::Offered(o) => o.cet_locktime,
Contract::Accepted(a) => a.offered_contract.cet_locktime,
Contract::Signed(s) => s.accepted_contract.offered_contract.cet_locktime,
Contract::Confirmed(c) => c.accepted_contract.offered_contract.cet_locktime,
Contract::PreClosed(p) => {
p.signed_contract
.accepted_contract
.offered_contract
.cet_locktime
}
Contract::Closed(c) => c.signed_cet.as_ref().unwrap().lock_time.to_consensus_u32(),
Contract::Refunded(r) => r.accepted_contract.offered_contract.cet_locktime,
Contract::FailedAccept(f) => f.offered_contract.cet_locktime,
Contract::FailedSign(f) => f.accepted_contract.offered_contract.cet_locktime,
Contract::Rejected(_) => 0,
}
}
pub fn get_refund_locktime(&self) -> u32 {
match self {
Contract::Offered(o) => o.refund_locktime,
Contract::Accepted(a) => a.offered_contract.refund_locktime,
Contract::Signed(s) => s.accepted_contract.offered_contract.refund_locktime,
Contract::Confirmed(c) => c.accepted_contract.offered_contract.refund_locktime,
Contract::PreClosed(p) => {
p.signed_contract
.accepted_contract
.offered_contract
.refund_locktime
}
Contract::Closed(c) => c.signed_cet.as_ref().unwrap().lock_time.to_consensus_u32(),
Contract::Refunded(r) => r.accepted_contract.offered_contract.refund_locktime,
Contract::FailedAccept(f) => f.offered_contract.refund_locktime,
Contract::FailedSign(f) => f.accepted_contract.offered_contract.refund_locktime,
Contract::Rejected(_) => 0,
}
}
pub fn get_pnl(&self) -> SignedAmount {
match self {
Contract::Offered(_) => SignedAmount::ZERO,
Contract::Accepted(_) => SignedAmount::ZERO,
Contract::Signed(_) => SignedAmount::ZERO,
Contract::Confirmed(_) => SignedAmount::ZERO,
Contract::PreClosed(p) => p
.signed_contract
.accepted_contract
.compute_pnl(&p.signed_cet),
Contract::Closed(c) => c.pnl,
Contract::Refunded(_) => SignedAmount::ZERO,
Contract::FailedAccept(_) => SignedAmount::ZERO,
Contract::FailedSign(_) => SignedAmount::ZERO,
Contract::Rejected(_) => SignedAmount::ZERO,
}
}
pub fn get_funding_txid(&self) -> Option<Txid> {
match self {
Contract::Offered(_) => None,
Contract::Accepted(a) => Some(a.dlc_transactions.fund.compute_txid()),
Contract::Signed(s) => Some(s.accepted_contract.dlc_transactions.fund.compute_txid()),
Contract::Confirmed(c) => {
Some(c.accepted_contract.dlc_transactions.fund.compute_txid())
}
Contract::PreClosed(p) => Some(
p.signed_contract
.accepted_contract
.dlc_transactions
.fund
.compute_txid(),
),
Contract::Closed(c) => Some(c.funding_txid),
Contract::Refunded(r) => Some(r.accepted_contract.dlc_transactions.fund.compute_txid()),
Contract::FailedAccept(_) => None,
Contract::FailedSign(_) => None,
Contract::Rejected(_) => None,
}
}
pub fn get_oracle_announcement(&self) -> Option<OracleAnnouncement> {
match self {
Contract::Offered(o) => Some(o.contract_info[0].oracle_announcements[0].clone()),
Contract::Accepted(a) => {
Some(a.offered_contract.contract_info[0].oracle_announcements[0].clone())
}
Contract::Signed(s) => Some(
s.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
.clone(),
),
Contract::Confirmed(c) => Some(
c.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
.clone(),
),
Contract::PreClosed(p) => Some(
p.signed_contract
.accepted_contract
.offered_contract
.contract_info[0]
.oracle_announcements[0]
.clone(),
),
Contract::Closed(_) => None,
Contract::Refunded(r) => Some(
r.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
.clone(),
),
Contract::FailedAccept(f) => {
Some(f.offered_contract.contract_info[0].oracle_announcements[0].clone())
}
Contract::FailedSign(f) => Some(
f.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
.clone(),
),
Contract::Rejected(r) => Some(r.contract_info[0].oracle_announcements[0].clone()),
}
}
pub fn get_cet_txid(&self) -> Option<Txid> {
match self {
Contract::Offered(_) => None,
Contract::Accepted(_) => None,
Contract::Signed(_) => None,
Contract::Confirmed(_) => None,
Contract::PreClosed(p) => Some(p.signed_cet.compute_txid()),
Contract::Closed(c) => c.signed_cet.as_ref().map(|cet| cet.compute_txid()),
Contract::Refunded(_) => None,
Contract::FailedAccept(_) => None,
Contract::FailedSign(_) => None,
Contract::Rejected(_) => None,
}
}
}
#[derive(Clone)]
pub struct FailedAcceptContract {
pub offered_contract: offered_contract::OfferedContract,
pub accept_message: AcceptDlc,
pub error_message: String,
}
#[derive(Clone)]
pub struct FailedSignContract {
pub accepted_contract: accepted_contract::AcceptedContract,
pub sign_message: SignDlc,
pub error_message: String,
}
#[derive(Clone)]
pub struct PreClosedContract {
pub signed_contract: SignedContract,
pub attestations: Option<Vec<OracleAttestation>>,
pub signed_cet: Transaction,
}
#[derive(Clone)]
pub struct ClosedContract {
pub attestations: Option<Vec<OracleAttestation>>,
pub signed_cet: Option<Transaction>,
pub contract_id: ContractId,
pub temporary_contract_id: ContractId,
pub counter_party_id: PublicKey,
pub funding_txid: Txid,
pub pnl: SignedAmount,
pub signed_contract: SignedContract,
}
#[derive(Clone)]
pub enum AdaptorInfo {
Enum,
Numerical(MultiOracleTrie),
NumericalWithDifference(MultiOracleTrieWithDiff),
}
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub enum ContractDescriptor {
Enum(enum_descriptor::EnumDescriptor),
Numerical(numerical_descriptor::NumericalDescriptor),
}
impl ContractDescriptor {
pub fn get_oracle_params(&self) -> Option<numerical_descriptor::DifferenceParams> {
match self {
ContractDescriptor::Enum(_) => None,
ContractDescriptor::Numerical(n) => n.difference_params.clone(),
}
}
pub fn validate(
&self,
announcements: &Vec<OracleAnnouncement>,
) -> Result<(), crate::error::Error> {
let first = announcements
.first()
.expect("to have at least one element.");
match &first.oracle_event.event_descriptor {
EventDescriptor::EnumEvent(ee) => {
for announcement in announcements {
match &announcement.oracle_event.event_descriptor {
EventDescriptor::EnumEvent(enum_desc) => {
if !unordered_equal(&ee.outcomes, &enum_desc.outcomes) {
return Err(Error::InvalidParameters(
"Oracles don't have same enum outcomes.".to_string(),
));
}
}
_ => {
return Err(Error::InvalidParameters(
"Expected enum event descriptor.".to_string(),
))
}
}
}
match self {
ContractDescriptor::Enum(ed) => ed.validate(ee),
_ => Err(Error::InvalidParameters(
"Event descriptor from contract and oracle differ.".to_string(),
)),
}
}
EventDescriptor::DigitDecompositionEvent(_) => match self {
ContractDescriptor::Numerical(n) => {
let min_nb_digits = n.oracle_numeric_infos.get_min_nb_digits();
let max_value = n
.oracle_numeric_infos
.base
.checked_pow(min_nb_digits as u32)
.ok_or_else(|| {
Error::InvalidParameters("Could not compute max value".to_string())
})?;
n.validate((max_value - 1) as u64)
}
_ => Err(Error::InvalidParameters(
"Event descriptor from contract and oracle differ.".to_string(),
)),
},
}
}
}