ddk_manager/contract/
numerical_descriptor.rs

1//! #NumericalDescriptor
2
3use super::AdaptorInfo;
4use crate::error::Error;
5use crate::payout_curve::{PayoutFunction, RoundingIntervals};
6use bitcoin::{Script, Transaction};
7use dlc::{Payout, RangePayout};
8use dlc_trie::multi_oracle_trie::MultiOracleTrie;
9use dlc_trie::multi_oracle_trie_with_diff::MultiOracleTrieWithDiff;
10use dlc_trie::{DlcTrie, OracleNumericInfo};
11use secp256k1_zkp::{All, EcdsaAdaptorSignature, PublicKey, Secp256k1, SecretKey};
12#[cfg(feature = "use-serde")]
13use serde::{Deserialize, Serialize};
14
15/// Information about the allowed deviation in outcome value between the oracles.
16#[derive(Clone, Debug)]
17#[cfg_attr(
18    feature = "use-serde",
19    derive(Serialize, Deserialize),
20    serde(rename_all = "camelCase")
21)]
22pub struct DifferenceParams {
23    /// The maximum error above which the contract should failed to close. Note
24    /// that this value represents a power of two.
25    pub max_error_exp: usize,
26    /// The minimum error deviation under which the contract should be guaranteed
27    /// to be closeable.
28    pub min_support_exp: usize,
29    /// Whether to maximize the coverage of the \[min;max\] interval to increase
30    /// the probability of the contract being closeable within it.
31    pub maximize_coverage: bool,
32}
33
34#[derive(Clone, Debug)]
35#[cfg_attr(
36    feature = "use-serde",
37    derive(Serialize, Deserialize),
38    serde(rename_all = "camelCase")
39)]
40/// Contains information about a contract based on a numerical outcome.
41pub struct NumericalDescriptor {
42    /// The function representing the set of payouts.
43    pub payout_function: PayoutFunction,
44    /// Rounding intervals enabling reducing the precision of the payout values
45    /// which in turns reduces the number of required adaptor signatures.
46    pub rounding_intervals: RoundingIntervals,
47    /// Information about the allowed differences in outcome value between oracles.
48    /// If None, a quorum of oracle needs to sign the same value for the contract
49    /// to be closeable.
50    pub difference_params: Option<DifferenceParams>,
51    /// Information about base and number of digits for each oracle.
52    pub oracle_numeric_infos: OracleNumericInfo,
53}
54
55impl NumericalDescriptor {
56    /// Returns the set of RangePayout for the descriptor generated from the
57    /// payout function.
58    pub fn get_range_payouts(&self, total_collateral: u64) -> Result<Vec<RangePayout>, Error> {
59        self.payout_function
60            .to_range_payouts(total_collateral, &self.rounding_intervals)
61    }
62
63    /// Validate that the descriptor covers all possible outcomes of the given
64    /// digit decomposition event descriptor.
65    pub fn validate(&self, max_value: u64) -> Result<(), Error> {
66        self.rounding_intervals.validate()?;
67        self.payout_function.validate(max_value)
68    }
69
70    /// Returns the set of payouts for the descriptor generated from the payout
71    /// function.
72    pub fn get_payouts(&self, total_collateral: u64) -> Result<Vec<Payout>, Error> {
73        Ok(self
74            .get_range_payouts(total_collateral)?
75            .iter()
76            .map(|x| x.payout.clone())
77            .collect())
78    }
79
80    /// Verify the given set of adaptor signatures and generate the adaptor info.
81    #[allow(clippy::too_many_arguments)]
82    pub fn verify_and_get_adaptor_info(
83        &self,
84        secp: &Secp256k1<All>,
85        total_collateral: u64,
86        fund_pubkey: &PublicKey,
87        funding_script_pubkey: &Script,
88        fund_output_value: u64,
89        threshold: usize,
90        precomputed_points: &[Vec<Vec<PublicKey>>],
91        cets: &[Transaction],
92        adaptor_pairs: &[EcdsaAdaptorSignature],
93        adaptor_index_start: usize,
94    ) -> Result<(AdaptorInfo, usize), Error> {
95        match &self.difference_params {
96            Some(params) => {
97                let mut multi_trie = MultiOracleTrieWithDiff::new(
98                    &self.oracle_numeric_infos,
99                    threshold,
100                    params.min_support_exp,
101                    params.max_error_exp,
102                )?;
103                let index = multi_trie.generate_verify(
104                    secp,
105                    fund_pubkey,
106                    funding_script_pubkey,
107                    fund_output_value,
108                    &self.get_range_payouts(total_collateral)?,
109                    cets,
110                    precomputed_points,
111                    adaptor_pairs,
112                    adaptor_index_start,
113                )?;
114                Ok((AdaptorInfo::NumericalWithDifference(multi_trie), index))
115            }
116            None => {
117                let mut trie = MultiOracleTrie::new(&self.oracle_numeric_infos, threshold)?;
118                let index = trie.generate_verify(
119                    secp,
120                    fund_pubkey,
121                    funding_script_pubkey,
122                    fund_output_value,
123                    &self.get_range_payouts(total_collateral)?,
124                    cets,
125                    precomputed_points,
126                    adaptor_pairs,
127                    adaptor_index_start,
128                )?;
129                Ok((AdaptorInfo::Numerical(trie), index))
130            }
131        }
132    }
133
134    /// Generate the set of adaptor signatures and the adaptor info.
135    #[allow(clippy::too_many_arguments)]
136    pub fn get_adaptor_info(
137        &self,
138        secp: &Secp256k1<All>,
139        total_collateral: u64,
140        fund_priv_key: &SecretKey,
141        funding_script_pubkey: &Script,
142        fund_output_value: u64,
143        threshold: usize,
144        precomputed_points: &[Vec<Vec<PublicKey>>],
145        cets: &[Transaction],
146        adaptor_index_start: usize,
147    ) -> Result<(AdaptorInfo, Vec<EcdsaAdaptorSignature>), Error> {
148        match &self.difference_params {
149            Some(params) => {
150                let mut multi_trie = MultiOracleTrieWithDiff::new(
151                    &self.oracle_numeric_infos,
152                    threshold,
153                    params.min_support_exp,
154                    params.max_error_exp,
155                )?;
156                let adaptor_pairs = multi_trie.generate_sign(
157                    secp,
158                    fund_priv_key,
159                    funding_script_pubkey,
160                    fund_output_value,
161                    &self.get_range_payouts(total_collateral)?,
162                    cets,
163                    precomputed_points,
164                    adaptor_index_start,
165                )?;
166                Ok((
167                    AdaptorInfo::NumericalWithDifference(multi_trie),
168                    adaptor_pairs,
169                ))
170            }
171
172            None => {
173                let mut trie = MultiOracleTrie::new(&self.oracle_numeric_infos, threshold)?;
174                let sigs = trie.generate_sign(
175                    secp,
176                    fund_priv_key,
177                    funding_script_pubkey,
178                    fund_output_value,
179                    &self.get_range_payouts(total_collateral)?,
180                    cets,
181                    precomputed_points,
182                    adaptor_index_start,
183                )?;
184                Ok((AdaptorInfo::Numerical(trie), sigs))
185            }
186        }
187    }
188}