ddk_manager/contract/
mod.rs

1//! Module containing structures and functions related to contracts.
2
3use crate::error::Error;
4use crate::ContractId;
5use bitcoin::Transaction;
6use dlc_messages::{
7    oracle_msgs::{EventDescriptor, OracleAnnouncement, OracleAttestation},
8    AcceptDlc, SignDlc,
9};
10use dlc_trie::multi_oracle_trie::MultiOracleTrie;
11use dlc_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)]
31/// Enum representing the possible states of a DLC.
32pub enum Contract {
33    /// Initial state where a contract is being proposed.
34    Offered(offered_contract::OfferedContract),
35    /// A contract that was accepted.
36    Accepted(accepted_contract::AcceptedContract),
37    /// A contract for which signatures have been produced.
38    Signed(signed_contract::SignedContract),
39    /// A contract whose funding transaction was included in the blockchain.
40    Confirmed(signed_contract::SignedContract),
41    /// A contract for which a CET was broadcasted, but not neccesarily confirmed to blockchain
42    PreClosed(PreClosedContract),
43    /// A contract for which a CET was confirmed to blockchain
44    Closed(ClosedContract),
45    /// A contract whose refund transaction was broadcast.
46    Refunded(signed_contract::SignedContract),
47    /// A contract that failed when verifying information from an accept message.
48    FailedAccept(FailedAcceptContract),
49    /// A contract that failed when verifying information from a sign message.
50    FailedSign(FailedSignContract),
51    /// A contract that was rejected by the party to whom it was offered.
52    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    /// Get the id of a contract. Returns the temporary contract id for offered
75    /// and failed accept contracts.
76    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    /// Get the string representation of the contract id.
91    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, "{:02x}", i).unwrap();
97        }
98
99        string_id
100    }
101
102    /// Returns the temporary contract id of a contract.
103    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    /// Returns the public key of the counter party's node.
118    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    /// Checks if the contract is the offer party.
138    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    /// Get the collateral for a contract.
158    pub fn get_collateral(
159        &self,
160    ) -> (
161        u64, /* offer collateral */
162        u64, /* accept collateral */
163        u64, /* total collateral */
164    ) {
165        // TODO: We should assert that the offer + accept collateral is equal to the total collateral
166        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                0,
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(_) => (0, 0, 0),
205            Contract::Rejected(_) => (0, 0, 0),
206        }
207    }
208
209    /// Get the CET locktime for a contract.
210    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    /// Get the refund locktime for a contract.
231    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    /// Get the profit and loss for a contract.
252    pub fn get_pnl(&self) -> i64 {
253        match self {
254            Contract::Offered(_) => 0,
255            Contract::Accepted(_) => 0,
256            Contract::Signed(_) => 0,
257            Contract::Confirmed(_) => 0,
258            Contract::PreClosed(_) => 0,
259            Contract::Closed(c) => c.pnl,
260            Contract::Refunded(_) => 0,
261            Contract::FailedAccept(_) => 0,
262            Contract::FailedSign(_) => 0,
263            Contract::Rejected(_) => 0,
264        }
265    }
266}
267
268/// Information about a contract that failed while verifying an accept message.
269#[derive(Clone)]
270pub struct FailedAcceptContract {
271    /// The offered contract that was accepted.
272    pub offered_contract: offered_contract::OfferedContract,
273    /// The received accept message.
274    pub accept_message: AcceptDlc,
275    /// The error message that was generated.
276    pub error_message: String,
277}
278
279/// Information about a contract that failed while verifying a sign message.
280#[derive(Clone)]
281pub struct FailedSignContract {
282    /// The accepted contract that was signed.
283    pub accepted_contract: accepted_contract::AcceptedContract,
284    /// The sign message that was received.
285    pub sign_message: SignDlc,
286    /// The error message that was generated.
287    pub error_message: String,
288}
289
290/// Information about a contract that is almost closed by a broadcasted, but not confirmed CET.
291#[derive(Clone)]
292pub struct PreClosedContract {
293    /// The signed contract that was closed.
294    pub signed_contract: SignedContract,
295    /// The attestations that were used to decrypt the broadcast CET.
296    pub attestations: Option<Vec<OracleAttestation>>,
297    /// The signed version of the CET that was broadcast.
298    pub signed_cet: Transaction,
299}
300
301/// Information about a contract that was closed by a CET that was confirmed on the blockchain.
302#[derive(Clone)]
303pub struct ClosedContract {
304    /// The attestations that were used to decrypt the broadcast CET.
305    pub attestations: Option<Vec<OracleAttestation>>,
306    /// The signed version of the CET that was broadcast.
307    pub signed_cet: Option<Transaction>,
308    /// The id of the contract
309    pub contract_id: ContractId,
310    /// The temporary id of the contract.
311    pub temporary_contract_id: ContractId,
312    /// The public key of the counter-party's node.
313    pub counter_party_id: PublicKey,
314    /// The profit and loss for the given contract
315    pub pnl: i64,
316}
317
318/// Information about the adaptor signatures and the CET for which they are
319/// valid.
320#[derive(Clone)]
321pub enum AdaptorInfo {
322    /// For enumeration outcome DLC, no special information needs to be kept.
323    Enum,
324    /// For numerical outcome DLC, a trie is used to store the information.
325    Numerical(MultiOracleTrie),
326    /// For numerical outcome DLC where oracles are allowed to diverge to some
327    /// extent in the outcome value, a trie of trie is used to store the information.
328    NumericalWithDifference(MultiOracleTrieWithDiff),
329}
330
331/// The descriptor of a contract.
332#[derive(Clone, Debug)]
333#[cfg_attr(
334    feature = "use-serde",
335    derive(Serialize, Deserialize),
336    serde(rename_all = "camelCase")
337)]
338pub enum ContractDescriptor {
339    /// Case for enumeration outcome DLC.
340    Enum(enum_descriptor::EnumDescriptor),
341    /// Case for numerical outcome DLC.
342    Numerical(numerical_descriptor::NumericalDescriptor),
343}
344
345impl ContractDescriptor {
346    /// Get the parameters on allowed divergence between oracle if any.
347    pub fn get_oracle_params(&self) -> Option<numerical_descriptor::DifferenceParams> {
348        match self {
349            ContractDescriptor::Enum(_) => None,
350            ContractDescriptor::Numerical(n) => n.difference_params.clone(),
351        }
352    }
353
354    /// Validate that all possible outcomes that can be attested by the oracle(s)
355    /// have a single associated payout.
356    pub fn validate(
357        &self,
358        announcements: &Vec<OracleAnnouncement>,
359    ) -> Result<(), crate::error::Error> {
360        let first = announcements
361            .first()
362            .expect("to have at least one element.");
363        match &first.oracle_event.event_descriptor {
364            EventDescriptor::EnumEvent(ee) => {
365                for announcement in announcements {
366                    match &announcement.oracle_event.event_descriptor {
367                        EventDescriptor::EnumEvent(enum_desc) => {
368                            if !unordered_equal(&ee.outcomes, &enum_desc.outcomes) {
369                                return Err(Error::InvalidParameters(
370                                    "Oracles don't have same enum outcomes.".to_string(),
371                                ));
372                            }
373                        }
374                        _ => {
375                            return Err(Error::InvalidParameters(
376                                "Expected enum event descriptor.".to_string(),
377                            ))
378                        }
379                    }
380                }
381                match self {
382                    ContractDescriptor::Enum(ed) => ed.validate(ee),
383                    _ => Err(Error::InvalidParameters(
384                        "Event descriptor from contract and oracle differ.".to_string(),
385                    )),
386                }
387            }
388            EventDescriptor::DigitDecompositionEvent(_) => match self {
389                ContractDescriptor::Numerical(n) => {
390                    let min_nb_digits = n.oracle_numeric_infos.get_min_nb_digits();
391                    let max_value = n
392                        .oracle_numeric_infos
393                        .base
394                        .checked_pow(min_nb_digits as u32)
395                        .ok_or_else(|| {
396                            Error::InvalidParameters("Could not compute max value".to_string())
397                        })?;
398                    n.validate((max_value - 1) as u64)
399                }
400                _ => Err(Error::InvalidParameters(
401                    "Event descriptor from contract and oracle differ.".to_string(),
402                )),
403            },
404        }
405    }
406}