1use crate::contract::{
2 contract_info::ContractInfo,
3 enum_descriptor::EnumDescriptor,
4 numerical_descriptor::{DifferenceParams, NumericalDescriptor},
5 offered_contract::OfferedContract,
6 ContractDescriptor,
7};
8use crate::payout_curve::{
9 HyperbolaPayoutCurvePiece, PayoutFunction, PayoutFunctionPiece, PayoutPoint,
10 PolynomialPayoutCurvePiece, RoundingInterval, RoundingIntervals,
11};
12use bitcoin::{consensus::encode::Decodable, Amount, OutPoint, Transaction};
13use dlc::{EnumerationPayout, Payout, TxInputInfo};
14use dlc_messages::oracle_msgs::{
15 MultiOracleInfo, OracleInfo as SerOracleInfo, OracleParams, SingleOracleInfo,
16};
17use dlc_messages::FundingInput;
18use dlc_messages::{
19 contract_msgs::{
20 ContractDescriptor as SerContractDescriptor, ContractInfo as SerContractInfo,
21 ContractInfoInner, ContractOutcome, DisjointContractInfo, EnumeratedContractDescriptor,
22 HyperbolaPayoutCurvePiece as SerHyperbolaPayoutCurvePiece,
23 NumericOutcomeContractDescriptor, PayoutCurvePiece as SerPayoutCurvePiece,
24 PayoutFunction as SerPayoutFunction, PayoutFunctionPiece as SerPayoutFunctionPiece,
25 PayoutPoint as SerPayoutPoint, PolynomialPayoutCurvePiece as SerPolynomialPayoutCurvePiece,
26 RoundingInterval as SerRoundingInterval, RoundingIntervals as SerRoundingIntervals,
27 SingleContractInfo,
28 },
29 oracle_msgs::EventDescriptor,
30};
31use dlc_trie::OracleNumericInfo;
32use std::fmt;
33
34pub(crate) const BITCOIN_CHAINHASH: [u8; 32] = [
35 0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf,
36 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f,
37];
38
39pub(crate) const PROTOCOL_VERSION: u32 = 1;
40
41#[derive(Debug)]
42pub enum Error {
43 BitcoinEncoding(bitcoin::consensus::encode::Error),
44 InvalidParameters,
45}
46
47impl fmt::Display for Error {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 match *self {
50 Error::BitcoinEncoding(_) => write!(f, "Invalid encoding"),
51 Error::InvalidParameters => write!(f, "Invalid parameters."),
52 }
53 }
54}
55
56#[cfg(feature = "std")]
57impl std::error::Error for Error {
58 fn cause(&self) -> Option<&dyn std::error::Error> {
59 match *self {
60 Error::BitcoinEncoding(ref e) => Some(e),
61 Error::InvalidParameters => None,
62 }
63 }
64}
65
66impl From<bitcoin::consensus::encode::Error> for Error {
67 fn from(e: bitcoin::consensus::encode::Error) -> Error {
68 Error::BitcoinEncoding(e)
69 }
70}
71
72pub fn get_tx_input_infos(
73 funding_inputs: &[FundingInput],
74) -> Result<(Vec<TxInputInfo>, Amount), Error> {
75 let mut input_amount = Amount::ZERO;
76 let mut inputs = Vec::new();
77
78 for fund_input in funding_inputs {
79 let tx = Transaction::consensus_decode(&mut fund_input.prev_tx.as_slice())?;
80 let vout = fund_input.prev_tx_vout;
81 let tx_out = tx
82 .output
83 .get(vout as usize)
84 .ok_or(Error::InvalidParameters)?;
85 input_amount += tx_out.value;
86 inputs.push(TxInputInfo {
87 outpoint: OutPoint {
88 txid: tx.compute_txid(),
89 vout,
90 },
91 max_witness_len: 107,
92 redeem_script: fund_input.redeem_script.clone(),
93 serial_id: fund_input.input_serial_id,
94 });
95 }
96
97 Ok((inputs, input_amount))
98}
99
100pub(crate) fn get_contract_info_and_announcements(
101 contract_info: &SerContractInfo,
102) -> Result<Vec<ContractInfo>, Error> {
103 let mut contract_infos = Vec::new();
104 let (total_collateral, inner_contract_infos) = match contract_info {
105 SerContractInfo::SingleContractInfo(single) => {
106 (single.total_collateral, vec![single.contract_info.clone()])
107 }
108 SerContractInfo::DisjointContractInfo(disjoint) => {
109 (disjoint.total_collateral, disjoint.contract_infos.clone())
110 }
111 };
112
113 for contract_info in inner_contract_infos {
114 let (descriptor, oracle_announcements, threshold) = match contract_info.contract_descriptor
115 {
116 SerContractDescriptor::EnumeratedContractDescriptor(enumerated) => {
117 let outcome_payouts = enumerated
118 .payouts
119 .iter()
120 .map(|x| EnumerationPayout {
121 outcome: x.outcome.clone(),
122 payout: Payout {
123 offer: x.offer_payout,
124 accept: total_collateral - x.offer_payout,
125 },
126 })
127 .collect();
128 let descriptor = ContractDescriptor::Enum(EnumDescriptor { outcome_payouts });
129 let mut threshold = 1;
130 let announcements = match contract_info.oracle_info {
131 SerOracleInfo::Single(single) => vec![single.oracle_announcement],
132 SerOracleInfo::Multi(multi) => {
133 threshold = multi.threshold;
134 multi.oracle_announcements
135 }
136 };
137
138 if announcements
139 .iter()
140 .any(|x| match &x.oracle_event.event_descriptor {
141 EventDescriptor::EnumEvent(_) => false,
142 EventDescriptor::DigitDecompositionEvent(_) => true,
143 })
144 {
145 return Err(Error::InvalidParameters);
146 }
147
148 (descriptor, announcements, threshold)
149 }
150 SerContractDescriptor::NumericOutcomeContractDescriptor(numeric) => {
151 let threshold;
152 let mut difference_params: Option<DifferenceParams> = None;
153 let announcements = match contract_info.oracle_info {
154 SerOracleInfo::Single(single) => {
155 threshold = 1;
156 vec![single.oracle_announcement]
157 }
158 SerOracleInfo::Multi(multi) => {
159 threshold = multi.threshold;
160 if let Some(params) = multi.oracle_params {
161 difference_params = Some(DifferenceParams {
162 max_error_exp: params.max_error_exp as usize,
163 min_support_exp: params.min_fail_exp as usize,
164 maximize_coverage: params.maximize_coverage,
165 })
166 }
167 multi.oracle_announcements.clone()
168 }
169 };
170 if announcements.is_empty() {
171 return Err(Error::InvalidParameters);
172 }
173 let expected_base = if let EventDescriptor::DigitDecompositionEvent(d) =
174 &announcements[0].oracle_event.event_descriptor
175 {
176 d.base
177 } else {
178 return Err(Error::InvalidParameters);
179 };
180 let nb_digits = announcements
181 .iter()
182 .map(|x| match &x.oracle_event.event_descriptor {
183 EventDescriptor::DigitDecompositionEvent(d) => {
184 if d.base == expected_base {
185 Ok(d.nb_digits as usize)
186 } else {
187 Err(Error::InvalidParameters)
188 }
189 }
190 _ => Err(Error::InvalidParameters),
191 })
192 .collect::<Result<Vec<_>, _>>()?;
193 let descriptor = ContractDescriptor::Numerical(NumericalDescriptor {
194 payout_function: (&numeric.payout_function).into(),
195 rounding_intervals: (&numeric.rounding_intervals).into(),
196 difference_params,
197 oracle_numeric_infos: OracleNumericInfo {
198 base: expected_base as usize,
199 nb_digits,
200 },
201 });
202 (descriptor, announcements, threshold)
203 }
204 };
205 contract_infos.push(ContractInfo {
206 contract_descriptor: descriptor,
207 oracle_announcements,
208 threshold: threshold as usize,
209 });
210 }
211
212 Ok(contract_infos)
213}
214
215impl From<&OfferedContract> for SerContractInfo {
216 fn from(offered_contract: &OfferedContract) -> SerContractInfo {
217 let oracle_infos: Vec<SerOracleInfo> = offered_contract.into();
218 let mut contract_infos: Vec<ContractInfoInner> = offered_contract
219 .contract_info
220 .iter()
221 .zip(oracle_infos)
222 .map(|(c, o)| ContractInfoInner {
223 contract_descriptor: (&c.contract_descriptor).into(),
224 oracle_info: o,
225 })
226 .collect();
227 if contract_infos.len() == 1 {
228 SerContractInfo::SingleContractInfo(SingleContractInfo {
229 total_collateral: offered_contract.total_collateral,
230 contract_info: contract_infos.remove(0),
231 })
232 } else {
233 SerContractInfo::DisjointContractInfo(DisjointContractInfo {
234 total_collateral: offered_contract.total_collateral,
235 contract_infos,
236 })
237 }
238 }
239}
240
241impl From<&OfferedContract> for Vec<SerOracleInfo> {
242 fn from(offered_contract: &OfferedContract) -> Vec<SerOracleInfo> {
243 let mut infos = Vec::new();
244 for contract_info in &offered_contract.contract_info {
245 let announcements = &contract_info.oracle_announcements;
246 if announcements.len() == 1 {
247 infos.push(SerOracleInfo::Single(SingleOracleInfo {
248 oracle_announcement: announcements[0].clone(),
249 }));
250 } else {
251 if let ContractDescriptor::Numerical(n) = &contract_info.contract_descriptor {
252 if let Some(params) = &n.difference_params {
253 infos.push(SerOracleInfo::Multi(MultiOracleInfo {
254 threshold: contract_info.threshold as u16,
255 oracle_announcements: announcements.clone(),
256 oracle_params: Some(OracleParams {
257 max_error_exp: params.max_error_exp as u16,
258 min_fail_exp: params.min_support_exp as u16,
259 maximize_coverage: params.maximize_coverage,
260 }),
261 }));
262 continue;
263 }
264 }
265 infos.push(SerOracleInfo::Multi(MultiOracleInfo {
266 threshold: contract_info.threshold as u16,
267 oracle_announcements: announcements.clone(),
268 oracle_params: None,
269 }))
270 }
271 }
272
273 infos
274 }
275}
276
277impl From<&EnumDescriptor> for EnumeratedContractDescriptor {
278 fn from(enum_descriptor: &EnumDescriptor) -> EnumeratedContractDescriptor {
279 let payouts: Vec<ContractOutcome> = enum_descriptor
280 .outcome_payouts
281 .iter()
282 .map(|x| ContractOutcome {
283 outcome: x.outcome.clone(),
284 offer_payout: x.payout.offer,
285 })
286 .collect();
287 EnumeratedContractDescriptor { payouts }
288 }
289}
290
291impl From<&NumericalDescriptor> for NumericOutcomeContractDescriptor {
292 fn from(num_descriptor: &NumericalDescriptor) -> NumericOutcomeContractDescriptor {
293 NumericOutcomeContractDescriptor {
294 num_digits: *num_descriptor
295 .oracle_numeric_infos
296 .nb_digits
297 .iter()
298 .min()
299 .expect("to have at least a value") as u16,
300 payout_function: (&num_descriptor.payout_function).into(),
301 rounding_intervals: (&num_descriptor.rounding_intervals).into(),
302 }
303 }
304}
305
306impl From<&ContractDescriptor> for SerContractDescriptor {
307 fn from(descriptor: &ContractDescriptor) -> SerContractDescriptor {
308 match descriptor {
309 ContractDescriptor::Enum(e) => {
310 SerContractDescriptor::EnumeratedContractDescriptor(e.into())
311 }
312 ContractDescriptor::Numerical(n) => {
313 SerContractDescriptor::NumericOutcomeContractDescriptor(n.into())
314 }
315 }
316 }
317}
318
319impl From<&PayoutFunction> for SerPayoutFunction {
320 fn from(payout_function: &PayoutFunction) -> SerPayoutFunction {
321 SerPayoutFunction {
322 payout_function_pieces: payout_function
323 .payout_function_pieces
324 .iter()
325 .map(|x| {
326 let (left, piece) = match x {
327 PayoutFunctionPiece::PolynomialPayoutCurvePiece(p) => (
328 (&p.payout_points[0]).into(),
329 SerPayoutCurvePiece::PolynomialPayoutCurvePiece(
330 SerPolynomialPayoutCurvePiece {
331 payout_points: p
332 .payout_points
333 .iter()
334 .skip(1)
335 .take(p.payout_points.len() - 2)
336 .map(|x| x.into())
337 .collect(),
338 },
339 ),
340 ),
341 PayoutFunctionPiece::HyperbolaPayoutCurvePiece(h) => (
342 (&h.left_end_point).into(),
343 SerPayoutCurvePiece::HyperbolaPayoutCurvePiece(h.into()),
344 ),
345 };
346 SerPayoutFunctionPiece {
347 end_point: left,
348 payout_curve_piece: piece,
349 }
350 })
351 .collect(),
352 last_endpoint: {
353 let last_piece = payout_function.payout_function_pieces.last().unwrap();
354 match last_piece {
355 PayoutFunctionPiece::PolynomialPayoutCurvePiece(p) => {
356 p.payout_points.last().unwrap().into()
357 }
358 PayoutFunctionPiece::HyperbolaPayoutCurvePiece(h) => {
359 (&h.right_end_point).into()
360 }
361 }
362 },
363 }
364 }
365}
366
367impl From<&SerPayoutFunction> for PayoutFunction {
368 fn from(payout_function: &SerPayoutFunction) -> PayoutFunction {
369 PayoutFunction {
370 payout_function_pieces: payout_function
371 .payout_function_pieces
372 .iter()
373 .zip(
374 payout_function
375 .payout_function_pieces
376 .iter()
377 .skip(1)
378 .map(|x| &x.end_point)
379 .chain(vec![&payout_function.last_endpoint]),
380 )
381 .map(|(x, y)| from_ser_payout_function_piece(x, y))
382 .collect(),
383 }
384 }
385}
386
387fn from_ser_payout_function_piece(
388 piece: &SerPayoutFunctionPiece,
389 right_end_point: &SerPayoutPoint,
390) -> PayoutFunctionPiece {
391 match &piece.payout_curve_piece {
392 SerPayoutCurvePiece::PolynomialPayoutCurvePiece(p) => {
393 PayoutFunctionPiece::PolynomialPayoutCurvePiece(PolynomialPayoutCurvePiece {
394 payout_points: vec![(&piece.end_point).into()]
395 .into_iter()
396 .chain(p.payout_points.iter().map(|x| x.into()))
397 .chain(vec![(right_end_point).into()])
398 .collect(),
399 })
400 }
401 SerPayoutCurvePiece::HyperbolaPayoutCurvePiece(h) => {
402 PayoutFunctionPiece::HyperbolaPayoutCurvePiece(HyperbolaPayoutCurvePiece {
403 left_end_point: (&piece.end_point).into(),
404 right_end_point: right_end_point.into(),
405 use_positive_piece: h.use_positive_piece,
406 translate_outcome: h.translate_outcome,
407 translate_payout: h.translate_payout,
408 a: h.a,
409 b: h.b,
410 c: h.c,
411 d: h.d,
412 })
413 }
414 }
415}
416
417impl From<&RoundingIntervals> for SerRoundingIntervals {
418 fn from(rounding_intervals: &RoundingIntervals) -> SerRoundingIntervals {
419 let intervals = rounding_intervals
420 .intervals
421 .iter()
422 .map(|x| x.into())
423 .collect();
424 SerRoundingIntervals { intervals }
425 }
426}
427
428impl From<&SerRoundingIntervals> for RoundingIntervals {
429 fn from(rounding_intervals: &SerRoundingIntervals) -> RoundingIntervals {
430 let intervals = rounding_intervals
431 .intervals
432 .iter()
433 .map(|x| x.into())
434 .collect();
435 RoundingIntervals { intervals }
436 }
437}
438
439impl From<&RoundingInterval> for SerRoundingInterval {
440 fn from(rounding_interval: &RoundingInterval) -> SerRoundingInterval {
441 SerRoundingInterval {
442 begin_interval: rounding_interval.begin_interval,
443 rounding_mod: rounding_interval.rounding_mod,
444 }
445 }
446}
447
448impl From<&SerRoundingInterval> for RoundingInterval {
449 fn from(rounding_interval: &SerRoundingInterval) -> RoundingInterval {
450 RoundingInterval {
451 begin_interval: rounding_interval.begin_interval,
452 rounding_mod: rounding_interval.rounding_mod,
453 }
454 }
455}
456
457impl From<&PayoutPoint> for SerPayoutPoint {
458 fn from(payout_point: &PayoutPoint) -> SerPayoutPoint {
459 SerPayoutPoint {
460 event_outcome: payout_point.event_outcome,
461 outcome_payout: payout_point.outcome_payout,
462 extra_precision: payout_point.extra_precision,
463 }
464 }
465}
466
467impl From<&SerPayoutPoint> for PayoutPoint {
468 fn from(payout_point: &SerPayoutPoint) -> PayoutPoint {
469 PayoutPoint {
470 event_outcome: payout_point.event_outcome,
471 outcome_payout: payout_point.outcome_payout,
472 extra_precision: payout_point.extra_precision,
473 }
474 }
475}
476
477impl From<&HyperbolaPayoutCurvePiece> for SerHyperbolaPayoutCurvePiece {
478 fn from(piece: &HyperbolaPayoutCurvePiece) -> SerHyperbolaPayoutCurvePiece {
479 SerHyperbolaPayoutCurvePiece {
480 use_positive_piece: piece.use_positive_piece,
481 translate_outcome: piece.translate_outcome,
482 translate_payout: piece.translate_payout,
483 a: piece.a,
484 b: piece.b,
485 c: piece.c,
486 d: piece.d,
487 }
488 }
489}
490
491impl From<&PolynomialPayoutCurvePiece> for SerPolynomialPayoutCurvePiece {
492 fn from(piece: &PolynomialPayoutCurvePiece) -> SerPolynomialPayoutCurvePiece {
493 SerPolynomialPayoutCurvePiece {
494 payout_points: piece
495 .payout_points
496 .iter()
497 .skip(1)
498 .take(piece.payout_points.len() - 2)
499 .map(|x| x.into())
500 .collect(),
501 }
502 }
503}
504
505impl From<&SerPolynomialPayoutCurvePiece> for PolynomialPayoutCurvePiece {
506 fn from(piece: &SerPolynomialPayoutCurvePiece) -> PolynomialPayoutCurvePiece {
507 PolynomialPayoutCurvePiece {
508 payout_points: piece.payout_points.iter().map(|x| x.into()).collect(),
509 }
510 }
511}
512
513impl From<&DifferenceParams> for OracleParams {
514 fn from(input: &DifferenceParams) -> OracleParams {
515 OracleParams {
516 max_error_exp: input.max_error_exp as u16,
517 min_fail_exp: input.min_support_exp as u16,
518 maximize_coverage: input.maximize_coverage,
519 }
520 }
521}
522
523impl From<&OracleParams> for DifferenceParams {
524 fn from(input: &OracleParams) -> DifferenceParams {
525 DifferenceParams {
526 max_error_exp: input.max_error_exp as usize,
527 min_support_exp: input.min_fail_exp as usize,
528 maximize_coverage: input.maximize_coverage,
529 }
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536
537 #[test]
538 fn payout_function_round_trip() {
539 let payout_function = PayoutFunction {
540 payout_function_pieces: vec![
541 PayoutFunctionPiece::PolynomialPayoutCurvePiece(PolynomialPayoutCurvePiece {
542 payout_points: vec![
543 PayoutPoint {
544 event_outcome: 0,
545 outcome_payout: Amount::ZERO,
546 extra_precision: 0,
547 },
548 PayoutPoint {
549 event_outcome: 9,
550 outcome_payout: Amount::ZERO,
551 extra_precision: 0,
552 },
553 ],
554 }),
555 PayoutFunctionPiece::PolynomialPayoutCurvePiece(PolynomialPayoutCurvePiece {
556 payout_points: vec![
557 PayoutPoint {
558 event_outcome: 9,
559 outcome_payout: Amount::ZERO,
560 extra_precision: 0,
561 },
562 PayoutPoint {
563 event_outcome: 10,
564 outcome_payout: Amount::from_sat(10),
565 extra_precision: 0,
566 },
567 ],
568 }),
569 PayoutFunctionPiece::PolynomialPayoutCurvePiece(PolynomialPayoutCurvePiece {
570 payout_points: vec![
571 PayoutPoint {
572 event_outcome: 10,
573 outcome_payout: Amount::from_sat(10),
574 extra_precision: 0,
575 },
576 PayoutPoint {
577 event_outcome: 20,
578 outcome_payout: Amount::from_sat(10),
579 extra_precision: 0,
580 },
581 ],
582 }),
583 ],
584 };
585 let ser_payout_function: SerPayoutFunction = (&payout_function).into();
586 let res: PayoutFunction = (&ser_payout_function).into();
587 assert_eq!(payout_function, res);
588 }
589}