1#![crate_name = "ddk_trie"]
5#![forbid(unsafe_code)]
7#![deny(non_upper_case_globals)]
8#![deny(non_camel_case_types)]
9#![deny(non_snake_case)]
10#![deny(unused_mut)]
11#![deny(dead_code)]
12#![deny(unused_imports)]
13#![deny(missing_docs)]
14
15extern crate bitcoin;
16extern crate ddk_dlc;
17#[cfg(feature = "parallel")]
18extern crate rayon;
19extern crate secp256k1_zkp;
20#[cfg(feature = "use-serde")]
21extern crate serde;
22
23use bitcoin::{Amount, Script, Transaction};
24use ddk_dlc::{Error, RangePayout};
25#[cfg(feature = "parallel")]
26use rayon::prelude::*;
27use secp256k1_zkp::{All, EcdsaAdaptorSignature, PublicKey, Secp256k1, SecretKey};
28#[cfg(feature = "use-serde")]
29use serde::{Deserialize, Serialize};
30
31pub mod combination_iterator;
32pub mod digit_decomposition;
33pub mod digit_trie;
34pub mod multi_oracle;
35pub mod multi_oracle_trie;
36pub mod multi_oracle_trie_with_diff;
37pub mod multi_trie;
38#[cfg(test)]
39mod test_utils;
40mod utils;
41
42pub(crate) type IndexedPath = (usize, Vec<usize>);
43
44#[derive(Debug, Clone)]
47pub struct LookupResult<'a, TValue, TPath> {
48 pub path: Vec<TPath>,
50 pub value: &'a TValue,
52}
53
54#[derive(Debug, Clone)]
56pub enum Node<TLeaf, TNode> {
57 None,
60 Leaf(TLeaf),
62 Node(TNode),
64}
65
66#[derive(Eq, PartialEq, Debug, Clone)]
67pub struct RangeInfo {
71 pub cet_index: usize,
73 pub adaptor_index: usize,
75}
76
77#[derive(Clone, Debug)]
78#[cfg_attr(
79 feature = "use-serde",
80 derive(Serialize, Deserialize),
81 serde(rename_all = "camelCase")
82)]
83pub struct OracleNumericInfo {
85 pub base: usize,
87 pub nb_digits: Vec<usize>,
89}
90
91impl OracleNumericInfo {
92 pub fn get_min_nb_digits(&self) -> usize {
94 *self.nb_digits.iter().min().unwrap()
95 }
96
97 pub fn has_diff_nb_digits(&self) -> bool {
99 self.nb_digits
100 .iter()
101 .skip(1)
102 .any(|x| *x != self.nb_digits[0])
103 }
104}
105
106pub trait DlcTrie<'a, TrieIterator: Iterator<Item = TrieIterInfo>> {
109 fn generate(
113 &'a mut self,
114 adaptor_index_start: usize,
115 outcomes: &[RangePayout],
116 ) -> Result<Vec<TrieIterInfo>, Error>;
117
118 fn iter(&'a self) -> TrieIterator;
120
121 #[allow(clippy::too_many_arguments)]
123 fn generate_verify(
124 &'a mut self,
125 secp: &Secp256k1<secp256k1_zkp::All>,
126 fund_pubkey: &PublicKey,
127 funding_script_pubkey: &Script,
128 fund_output_value: Amount,
129 outcomes: &[RangePayout],
130 cets: &[Transaction],
131 precomputed_points: &[Vec<Vec<PublicKey>>],
132 adaptor_sigs: &[EcdsaAdaptorSignature],
133 adaptor_index_start: usize,
134 ) -> Result<usize, Error> {
135 let trie_info = self.generate(adaptor_index_start, outcomes)?;
136 verify_helper(
137 secp,
138 cets,
139 adaptor_sigs,
140 fund_pubkey,
141 funding_script_pubkey,
142 fund_output_value,
143 precomputed_points,
144 trie_info.into_iter(),
145 )
146 }
147
148 #[allow(clippy::too_many_arguments)]
150 fn generate_sign(
151 &'a mut self,
152 secp: &Secp256k1<All>,
153 fund_privkey: &SecretKey,
154 funding_script_pubkey: &Script,
155 fund_output_value: Amount,
156 outcomes: &[RangePayout],
157 cets: &[Transaction],
158 precomputed_points: &[Vec<Vec<PublicKey>>],
159 adaptor_index_start: usize,
160 ) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
161 let trie_info = self.generate(adaptor_index_start, outcomes)?;
162 sign_helper(
163 secp,
164 cets,
165 fund_privkey,
166 funding_script_pubkey,
167 fund_output_value,
168 precomputed_points,
169 trie_info.into_iter(),
170 )
171 }
172
173 #[allow(clippy::too_many_arguments)]
176 fn verify(
177 &'a self,
178 secp: &Secp256k1<All>,
179 fund_pubkey: &PublicKey,
180 funding_script_pubkey: &Script,
181 fund_output_value: Amount,
182 adaptor_sigs: &[EcdsaAdaptorSignature],
183 cets: &[Transaction],
184 precomputed_points: &[Vec<Vec<PublicKey>>],
185 ) -> Result<usize, Error> {
186 verify_helper(
187 secp,
188 cets,
189 adaptor_sigs,
190 fund_pubkey,
191 funding_script_pubkey,
192 fund_output_value,
193 precomputed_points,
194 self.iter(),
195 )
196 }
197
198 fn sign(
200 &'a self,
201 secp: &Secp256k1<All>,
202 fund_privkey: &SecretKey,
203 funding_script_pubkey: &Script,
204 fund_output_value: Amount,
205 cets: &[Transaction],
206 precomputed_points: &[Vec<Vec<PublicKey>>],
207 ) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
208 let trie_info = self.iter();
209 sign_helper(
210 secp,
211 cets,
212 fund_privkey,
213 funding_script_pubkey,
214 fund_output_value,
215 precomputed_points,
216 trie_info,
217 )
218 }
219}
220
221#[derive(Debug)]
222pub struct TrieIterInfo {
224 indexes: Vec<usize>,
225 paths: Vec<Vec<usize>>,
226 value: RangeInfo,
227}
228
229#[cfg(not(feature = "parallel"))]
230fn sign_helper<T: Iterator<Item = TrieIterInfo>>(
231 secp: &Secp256k1<All>,
232 cets: &[Transaction],
233 fund_privkey: &SecretKey,
234 funding_script_pubkey: &Script,
235 fund_output_value: Amount,
236 precomputed_points: &[Vec<Vec<PublicKey>>],
237 trie_info: T,
238) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
239 let mut unsorted = trie_info
240 .map(|x| {
241 let adaptor_point = utils::get_adaptor_point_for_indexed_paths(
242 &x.indexes,
243 &x.paths,
244 precomputed_points,
245 )?;
246 let adaptor_sig = ddk_dlc::create_cet_adaptor_sig_from_point(
247 secp,
248 &cets[x.value.cet_index],
249 &adaptor_point,
250 fund_privkey,
251 funding_script_pubkey,
252 fund_output_value,
253 )?;
254 Ok((x.value.adaptor_index, adaptor_sig))
255 })
256 .collect::<Result<Vec<(usize, EcdsaAdaptorSignature)>, Error>>()?;
257 unsorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
258 Ok(unsorted.into_iter().map(|(_, y)| y).collect())
259}
260
261#[cfg(feature = "parallel")]
262fn sign_helper<T: Iterator<Item = TrieIterInfo>>(
263 secp: &Secp256k1<All>,
264 cets: &[Transaction],
265 fund_privkey: &SecretKey,
266 funding_script_pubkey: &Script,
267 fund_output_value: Amount,
268 precomputed_points: &[Vec<Vec<PublicKey>>],
269 trie_info: T,
270) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
271 let trie_info: Vec<TrieIterInfo> = trie_info.collect();
272 let mut unsorted = trie_info
273 .par_iter()
274 .map(|x| {
275 let adaptor_point = utils::get_adaptor_point_for_indexed_paths(
276 &x.indexes,
277 &x.paths,
278 precomputed_points,
279 )?;
280 let adaptor_sig = ddk_dlc::create_cet_adaptor_sig_from_point(
281 secp,
282 &cets[x.value.cet_index],
283 &adaptor_point,
284 fund_privkey,
285 funding_script_pubkey,
286 fund_output_value,
287 )?;
288 Ok((x.value.adaptor_index, adaptor_sig))
289 })
290 .collect::<Result<Vec<(usize, EcdsaAdaptorSignature)>, Error>>()?;
291 unsorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
292 Ok(unsorted.into_iter().map(|(_, y)| y).collect())
293}
294
295#[cfg(not(feature = "parallel"))]
296#[allow(clippy::too_many_arguments)]
297fn verify_helper<T: Iterator<Item = TrieIterInfo>>(
298 secp: &Secp256k1<All>,
299 cets: &[Transaction],
300 adaptor_sigs: &[EcdsaAdaptorSignature],
301 fund_pubkey: &PublicKey,
302 funding_script_pubkey: &Script,
303 fund_output_value: Amount,
304 precomputed_points: &[Vec<Vec<PublicKey>>],
305 trie_info: T,
306) -> Result<usize, Error> {
307 let mut max_adaptor_index = 0;
308 for x in trie_info {
309 let adaptor_point =
310 utils::get_adaptor_point_for_indexed_paths(&x.indexes, &x.paths, precomputed_points)?;
311 let adaptor_sig = adaptor_sigs[x.value.adaptor_index];
312 let cet = &cets[x.value.cet_index];
313 if x.value.adaptor_index > max_adaptor_index {
314 max_adaptor_index = x.value.adaptor_index;
315 }
316 ddk_dlc::verify_cet_adaptor_sig_from_point(
317 secp,
318 &adaptor_sig,
319 cet,
320 &adaptor_point,
321 fund_pubkey,
322 funding_script_pubkey,
323 fund_output_value,
324 )?;
325 }
326 Ok(max_adaptor_index + 1)
327}
328
329#[cfg(feature = "parallel")]
330fn verify_helper<T: Iterator<Item = TrieIterInfo>>(
331 secp: &Secp256k1<All>,
332 cets: &[Transaction],
333 adaptor_sigs: &[EcdsaAdaptorSignature],
334 fund_pubkey: &PublicKey,
335 funding_script_pubkey: &Script,
336 fund_output_value: Amount,
337 precomputed_points: &[Vec<Vec<PublicKey>>],
338 trie_info: T,
339) -> Result<usize, Error> {
340 let trie_info: Vec<TrieIterInfo> = trie_info.collect();
341 let max_adaptor_index = trie_info
342 .iter()
343 .max_by(|x, y| x.value.adaptor_index.cmp(&y.value.adaptor_index))
344 .unwrap();
345 trie_info.par_iter().try_for_each(|x| {
346 let adaptor_point =
347 utils::get_adaptor_point_for_indexed_paths(&x.indexes, &x.paths, precomputed_points)?;
348 let adaptor_sig = adaptor_sigs[x.value.adaptor_index];
349 let cet = &cets[x.value.cet_index];
350 ddk_dlc::verify_cet_adaptor_sig_from_point(
351 secp,
352 &adaptor_sig,
353 cet,
354 &adaptor_point,
355 fund_pubkey,
356 funding_script_pubkey,
357 fund_output_value,
358 )
359 })?;
360
361 Ok(max_adaptor_index.value.adaptor_index + 1)
362}