ddk_manager/contract/
contract_input.rsuse crate::error::Error;
use super::ContractDescriptor;
use secp256k1_zkp::XOnlyPublicKey;
#[cfg(feature = "use-serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct OracleInput {
pub public_keys: Vec<XOnlyPublicKey>,
pub event_id: String,
pub threshold: u16,
}
impl OracleInput {
pub fn validate(&self) -> Result<(), Error> {
if self.public_keys.is_empty() {
return Err(Error::InvalidParameters(
"OracleInput must have at least one public key.".to_string(),
));
}
if self.threshold > self.public_keys.len() as u16 {
return Err(Error::InvalidParameters(
"Threshold cannot be larger than number of oracles.".to_string(),
));
}
if self.threshold == 0 {
return Err(Error::InvalidParameters(
"Threshold cannot be zero.".to_string(),
));
}
Ok(())
}
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct ContractInputInfo {
pub contract_descriptor: ContractDescriptor,
pub oracles: OracleInput,
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "use-serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct ContractInput {
pub offer_collateral: u64,
pub accept_collateral: u64,
pub fee_rate: u64,
pub contract_infos: Vec<ContractInputInfo>,
}
impl ContractInput {
pub fn validate(&self) -> Result<(), Error> {
if self.contract_infos.is_empty() {
return Err(Error::InvalidParameters(
"Need at least one contract info".to_string(),
));
}
for contract_info in &self.contract_infos {
contract_info.oracles.validate()?;
}
ddk_dlc::util::validate_fee_rate(self.fee_rate)
.map_err(|_| Error::InvalidParameters("Fee rate too high.".to_string()))
}
}
#[cfg(test)]
mod tests {
use ddk_dlc::{EnumerationPayout, Payout};
use secp256k1_zkp::{Keypair, SecretKey, SECP256K1};
use crate::contract::enum_descriptor::EnumDescriptor;
use super::*;
fn get_base_input() -> ContractInput {
ContractInput {
offer_collateral: 1000000,
accept_collateral: 2000000,
fee_rate: 1234,
contract_infos: vec![ContractInputInfo {
contract_descriptor: ContractDescriptor::Enum(EnumDescriptor {
outcome_payouts: vec![
EnumerationPayout {
outcome: "A".to_string(),
payout: Payout {
offer: 3000000,
accept: 0,
},
},
EnumerationPayout {
outcome: "B".to_string(),
payout: Payout {
offer: 0,
accept: 3000000,
},
},
],
}),
oracles: OracleInput {
public_keys: vec![
XOnlyPublicKey::from_keypair(&Keypair::from_secret_key(
SECP256K1,
&SecretKey::from_slice(&secp256k1_zkp::constants::ONE).unwrap(),
))
.0,
],
event_id: "1234".to_string(),
threshold: 1,
},
}],
}
}
#[test]
fn valid_contract_input_is_valid() {
let input = get_base_input();
input.validate().expect("the contract input to be valid.");
}
#[test]
fn no_contract_info_contract_input_is_not_valid() {
let mut input = get_base_input();
input.contract_infos.clear();
input
.validate()
.expect_err("the contract input to be invalid.");
}
#[test]
fn invalid_fee_rate_contract_input_is_not_valid() {
let mut input = get_base_input();
input.fee_rate = 251 * 25;
input
.validate()
.expect_err("the contract input to be invalid.");
}
#[test]
fn no_public_keys_oracle_input_contract_input_is_not_valid() {
let mut input = get_base_input();
input.contract_infos[0].oracles.public_keys.clear();
input
.validate()
.expect_err("the contract input to be invalid.");
}
#[test]
fn invalid_oracle_info_threshold_oracle_input_contract_input_is_not_valid() {
let mut input = get_base_input();
input.contract_infos[0].oracles.threshold = 2;
input
.validate()
.expect_err("the contract input to be invalid.");
}
#[test]
fn invalid_oracle_info_threshold_zero() {
let mut input = get_base_input();
input.contract_infos[0].oracles.threshold = 0;
input
.validate()
.expect_err("the contract input to be invalid.");
}
}