1use crate::error::Error;
4use crate::ContractId;
5use bitcoin::{Amount, SignedAmount, Transaction, Txid};
6use ddk_messages::{
7 oracle_msgs::{EventDescriptor, OracleAnnouncement, OracleAttestation},
8 AcceptDlc, SignDlc,
9};
10use ddk_trie::multi_oracle_trie::MultiOracleTrie;
11use ddk_trie::multi_oracle_trie_with_diff::MultiOracleTrieWithDiff;
12use secp256k1_zkp::PublicKey;
13#[cfg(feature = "use-serde")]
14use serde::{Deserialize, Serialize};
15use signed_contract::SignedContract;
16use std::fmt::Write;
17
18use self::utils::unordered_equal;
19
20pub mod accepted_contract;
21pub mod contract_info;
22pub mod contract_input;
23pub mod enum_descriptor;
24pub mod numerical_descriptor;
25pub mod offered_contract;
26pub mod ser;
27pub mod signed_contract;
28pub(crate) mod utils;
29
30#[derive(Clone)]
31pub enum Contract {
33 Offered(offered_contract::OfferedContract),
35 Accepted(accepted_contract::AcceptedContract),
37 Signed(signed_contract::SignedContract),
39 Confirmed(signed_contract::SignedContract),
41 PreClosed(PreClosedContract),
43 Closed(ClosedContract),
45 Refunded(signed_contract::SignedContract),
47 FailedAccept(FailedAcceptContract),
49 FailedSign(FailedSignContract),
51 Rejected(offered_contract::OfferedContract),
53}
54
55impl std::fmt::Debug for Contract {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 let state = match self {
58 Contract::Offered(_) => "offered",
59 Contract::Accepted(_) => "accepted",
60 Contract::Signed(_) => "signed",
61 Contract::Confirmed(_) => "confirmed",
62 Contract::PreClosed(_) => "pre-closed",
63 Contract::Closed(_) => "closed",
64 Contract::Refunded(_) => "refunded",
65 Contract::FailedAccept(_) => "failed accept",
66 Contract::FailedSign(_) => "failed sign",
67 Contract::Rejected(_) => "rejected",
68 };
69 f.debug_struct("Contract").field("state", &state).finish()
70 }
71}
72
73impl Contract {
74 pub fn get_id(&self) -> ContractId {
77 match self {
78 Contract::Offered(o) | Contract::Rejected(o) => o.id,
79 Contract::Accepted(o) => o.get_contract_id(),
80 Contract::Signed(o) | Contract::Confirmed(o) | Contract::Refunded(o) => {
81 o.accepted_contract.get_contract_id()
82 }
83 Contract::FailedAccept(c) => c.offered_contract.id,
84 Contract::FailedSign(c) => c.accepted_contract.get_contract_id(),
85 Contract::PreClosed(c) => c.signed_contract.accepted_contract.get_contract_id(),
86 Contract::Closed(c) => c.contract_id,
87 }
88 }
89
90 pub fn get_id_string(&self) -> String {
92 let mut string_id = String::with_capacity(32 * 2 + 2);
93 string_id.push_str("0x");
94 let id = self.get_id();
95 for i in &id {
96 write!(string_id, "{i:02x}").unwrap();
97 }
98
99 string_id
100 }
101
102 pub fn get_temporary_id(&self) -> ContractId {
104 match self {
105 Contract::Offered(o) | Contract::Rejected(o) => o.id,
106 Contract::Accepted(o) => o.offered_contract.id,
107 Contract::Signed(o) | Contract::Confirmed(o) | Contract::Refunded(o) => {
108 o.accepted_contract.offered_contract.id
109 }
110 Contract::FailedAccept(c) => c.offered_contract.id,
111 Contract::FailedSign(c) => c.accepted_contract.offered_contract.id,
112 Contract::PreClosed(c) => c.signed_contract.accepted_contract.offered_contract.id,
113 Contract::Closed(c) => c.temporary_contract_id,
114 }
115 }
116
117 pub fn get_counter_party_id(&self) -> PublicKey {
119 match self {
120 Contract::Offered(o) | Contract::Rejected(o) => o.counter_party,
121 Contract::Accepted(a) => a.offered_contract.counter_party,
122 Contract::Signed(s) | Contract::Confirmed(s) | Contract::Refunded(s) => {
123 s.accepted_contract.offered_contract.counter_party
124 }
125 Contract::PreClosed(c) => {
126 c.signed_contract
127 .accepted_contract
128 .offered_contract
129 .counter_party
130 }
131 Contract::Closed(c) => c.counter_party_id,
132 Contract::FailedAccept(f) => f.offered_contract.counter_party,
133 Contract::FailedSign(f) => f.accepted_contract.offered_contract.counter_party,
134 }
135 }
136
137 pub fn is_offer_party(&self) -> bool {
139 match self {
140 Contract::Offered(o) | Contract::Rejected(o) => o.is_offer_party,
141 Contract::Accepted(a) => a.offered_contract.is_offer_party,
142 Contract::Signed(s) | Contract::Confirmed(s) | Contract::Refunded(s) => {
143 s.accepted_contract.offered_contract.is_offer_party
144 }
145 Contract::FailedAccept(f) => f.offered_contract.is_offer_party,
146 Contract::FailedSign(f) => f.accepted_contract.offered_contract.is_offer_party,
147 Contract::PreClosed(c) => {
148 c.signed_contract
149 .accepted_contract
150 .offered_contract
151 .is_offer_party
152 }
153 Contract::Closed(_) => false,
154 }
155 }
156
157 pub fn get_collateral(
159 &self,
160 ) -> (
161 Amount, Amount, Amount, ) {
165 match self {
167 Contract::Offered(o) => (
168 o.offer_params.collateral,
169 o.total_collateral - o.offer_params.collateral,
170 o.total_collateral,
171 ),
172 Contract::Accepted(a) => (
173 a.offered_contract.offer_params.collateral,
174 a.accept_params.collateral,
175 a.offered_contract.total_collateral,
176 ),
177 Contract::Signed(s) | Contract::Confirmed(s) | Contract::Refunded(s) => (
178 s.accepted_contract.offered_contract.offer_params.collateral,
179 s.accepted_contract.accept_params.collateral,
180 s.accepted_contract.offered_contract.total_collateral,
181 ),
182 Contract::FailedAccept(f) => (
183 f.offered_contract.offer_params.collateral,
184 Amount::ZERO,
185 f.offered_contract.total_collateral,
186 ),
187 Contract::FailedSign(f) => (
188 f.accepted_contract.offered_contract.offer_params.collateral,
189 f.accepted_contract.accept_params.collateral,
190 f.accepted_contract.offered_contract.total_collateral,
191 ),
192 Contract::PreClosed(p) => (
193 p.signed_contract
194 .accepted_contract
195 .offered_contract
196 .offer_params
197 .collateral,
198 p.signed_contract.accepted_contract.accept_params.collateral,
199 p.signed_contract
200 .accepted_contract
201 .offered_contract
202 .total_collateral,
203 ),
204 Contract::Closed(_) => (Amount::ZERO, Amount::ZERO, Amount::ZERO),
205 Contract::Rejected(_) => (Amount::ZERO, Amount::ZERO, Amount::ZERO),
206 }
207 }
208
209 pub fn get_cet_locktime(&self) -> u32 {
211 match self {
212 Contract::Offered(o) => o.cet_locktime,
213 Contract::Accepted(a) => a.offered_contract.cet_locktime,
214 Contract::Signed(s) => s.accepted_contract.offered_contract.cet_locktime,
215 Contract::Confirmed(c) => c.accepted_contract.offered_contract.cet_locktime,
216 Contract::PreClosed(p) => {
217 p.signed_contract
218 .accepted_contract
219 .offered_contract
220 .cet_locktime
221 }
222 Contract::Closed(c) => c.signed_cet.as_ref().unwrap().lock_time.to_consensus_u32(),
223 Contract::Refunded(r) => r.accepted_contract.offered_contract.cet_locktime,
224 Contract::FailedAccept(f) => f.offered_contract.cet_locktime,
225 Contract::FailedSign(f) => f.accepted_contract.offered_contract.cet_locktime,
226 Contract::Rejected(_) => 0,
227 }
228 }
229
230 pub fn get_refund_locktime(&self) -> u32 {
232 match self {
233 Contract::Offered(o) => o.refund_locktime,
234 Contract::Accepted(a) => a.offered_contract.refund_locktime,
235 Contract::Signed(s) => s.accepted_contract.offered_contract.refund_locktime,
236 Contract::Confirmed(c) => c.accepted_contract.offered_contract.refund_locktime,
237 Contract::PreClosed(p) => {
238 p.signed_contract
239 .accepted_contract
240 .offered_contract
241 .refund_locktime
242 }
243 Contract::Closed(c) => c.signed_cet.as_ref().unwrap().lock_time.to_consensus_u32(),
244 Contract::Refunded(r) => r.accepted_contract.offered_contract.refund_locktime,
245 Contract::FailedAccept(f) => f.offered_contract.refund_locktime,
246 Contract::FailedSign(f) => f.accepted_contract.offered_contract.refund_locktime,
247 Contract::Rejected(_) => 0,
248 }
249 }
250
251 pub fn get_pnl(&self) -> SignedAmount {
253 match self {
254 Contract::Offered(_) => SignedAmount::ZERO,
255 Contract::Accepted(_) => SignedAmount::ZERO,
256 Contract::Signed(_) => SignedAmount::ZERO,
257 Contract::Confirmed(_) => SignedAmount::ZERO,
258 Contract::PreClosed(p) => p
259 .signed_contract
260 .accepted_contract
261 .compute_pnl(&p.signed_cet),
262 Contract::Closed(c) => c.pnl,
263 Contract::Refunded(_) => SignedAmount::ZERO,
264 Contract::FailedAccept(_) => SignedAmount::ZERO,
265 Contract::FailedSign(_) => SignedAmount::ZERO,
266 Contract::Rejected(_) => SignedAmount::ZERO,
267 }
268 }
269
270 pub fn get_funding_txid(&self) -> Option<Txid> {
272 match self {
273 Contract::Offered(_) => None,
274 Contract::Accepted(a) => Some(a.dlc_transactions.fund.compute_txid()),
275 Contract::Signed(s) => Some(s.accepted_contract.dlc_transactions.fund.compute_txid()),
276 Contract::Confirmed(c) => {
277 Some(c.accepted_contract.dlc_transactions.fund.compute_txid())
278 }
279 Contract::PreClosed(p) => Some(
280 p.signed_contract
281 .accepted_contract
282 .dlc_transactions
283 .fund
284 .compute_txid(),
285 ),
286 Contract::Closed(c) => Some(c.funding_txid),
287 Contract::Refunded(r) => Some(r.accepted_contract.dlc_transactions.fund.compute_txid()),
288 Contract::FailedAccept(_) => None,
289 Contract::FailedSign(_) => None,
290 Contract::Rejected(_) => None,
291 }
292 }
293
294 pub fn get_oracle_announcement(&self) -> Option<OracleAnnouncement> {
296 match self {
297 Contract::Offered(o) => Some(o.contract_info[0].oracle_announcements[0].clone()),
298 Contract::Accepted(a) => {
299 Some(a.offered_contract.contract_info[0].oracle_announcements[0].clone())
300 }
301 Contract::Signed(s) => Some(
302 s.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
303 .clone(),
304 ),
305 Contract::Confirmed(c) => Some(
306 c.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
307 .clone(),
308 ),
309 Contract::PreClosed(p) => Some(
310 p.signed_contract
311 .accepted_contract
312 .offered_contract
313 .contract_info[0]
314 .oracle_announcements[0]
315 .clone(),
316 ),
317 Contract::Closed(_) => None,
318 Contract::Refunded(r) => Some(
319 r.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
320 .clone(),
321 ),
322 Contract::FailedAccept(f) => {
323 Some(f.offered_contract.contract_info[0].oracle_announcements[0].clone())
324 }
325 Contract::FailedSign(f) => Some(
326 f.accepted_contract.offered_contract.contract_info[0].oracle_announcements[0]
327 .clone(),
328 ),
329 Contract::Rejected(r) => Some(r.contract_info[0].oracle_announcements[0].clone()),
330 }
331 }
332
333 pub fn get_cet_txid(&self) -> Option<Txid> {
335 match self {
336 Contract::Offered(_) => None,
337 Contract::Accepted(_) => None,
338 Contract::Signed(_) => None,
339 Contract::Confirmed(_) => None,
340 Contract::PreClosed(p) => Some(p.signed_cet.compute_txid()),
341 Contract::Closed(c) => c.signed_cet.as_ref().map(|cet| cet.compute_txid()),
342 Contract::Refunded(_) => None,
343 Contract::FailedAccept(_) => None,
344 Contract::FailedSign(_) => None,
345 Contract::Rejected(_) => None,
346 }
347 }
348}
349
350#[derive(Clone)]
352pub struct FailedAcceptContract {
353 pub offered_contract: offered_contract::OfferedContract,
355 pub accept_message: AcceptDlc,
357 pub error_message: String,
359}
360
361#[derive(Clone)]
363pub struct FailedSignContract {
364 pub accepted_contract: accepted_contract::AcceptedContract,
366 pub sign_message: SignDlc,
368 pub error_message: String,
370}
371
372#[derive(Clone)]
374pub struct PreClosedContract {
375 pub signed_contract: SignedContract,
377 pub attestations: Option<Vec<OracleAttestation>>,
379 pub signed_cet: Transaction,
381}
382
383#[derive(Clone)]
385pub struct ClosedContract {
386 pub attestations: Option<Vec<OracleAttestation>>,
388 pub signed_cet: Option<Transaction>,
390 pub contract_id: ContractId,
392 pub temporary_contract_id: ContractId,
394 pub counter_party_id: PublicKey,
396 pub funding_txid: Txid,
398 pub pnl: SignedAmount,
400}
401
402#[derive(Clone)]
405pub enum AdaptorInfo {
406 Enum,
408 Numerical(MultiOracleTrie),
410 NumericalWithDifference(MultiOracleTrieWithDiff),
413}
414
415#[derive(Clone, Debug)]
417#[cfg_attr(
418 feature = "use-serde",
419 derive(Serialize, Deserialize),
420 serde(rename_all = "camelCase")
421)]
422pub enum ContractDescriptor {
423 Enum(enum_descriptor::EnumDescriptor),
425 Numerical(numerical_descriptor::NumericalDescriptor),
427}
428
429impl ContractDescriptor {
430 pub fn get_oracle_params(&self) -> Option<numerical_descriptor::DifferenceParams> {
432 match self {
433 ContractDescriptor::Enum(_) => None,
434 ContractDescriptor::Numerical(n) => n.difference_params.clone(),
435 }
436 }
437
438 pub fn validate(
441 &self,
442 announcements: &Vec<OracleAnnouncement>,
443 ) -> Result<(), crate::error::Error> {
444 let first = announcements
445 .first()
446 .expect("to have at least one element.");
447 match &first.oracle_event.event_descriptor {
448 EventDescriptor::EnumEvent(ee) => {
449 for announcement in announcements {
450 match &announcement.oracle_event.event_descriptor {
451 EventDescriptor::EnumEvent(enum_desc) => {
452 if !unordered_equal(&ee.outcomes, &enum_desc.outcomes) {
453 return Err(Error::InvalidParameters(
454 "Oracles don't have same enum outcomes.".to_string(),
455 ));
456 }
457 }
458 _ => {
459 return Err(Error::InvalidParameters(
460 "Expected enum event descriptor.".to_string(),
461 ))
462 }
463 }
464 }
465 match self {
466 ContractDescriptor::Enum(ed) => ed.validate(ee),
467 _ => Err(Error::InvalidParameters(
468 "Event descriptor from contract and oracle differ.".to_string(),
469 )),
470 }
471 }
472 EventDescriptor::DigitDecompositionEvent(_) => match self {
473 ContractDescriptor::Numerical(n) => {
474 let min_nb_digits = n.oracle_numeric_infos.get_min_nb_digits();
475 let max_value = n
476 .oracle_numeric_infos
477 .base
478 .checked_pow(min_nb_digits as u32)
479 .ok_or_else(|| {
480 Error::InvalidParameters("Could not compute max value".to_string())
481 })?;
482 n.validate((max_value - 1) as u64)
483 }
484 _ => Err(Error::InvalidParameters(
485 "Event descriptor from contract and oracle differ.".to_string(),
486 )),
487 },
488 }
489 }
490}