dlc_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::{Amount, 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    pub fn verify_adaptor_info(
114        &self,
115        secp: &Secp256k1<All>,
116        oracle_infos: &[OracleInfo],
117        threshold: usize,
118        fund_pubkey: &PublicKey,
119        funding_script_pubkey: &Script,
120        fund_output_value: Amount,
121        cets: &[Transaction],
122        adaptor_sigs: &[EcdsaAdaptorSignature],
123        adaptor_sig_start: usize,
124    ) -> Result<usize, dlc::Error> {
125        let mut adaptor_sig_index = adaptor_sig_start;
126        let mut callback =
127            |adaptor_point: &PublicKey, cet_index: usize| -> Result<(), dlc::Error> {
128                let sig = adaptor_sigs[adaptor_sig_index];
129                adaptor_sig_index += 1;
130                dlc::verify_cet_adaptor_sig_from_point(
131                    secp,
132                    &sig,
133                    &cets[cet_index],
134                    adaptor_point,
135                    fund_pubkey,
136                    funding_script_pubkey,
137                    fund_output_value,
138                )?;
139                Ok(())
140            };
141
142        self.iter_outcomes(secp, oracle_infos, threshold, &mut callback)?;
143
144        Ok(adaptor_sig_index)
145    }
146
147    /// Verify the given set of adaptor signature and generates the adaptor info.
148    pub fn verify_and_get_adaptor_info(
149        &self,
150        secp: &Secp256k1<All>,
151        oracle_infos: &[OracleInfo],
152        threshold: usize,
153        fund_pubkey: &PublicKey,
154        funding_script_pubkey: &Script,
155        fund_output_value: Amount,
156        cets: &[Transaction],
157        adaptor_sigs: &[EcdsaAdaptorSignature],
158        adaptor_sig_start: usize,
159    ) -> Result<(AdaptorInfo, usize), dlc::Error> {
160        let adaptor_sig_index = self.verify_adaptor_info(
161            secp,
162            oracle_infos,
163            threshold,
164            fund_pubkey,
165            funding_script_pubkey,
166            fund_output_value,
167            cets,
168            adaptor_sigs,
169            adaptor_sig_start,
170        )?;
171
172        Ok((AdaptorInfo::Enum, adaptor_sig_index))
173    }
174
175    /// Generate the set of adaptor signatures and return the adaptor info.
176    pub fn get_adaptor_info(
177        &self,
178        secp: &Secp256k1<All>,
179        oracle_infos: &[OracleInfo],
180        threshold: usize,
181        fund_privkey: &SecretKey,
182        funding_script_pubkey: &Script,
183        fund_output_value: Amount,
184        cets: &[Transaction],
185    ) -> Result<(AdaptorInfo, Vec<EcdsaAdaptorSignature>), Error> {
186        let adaptor_sigs = self.get_adaptor_signatures(
187            secp,
188            oracle_infos,
189            threshold,
190            cets,
191            fund_privkey,
192            funding_script_pubkey,
193            fund_output_value,
194        )?;
195
196        Ok((AdaptorInfo::Enum, adaptor_sigs))
197    }
198
199    /// Generate the set of adaptor signatures.
200    pub fn get_adaptor_signatures(
201        &self,
202        secp: &Secp256k1<All>,
203        oracle_infos: &[OracleInfo],
204        threshold: usize,
205        cets: &[Transaction],
206        fund_privkey: &SecretKey,
207        funding_script_pubkey: &Script,
208        fund_output_value: Amount,
209    ) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
210        let mut adaptor_sigs = Vec::new();
211        let mut callback =
212            |adaptor_point: &PublicKey, cet_index: usize| -> Result<(), dlc::Error> {
213                let sig = dlc::create_cet_adaptor_sig_from_point(
214                    secp,
215                    &cets[cet_index],
216                    adaptor_point,
217                    fund_privkey,
218                    funding_script_pubkey,
219                    fund_output_value,
220                )?;
221                adaptor_sigs.push(sig);
222                Ok(())
223            };
224
225        self.iter_outcomes(secp, oracle_infos, threshold, &mut callback)?;
226
227        Ok(adaptor_sigs)
228    }
229
230    fn iter_outcomes<C: Verification, F>(
231        &self,
232        secp: &Secp256k1<C>,
233        oracle_infos: &[OracleInfo],
234        threshold: usize,
235        callback: &mut F,
236    ) -> Result<(), dlc::Error>
237    where
238        F: FnMut(&PublicKey, usize) -> Result<(), dlc::Error>,
239    {
240        let messages: Vec<Vec<Vec<Message>>> = self
241            .outcome_payouts
242            .iter()
243            .map(|x| {
244                let hash =
245                    bitcoin::hashes::sha256::Hash::hash(x.outcome.as_bytes()).to_byte_array();
246                let message = vec![Message::from_digest(hash)];
247                std::iter::repeat(message).take(threshold).collect()
248            })
249            .collect();
250        let combination_iter = CombinationIterator::new(oracle_infos.len(), threshold);
251        let combinations: Vec<Vec<usize>> = combination_iter.collect();
252
253        for (i, outcome_messages) in messages.iter().enumerate() {
254            for selector in &combinations {
255                let cur_oracle_infos: Vec<_> = oracle_infos
256                    .iter()
257                    .enumerate()
258                    .filter_map(|(i, x)| {
259                        if selector.contains(&i) {
260                            Some(x.clone())
261                        } else {
262                            None
263                        }
264                    })
265                    .collect();
266                let adaptor_point = dlc::get_adaptor_point_from_oracle_info(
267                    secp,
268                    &cur_oracle_infos,
269                    outcome_messages,
270                )?;
271                callback(&adaptor_point, i)?;
272            }
273        }
274
275        Ok(())
276    }
277}