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