diff --git a/src/contract/mod.rs b/src/contract/mod.rs index 961c1d6..96701d7 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -93,6 +93,59 @@ pub struct WinCondition { } impl ContractParameters { + /// Verifies the parameters are in standardized format, checking for + /// errors such as duplicate players or zero-value payouts. + pub fn validate(&self) -> Result<(), Error> { + let uniq_ticket_hashes: BTreeSet<&[u8; 32]> = self + .players + .iter() + .map(|player| &player.ticket_hash) + .collect(); + + // This would imply the players array contains duplicate ticket hashes. + if uniq_ticket_hashes.len() != self.players.len() { + return Err(Error); + } + + for (outcome, payout_map) in self.outcome_payouts.iter() { + // Check for unknown outcomes. + if let &Outcome::Attestation(outcome_index) = outcome { + if outcome_index >= self.event.outcome_messages.len() { + return Err(Error); + } + } + + // Check for empty payout map. + if payout_map.len() == 0 { + return Err(Error); + } + + // Check for zero payout weights. + for &weight in payout_map.values() { + if weight == 0 { + return Err(Error); + } + } + } + + // Must use a non-zero fee rate. + if self.fee_rate == FeeRate::ZERO { + return Err(Error); + } + + // Must use a non-zero locktime delta + if self.relative_locktime_block_delta == 0 { + return Err(Error); + } + + // Must be funded by some fixed non-zero amount. + if self.funding_value < Amount::ZERO { + return Err(Error); + } + + Ok(()) + } + /// Returns the transaction output which the funding transaction should pay to. /// /// Avoid overusing this method, as it recomputes the aggregated key every time diff --git a/src/lib.rs b/src/lib.rs index e33f807..a2c6e8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,10 +37,13 @@ pub struct TicketedDLC { impl TicketedDLC { /// Construct all ticketed DLC transactions and cache precomputed data for later signing. + /// Returns an error if the contract parameters are invalid. pub fn new( params: ContractParameters, funding_outpoint: OutPoint, ) -> Result { + params.validate()?; + let outcome_tx_build = contract::outcome::build_outcome_txs(¶ms, funding_outpoint)?; let split_tx_build = contract::split::build_split_txs(¶ms, &outcome_tx_build)?;