#![crate_name = "ddk_trie"]
#![forbid(unsafe_code)]
#![deny(non_upper_case_globals)]
#![deny(non_camel_case_types)]
#![deny(non_snake_case)]
#![deny(unused_mut)]
#![deny(dead_code)]
#![deny(unused_imports)]
#![deny(missing_docs)]
extern crate bitcoin;
extern crate ddk_dlc;
#[cfg(feature = "parallel")]
extern crate rayon;
extern crate secp256k1_zkp;
#[cfg(feature = "use-serde")]
extern crate serde;
use bitcoin::{Amount, Script, Transaction};
use ddk_dlc::{Error, RangePayout};
#[cfg(feature = "parallel")]
use rayon::prelude::*;
use secp256k1_zkp::{All, EcdsaAdaptorSignature, PublicKey, Secp256k1, SecretKey};
#[cfg(feature = "use-serde")]
use serde::{Deserialize, Serialize};
pub mod combination_iterator;
pub mod digit_decomposition;
pub mod digit_trie;
pub mod multi_oracle;
pub mod multi_oracle_trie;
pub mod multi_oracle_trie_with_diff;
pub mod multi_trie;
#[cfg(test)]
mod test_utils;
mod utils;
pub(crate) type IndexedPath = (usize, Vec<usize>);
#[derive(Debug, Clone)]
pub struct LookupResult<'a, TValue, TPath> {
pub path: Vec<TPath>,
pub value: &'a TValue,
}
#[derive(Debug, Clone)]
pub enum Node<TLeaf, TNode> {
None,
Leaf(TLeaf),
Node(TNode),
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RangeInfo {
pub cet_index: usize,
pub adaptor_index: usize,
}
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct OracleNumericInfo {
pub base: usize,
pub nb_digits: Vec<usize>,
}
impl OracleNumericInfo {
pub fn get_min_nb_digits(&self) -> usize {
*self.nb_digits.iter().min().unwrap()
}
pub fn has_diff_nb_digits(&self) -> bool {
self.nb_digits
.iter()
.skip(1)
.any(|x| *x != self.nb_digits[0])
}
}
pub trait DlcTrie<'a, TrieIterator: Iterator<Item = TrieIterInfo>> {
fn generate(
&'a mut self,
adaptor_index_start: usize,
outcomes: &[RangePayout],
) -> Result<Vec<TrieIterInfo>, Error>;
fn iter(&'a self) -> TrieIterator;
#[allow(clippy::too_many_arguments)]
fn generate_verify(
&'a mut self,
secp: &Secp256k1<secp256k1_zkp::All>,
fund_pubkey: &PublicKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
outcomes: &[RangePayout],
cets: &[Transaction],
precomputed_points: &[Vec<Vec<PublicKey>>],
adaptor_sigs: &[EcdsaAdaptorSignature],
adaptor_index_start: usize,
) -> Result<usize, Error> {
let trie_info = self.generate(adaptor_index_start, outcomes)?;
verify_helper(
secp,
cets,
adaptor_sigs,
fund_pubkey,
funding_script_pubkey,
fund_output_value,
precomputed_points,
trie_info.into_iter(),
)
}
#[allow(clippy::too_many_arguments)]
fn generate_sign(
&'a mut self,
secp: &Secp256k1<All>,
fund_privkey: &SecretKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
outcomes: &[RangePayout],
cets: &[Transaction],
precomputed_points: &[Vec<Vec<PublicKey>>],
adaptor_index_start: usize,
) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
let trie_info = self.generate(adaptor_index_start, outcomes)?;
sign_helper(
secp,
cets,
fund_privkey,
funding_script_pubkey,
fund_output_value,
precomputed_points,
trie_info.into_iter(),
)
}
#[allow(clippy::too_many_arguments)]
fn verify(
&'a self,
secp: &Secp256k1<All>,
fund_pubkey: &PublicKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
adaptor_sigs: &[EcdsaAdaptorSignature],
cets: &[Transaction],
precomputed_points: &[Vec<Vec<PublicKey>>],
) -> Result<usize, Error> {
verify_helper(
secp,
cets,
adaptor_sigs,
fund_pubkey,
funding_script_pubkey,
fund_output_value,
precomputed_points,
self.iter(),
)
}
fn sign(
&'a self,
secp: &Secp256k1<All>,
fund_privkey: &SecretKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
cets: &[Transaction],
precomputed_points: &[Vec<Vec<PublicKey>>],
) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
let trie_info = self.iter();
sign_helper(
secp,
cets,
fund_privkey,
funding_script_pubkey,
fund_output_value,
precomputed_points,
trie_info,
)
}
}
#[derive(Debug)]
pub struct TrieIterInfo {
indexes: Vec<usize>,
paths: Vec<Vec<usize>>,
value: RangeInfo,
}
#[cfg(not(feature = "parallel"))]
fn sign_helper<T: Iterator<Item = TrieIterInfo>>(
secp: &Secp256k1<All>,
cets: &[Transaction],
fund_privkey: &SecretKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
precomputed_points: &[Vec<Vec<PublicKey>>],
trie_info: T,
) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
let mut unsorted = trie_info
.map(|x| {
let adaptor_point = utils::get_adaptor_point_for_indexed_paths(
&x.indexes,
&x.paths,
precomputed_points,
)?;
let adaptor_sig = ddk_dlc::create_cet_adaptor_sig_from_point(
secp,
&cets[x.value.cet_index],
&adaptor_point,
fund_privkey,
funding_script_pubkey,
fund_output_value,
)?;
Ok((x.value.adaptor_index, adaptor_sig))
})
.collect::<Result<Vec<(usize, EcdsaAdaptorSignature)>, Error>>()?;
unsorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
Ok(unsorted.into_iter().map(|(_, y)| y).collect())
}
#[cfg(feature = "parallel")]
fn sign_helper<T: Iterator<Item = TrieIterInfo>>(
secp: &Secp256k1<All>,
cets: &[Transaction],
fund_privkey: &SecretKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
precomputed_points: &[Vec<Vec<PublicKey>>],
trie_info: T,
) -> Result<Vec<EcdsaAdaptorSignature>, Error> {
let trie_info: Vec<TrieIterInfo> = trie_info.collect();
let mut unsorted = trie_info
.par_iter()
.map(|x| {
let adaptor_point = utils::get_adaptor_point_for_indexed_paths(
&x.indexes,
&x.paths,
precomputed_points,
)?;
let adaptor_sig = ddk_dlc::create_cet_adaptor_sig_from_point(
secp,
&cets[x.value.cet_index],
&adaptor_point,
fund_privkey,
funding_script_pubkey,
fund_output_value,
)?;
Ok((x.value.adaptor_index, adaptor_sig))
})
.collect::<Result<Vec<(usize, EcdsaAdaptorSignature)>, Error>>()?;
unsorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
Ok(unsorted.into_iter().map(|(_, y)| y).collect())
}
#[cfg(not(feature = "parallel"))]
#[allow(clippy::too_many_arguments)]
fn verify_helper<T: Iterator<Item = TrieIterInfo>>(
secp: &Secp256k1<All>,
cets: &[Transaction],
adaptor_sigs: &[EcdsaAdaptorSignature],
fund_pubkey: &PublicKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
precomputed_points: &[Vec<Vec<PublicKey>>],
trie_info: T,
) -> Result<usize, Error> {
let mut max_adaptor_index = 0;
for x in trie_info {
let adaptor_point =
utils::get_adaptor_point_for_indexed_paths(&x.indexes, &x.paths, precomputed_points)?;
let adaptor_sig = adaptor_sigs[x.value.adaptor_index];
let cet = &cets[x.value.cet_index];
if x.value.adaptor_index > max_adaptor_index {
max_adaptor_index = x.value.adaptor_index;
}
ddk_dlc::verify_cet_adaptor_sig_from_point(
secp,
&adaptor_sig,
cet,
&adaptor_point,
fund_pubkey,
funding_script_pubkey,
fund_output_value,
)?;
}
Ok(max_adaptor_index + 1)
}
#[cfg(feature = "parallel")]
fn verify_helper<T: Iterator<Item = TrieIterInfo>>(
secp: &Secp256k1<All>,
cets: &[Transaction],
adaptor_sigs: &[EcdsaAdaptorSignature],
fund_pubkey: &PublicKey,
funding_script_pubkey: &Script,
fund_output_value: Amount,
precomputed_points: &[Vec<Vec<PublicKey>>],
trie_info: T,
) -> Result<usize, Error> {
let trie_info: Vec<TrieIterInfo> = trie_info.collect();
let max_adaptor_index = trie_info
.iter()
.max_by(|x, y| x.value.adaptor_index.cmp(&y.value.adaptor_index))
.unwrap();
trie_info.par_iter().try_for_each(|x| {
let adaptor_point =
utils::get_adaptor_point_for_indexed_paths(&x.indexes, &x.paths, precomputed_points)?;
let adaptor_sig = adaptor_sigs[x.value.adaptor_index];
let cet = &cets[x.value.cet_index];
ddk_dlc::verify_cet_adaptor_sig_from_point(
secp,
&adaptor_sig,
cet,
&adaptor_point,
fund_pubkey,
funding_script_pubkey,
fund_output_value,
)
})?;
Ok(max_adaptor_index.value.adaptor_index + 1)
}