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::{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 #[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 #[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 #[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 #[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}