1use 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#[derive(Clone, Debug)]
21#[cfg_attr(
22 feature = "use-serde",
23 derive(Serialize, Deserialize),
24 serde(rename_all = "camelCase")
25)]
26pub struct EnumDescriptor {
27 pub outcome_payouts: Vec<EnumerationPayout>,
29}
30
31impl EnumDescriptor {
32 pub fn get_payouts(&self) -> Vec<Payout> {
34 self.outcome_payouts
35 .iter()
36 .map(|x| x.payout.clone())
37 .collect()
38 }
39
40 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 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 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 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 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 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}