dlc_manager/contract/
contract_input.rs1use crate::error::Error;
4
5use super::ContractDescriptor;
6use bitcoin::Amount;
7use secp256k1_zkp::XOnlyPublicKey;
8#[cfg(feature = "use-serde")]
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone)]
13#[cfg_attr(
14 feature = "use-serde",
15 derive(Serialize, Deserialize),
16 serde(rename_all = "camelCase")
17)]
18pub struct OracleInput {
19 pub public_keys: Vec<XOnlyPublicKey>,
21 pub event_id: String,
24 pub threshold: u16,
27}
28
29impl OracleInput {
30 pub fn validate(&self) -> Result<(), Error> {
32 if self.public_keys.is_empty() {
33 return Err(Error::InvalidParameters(
34 "OracleInput must have at least one public key.".to_string(),
35 ));
36 }
37
38 if self.threshold > self.public_keys.len() as u16 {
39 return Err(Error::InvalidParameters(
40 "Threshold cannot be larger than number of oracles.".to_string(),
41 ));
42 }
43
44 if self.threshold == 0 {
45 return Err(Error::InvalidParameters(
46 "Threshold cannot be zero.".to_string(),
47 ));
48 }
49
50 Ok(())
51 }
52}
53
54#[derive(Debug, Clone)]
56#[cfg_attr(
57 feature = "use-serde",
58 derive(Serialize, Deserialize),
59 serde(rename_all = "camelCase")
60)]
61pub struct ContractInputInfo {
62 pub contract_descriptor: ContractDescriptor,
64 pub oracles: OracleInput,
66}
67
68#[derive(Debug, Clone)]
69#[cfg_attr(
70 feature = "use-serde",
71 derive(Serialize, Deserialize),
72 serde(rename_all = "camelCase")
73)]
74pub struct ContractInput {
76 pub offer_collateral: Amount,
78 pub accept_collateral: Amount,
80 pub fee_rate: u64,
82 pub contract_infos: Vec<ContractInputInfo>,
85}
86
87impl ContractInput {
88 pub fn validate(&self) -> Result<(), Error> {
90 if self.contract_infos.is_empty() {
91 return Err(Error::InvalidParameters(
92 "Need at least one contract info".to_string(),
93 ));
94 }
95
96 for contract_info in &self.contract_infos {
97 contract_info.oracles.validate()?;
98 }
99
100 dlc::util::validate_fee_rate(self.fee_rate)
101 .map_err(|_| Error::InvalidParameters("Fee rate too high.".to_string()))
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use dlc::{EnumerationPayout, Payout};
108 use secp256k1_zkp::{Keypair, SecretKey, SECP256K1};
109
110 use crate::contract::enum_descriptor::EnumDescriptor;
111
112 use super::*;
113
114 fn get_base_input() -> ContractInput {
115 ContractInput {
116 offer_collateral: Amount::from_sat(1000000),
117 accept_collateral: Amount::from_sat(2000000),
118 fee_rate: 1234,
119 contract_infos: vec![ContractInputInfo {
120 contract_descriptor: ContractDescriptor::Enum(EnumDescriptor {
121 outcome_payouts: vec![
122 EnumerationPayout {
123 outcome: "A".to_string(),
124 payout: Payout {
125 offer: Amount::from_sat(3000000),
126 accept: Amount::ZERO,
127 },
128 },
129 EnumerationPayout {
130 outcome: "B".to_string(),
131 payout: Payout {
132 offer: Amount::ZERO,
133 accept: Amount::from_sat(3000000),
134 },
135 },
136 ],
137 }),
138 oracles: OracleInput {
139 public_keys: vec![
140 XOnlyPublicKey::from_keypair(&Keypair::from_secret_key(
141 SECP256K1,
142 &SecretKey::from_slice(&secp256k1_zkp::constants::ONE).unwrap(),
143 ))
144 .0,
145 ],
146 event_id: "1234".to_string(),
147 threshold: 1,
148 },
149 }],
150 }
151 }
152
153 #[test]
154 fn valid_contract_input_is_valid() {
155 let input = get_base_input();
156 input.validate().expect("the contract input to be valid.");
157 }
158
159 #[test]
160 fn no_contract_info_contract_input_is_not_valid() {
161 let mut input = get_base_input();
162 input.contract_infos.clear();
163 input
164 .validate()
165 .expect_err("the contract input to be invalid.");
166 }
167
168 #[test]
169 fn invalid_fee_rate_contract_input_is_not_valid() {
170 let mut input = get_base_input();
171 input.fee_rate = 251 * 25;
172 input
173 .validate()
174 .expect_err("the contract input to be invalid.");
175 }
176
177 #[test]
178 fn no_public_keys_oracle_input_contract_input_is_not_valid() {
179 let mut input = get_base_input();
180 input.contract_infos[0].oracles.public_keys.clear();
181 input
182 .validate()
183 .expect_err("the contract input to be invalid.");
184 }
185
186 #[test]
187 fn invalid_oracle_info_threshold_oracle_input_contract_input_is_not_valid() {
188 let mut input = get_base_input();
189 input.contract_infos[0].oracles.threshold = 2;
190 input
191 .validate()
192 .expect_err("the contract input to be invalid.");
193 }
194
195 #[test]
196 fn invalid_oracle_info_threshold_zero() {
197 let mut input = get_base_input();
198 input.contract_infos[0].oracles.threshold = 0;
199 input
200 .validate()
201 .expect_err("the contract input to be invalid.");
202 }
203}