ddk_manager/contract/
enum_descriptor.rs

1//! #EnumDescriptor
2
3use super::contract_info::OracleIndexAndPrefixLength;
4use super::utils::{get_majority_combination, unordered_equal};
5use super::AdaptorInfo;
6use crate::error::Error;
7use bitcoin::hashes::Hash;
8use bitcoin::{Script, Transaction};
9use dlc::OracleInfo;
10use dlc::{EnumerationPayout, Payout};
11use dlc_messages::oracle_msgs::EnumEventDescriptor;
12use dlc_trie::{combination_iterator::CombinationIterator, RangeInfo};
13use secp256k1_zkp::{
14    All, EcdsaAdaptorSignature, Message, PublicKey, Secp256k1, SecretKey, Verification,
15};
16#[cfg(feature = "use-serde")]
17use serde::{Deserialize, Serialize};
18
19/// A descriptor for a contract whose outcomes are represented as an enumeration.
20#[derive(Clone, Debug)]
21#[cfg_attr(
22    feature = "use-serde",
23    derive(Serialize, Deserialize),
24    serde(rename_all = "camelCase")
25)]
26pub struct EnumDescriptor {
27    /// The set of outcomes.
28    pub outcome_payouts: Vec<EnumerationPayout>,
29}
30
31impl EnumDescriptor {
32    /// Returns the set of payouts.
33    pub fn get_payouts(&self) -> Vec<Payout> {
34        self.outcome_payouts
35            .iter()
36            .map(|x| x.payout.clone())
37            .collect()
38    }
39
40    /// Validate that the descriptor covers all possible outcomes of the given
41    /// enum event descriptor.
42    pub fn validate(&self, enum_event_descriptor: &EnumEventDescriptor) -> Result<(), Error> {
43        if unordered_equal(
44            &enum_event_descriptor.outcomes.iter().collect::<Vec<_>>(),
45            &self
46                .outcome_payouts
47                .iter()
48                .map(|x| &x.outcome)
49                .collect::<Vec<_>>(),
50        ) {
51            Ok(())
52        } else {
53            Err(Error::InvalidParameters(
54                "Oracle outcomes do not each have a single associated payout.".to_string(),
55            ))
56        }
57    }
58
59    /// Returns the `RangeInfo` that matches the given set of outcomes if any.
60    pub fn get_range_info_for_outcome(
61        &self,
62        nb_oracles: usize,
63        threshold: usize,
64        outcomes: &[(usize, &Vec<String>)],
65        adaptor_sig_start: usize,
66    ) -> Option<(OracleIndexAndPrefixLength, RangeInfo)> {
67        if outcomes.len() < threshold {
68            return None;
69        }
70
71        let filtered_outcomes: Vec<(usize, &Vec<String>)> = outcomes
72            .iter()
73            .filter(|x| x.1.len() == 1)
74            .cloned()
75            .collect();
76        let (mut outcome, mut actual_combination) = get_majority_combination(&filtered_outcomes)?;
77        let outcome = outcome.remove(0);
78
79        if actual_combination.len() < threshold {
80            return None;
81        }
82
83        actual_combination.truncate(threshold);
84
85        let pos = self
86            .outcome_payouts
87            .iter()
88            .position(|x| x.outcome == outcome)?;
89
90        let combinator = CombinationIterator::new(nb_oracles, threshold);
91        let mut comb_pos = 0;
92        let mut comb_count = 0;
93
94        for (i, combination) in combinator.enumerate() {
95            if combination == actual_combination {
96                comb_pos = i;
97            }
98            comb_count += 1;
99        }
100
101        let range_info = RangeInfo {
102            cet_index: pos,
103            adaptor_index: comb_count * pos + comb_pos + adaptor_sig_start,
104        };
105
106        Some((
107            actual_combination.iter().map(|x| (*x, 1)).collect(),
108            range_info,
109        ))
110    }
111
112    /// Verify the given set adaptor signatures.
113    #[allow(clippy::too_many_arguments)]
114    pub fn verify_adaptor_info(
115        &self,
116        secp: &Secp256k1<All>,
117        oracle_infos: &[OracleInfo],
118        threshold: usize,
119        fund_pubkey: &PublicKey,
120        funding_script_pubkey: &Script,
121        fund_output_value: u64,
122        cets: &[Transaction],
123        adaptor_sigs: &[EcdsaAdaptorSignature],
124        adaptor_sig_start: usize,
125    ) -> Result<usize, dlc::Error> {
126        let mut adaptor_sig_index = adaptor_sig_start;
127        let mut callback =
128            |adaptor_point: &PublicKey, cet_index: usize| -> Result<(), dlc::Error> {
129                let sig = adaptor_sigs[adaptor_sig_index];
130                adaptor_sig_index += 1;
131                dlc::verify_cet_adaptor_sig_from_point(
132                    secp,
133                    &sig,
134                    &cets[cet_index],
135                    adaptor_point,
136                    fund_pubkey,
137                    funding_script_pubkey,
138                    fund_output_value,
139                )?;
140                Ok(())
141            };
142
143        self.iter_outcomes(secp, oracle_infos, threshold, &mut callback)?;
144
145        Ok(adaptor_sig_index)
146    }
147
148    /// Verify the given set of adaptor signature and generates the adaptor info.
149    #[allow(clippy::too_many_arguments)]
150    pub fn verify_and_get_adaptor_info(
151        &self,
152        secp: &Secp256k1<All>,
153        oracle_infos: &[OracleInfo],
154        threshold: usize,
155        fund_pubkey: &PublicKey,
156        funding_script_pubkey: &Script,
157        fund_output_value: u64,
158        cets: &[Transaction],
159        adaptor_sigs: &[EcdsaAdaptorSignature],
160        adaptor_sig_start: usize,
161    ) -> Result<(AdaptorInfo, usize), dlc::Error> {
162        let adaptor_sig_index = self.verify_adaptor_info(
163            secp,
164            oracle_infos,
165            threshold,
166            fund_pubkey,
167            funding_script_pubkey,
168            fund_output_value,
169            cets,
170            adaptor_sigs,
171            adaptor_sig_start,
172        )?;
173
174        Ok((AdaptorInfo::Enum, adaptor_sig_index))
175    }
176
177    /// Generate the set of adaptor signatures and return the adaptor info.
178    #[allow(clippy::too_many_arguments)]
179    pub fn get_adaptor_info(
180        &self,
181        secp: &Secp256k1<All>,
182        oracle_infos: &[OracleInfo],
183        threshold: usize,
184        fund_privkey: &SecretKey,
185        funding_script_pubkey: &Script,
186        fund_output_value: u64,
187        cets: &[Transaction],
188    ) -> Result<(AdaptorInfo, Vec<EcdsaAdaptorSignature>), Error> {
189        let adaptor_sigs = self.get_adaptor_signatures(
190            secp,
191            oracle_infos,
192            threshold,
193            cets,
194            fund_privkey,
195            funding_script_pubkey,
196            fund_output_value,
197        )?;
198
199        Ok((AdaptorInfo::Enum, adaptor_sigs))
200    }
201
202    /// Generate the set of adaptor signatures.
203    #[allow(clippy::too_many_arguments)]
204    pub fn get_adaptor_signatures(
205        &self,
206        secp: &Secp256k1<All>,
207        oracle_infos: &[OracleInfo],
208        threshold: usize,
209        cets: &[Transaction],
210        fund_privkey: &SecretKey,
211        funding_script_pubkey: &Script,
212        fund_output_value: u64,
213    ) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
214        let mut adaptor_sigs = Vec::new();
215        let mut callback =
216            |adaptor_point: &PublicKey, cet_index: usize| -> Result<(), dlc::Error> {
217                let sig = dlc::create_cet_adaptor_sig_from_point(
218                    secp,
219                    &cets[cet_index],
220                    adaptor_point,
221                    fund_privkey,
222                    funding_script_pubkey,
223                    fund_output_value,
224                )?;
225                adaptor_sigs.push(sig);
226                Ok(())
227            };
228
229        self.iter_outcomes(secp, oracle_infos, threshold, &mut callback)?;
230
231        Ok(adaptor_sigs)
232    }
233
234    fn iter_outcomes<C: Verification, F>(
235        &self,
236        secp: &Secp256k1<C>,
237        oracle_infos: &[OracleInfo],
238        threshold: usize,
239        callback: &mut F,
240    ) -> Result<(), dlc::Error>
241    where
242        F: FnMut(&PublicKey, usize) -> Result<(), dlc::Error>,
243    {
244        let messages: Vec<Vec<Vec<Message>>> = self
245            .outcome_payouts
246            .iter()
247            .map(|x| {
248                let hash =
249                    bitcoin::hashes::sha256::Hash::hash(x.outcome.as_bytes()).to_byte_array();
250                let message = vec![Message::from_digest(hash)];
251                std::iter::repeat(message).take(threshold).collect()
252            })
253            .collect();
254        let combination_iter = CombinationIterator::new(oracle_infos.len(), threshold);
255        let combinations: Vec<Vec<usize>> = combination_iter.collect();
256
257        for (i, outcome_messages) in messages.iter().enumerate() {
258            for selector in &combinations {
259                let cur_oracle_infos: Vec<_> = oracle_infos
260                    .iter()
261                    .enumerate()
262                    .filter_map(|(i, x)| {
263                        if selector.contains(&i) {
264                            Some(x.clone())
265                        } else {
266                            None
267                        }
268                    })
269                    .collect();
270                let adaptor_point = dlc::get_adaptor_point_from_oracle_info(
271                    secp,
272                    &cur_oracle_infos,
273                    outcome_messages,
274                )?;
275                callback(&adaptor_point, i)?;
276            }
277        }
278
279        Ok(())
280    }
281}