mirror of
https://github.com/conduition/dlctix.git
synced 2025-12-17 00:24:20 +01:00
feat: adding error struct (#2)
This commit is contained in:
@@ -16,7 +16,7 @@ where
|
||||
O: IntoIterator<Item = usize>,
|
||||
{
|
||||
let tx_weight = bitcoin::transaction::predict_weight(input_weights, output_spk_lens);
|
||||
let fee = fee_rate.fee_wu(tx_weight).ok_or(Error)?;
|
||||
let fee = fee_rate.fee_wu(tx_weight).ok_or(Error::WeightOverflow)?;
|
||||
Ok(fee)
|
||||
}
|
||||
|
||||
@@ -29,11 +29,13 @@ pub(crate) fn fee_subtract_safe(
|
||||
dust_threshold: Amount,
|
||||
) -> Result<Amount, Error> {
|
||||
if fee >= available_coins {
|
||||
return Err(Error);
|
||||
return Err(Error::InsufficientFunds);
|
||||
}
|
||||
let after_fee = available_coins.checked_sub(fee).ok_or(Error)?;
|
||||
let after_fee = available_coins
|
||||
.checked_sub(fee)
|
||||
.ok_or(Error::InvalidFeeAmount)?;
|
||||
if after_fee <= dust_threshold {
|
||||
return Err(Error);
|
||||
return Err(Error::DustAmount);
|
||||
}
|
||||
Ok(after_fee)
|
||||
}
|
||||
|
||||
@@ -125,46 +125,46 @@ impl ContractParameters {
|
||||
|
||||
// This would imply the players array contains duplicate ticket hashes.
|
||||
if uniq_ticket_hashes.len() != self.players.len() {
|
||||
return Err(Error);
|
||||
return Err(Error::DuplicateTicketHash);
|
||||
}
|
||||
|
||||
for (outcome, payout_map) in self.outcome_payouts.iter() {
|
||||
// Check for unknown outcomes.
|
||||
if !self.event.is_valid_outcome(outcome) {
|
||||
return Err(Error);
|
||||
return Err(Error::UnknownOutcome);
|
||||
}
|
||||
|
||||
// Check for empty payout map.
|
||||
if payout_map.len() == 0 {
|
||||
return Err(Error);
|
||||
return Err(Error::EmptyPayoutMap);
|
||||
}
|
||||
|
||||
for (&player_index, &weight) in payout_map.iter() {
|
||||
// Check for zero payout weights.
|
||||
if weight == 0 {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidPayoutWeight);
|
||||
}
|
||||
|
||||
// Check for out-of-bounds player indexes.
|
||||
if player_index >= self.players.len() {
|
||||
return Err(Error);
|
||||
return Err(Error::OutOfBoundsPlayerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must use a non-zero fee rate.
|
||||
if self.fee_rate == FeeRate::ZERO {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidFeeAmount);
|
||||
}
|
||||
|
||||
// Must use a non-zero locktime delta
|
||||
if self.relative_locktime_block_delta == 0 {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidLocktime);
|
||||
}
|
||||
|
||||
// Must be funded by some fixed non-zero amount.
|
||||
if self.funding_value < Amount::ZERO {
|
||||
return Err(Error);
|
||||
return Err(Error::InsufficientFunds);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -78,7 +78,9 @@ pub(crate) fn build_outcome_txs(
|
||||
};
|
||||
|
||||
let lock_time = match outcome {
|
||||
Outcome::Expiry => LockTime::from_consensus(params.event.expiry.ok_or(Error)?),
|
||||
Outcome::Expiry => {
|
||||
LockTime::from_consensus(params.event.expiry.ok_or(Error::InvalidLocktime)?)
|
||||
}
|
||||
Outcome::Attestation(_) => LockTime::ZERO, // Normal outcome transaction
|
||||
};
|
||||
|
||||
@@ -120,13 +122,17 @@ pub(crate) fn partial_sign_outcome_txs(
|
||||
funding_spend_info
|
||||
.key_agg_ctx()
|
||||
.pubkey_index(seckey.base_point_mul())
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
|
||||
let mut outcome_partial_sigs = BTreeMap::<Outcome, PartialSignature>::new();
|
||||
|
||||
for (&outcome, outcome_tx) in outcome_txs {
|
||||
let aggnonce = aggnonces.get(&outcome).ok_or(Error)?; // must provide all aggnonces
|
||||
let secnonce = secnonces.remove(&outcome).ok_or(Error)?; // must provide all secnonces
|
||||
let aggnonce = aggnonces
|
||||
.get(&outcome)
|
||||
.ok_or(Error::MissingNonce(String::from("aggnonce for outcome")))?; // must provide all aggnonces
|
||||
let secnonce = secnonces
|
||||
.remove(&outcome)
|
||||
.ok_or(Error::MissingNonce(String::from("secnonce for outcome")))?; // must provide all secnonces
|
||||
|
||||
// Hash the outcome TX.
|
||||
let sighash = funding_spend_info.sighash_tx_outcome(outcome_tx)?;
|
||||
@@ -138,7 +144,7 @@ pub(crate) fn partial_sign_outcome_txs(
|
||||
.event
|
||||
.locking_points
|
||||
.get(outcome_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// sign under an attestation lock point
|
||||
musig2::adaptor::sign_partial(
|
||||
@@ -178,9 +184,17 @@ pub(crate) fn verify_outcome_tx_partial_signatures(
|
||||
let funding_spend_info = &outcome_build_out.funding_spend_info;
|
||||
|
||||
for (&outcome, outcome_tx) in outcome_txs {
|
||||
let aggnonce = aggnonces.get(&outcome).ok_or(Error)?; // must provide all aggnonces
|
||||
let pubnonce = pubnonces.get(&outcome).ok_or(Error)?; // must provide all pubnonces
|
||||
let &partial_sig = partial_signatures.get(&outcome).ok_or(Error)?; // must provide all sigs
|
||||
let aggnonce = aggnonces
|
||||
.get(&outcome)
|
||||
.ok_or(Error::MissingNonce(String::from("aggnonce for outcome")))?; // must provide all aggnonces
|
||||
let pubnonce = pubnonces
|
||||
.get(&outcome)
|
||||
.ok_or(Error::MissingNonce(String::from("pubnonce for outcome")))?; // must provide all pubnonces
|
||||
let &partial_sig = partial_signatures
|
||||
.get(&outcome)
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"partial_signatures for outcome",
|
||||
)))?; // must provide all sigs
|
||||
|
||||
// Hash the outcome TX.
|
||||
let sighash = funding_spend_info.sighash_tx_outcome(outcome_tx)?;
|
||||
@@ -192,7 +206,7 @@ pub(crate) fn verify_outcome_tx_partial_signatures(
|
||||
.event
|
||||
.locking_points
|
||||
.get(outcome_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
musig2::adaptor::verify_partial(
|
||||
funding_spend_info.key_agg_ctx(),
|
||||
@@ -263,10 +277,17 @@ where
|
||||
|
||||
for (&outcome, outcome_tx) in outcome_txs {
|
||||
// must provide a set of sigs for each TX
|
||||
let partial_sigs = partial_signature_groups.remove(&outcome).ok_or(Error)?;
|
||||
let partial_sigs =
|
||||
partial_signature_groups
|
||||
.remove(&outcome)
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"outcome from partial signature groups",
|
||||
)))?;
|
||||
|
||||
// must provide all aggnonces
|
||||
let aggnonce = aggnonces.get(&outcome).ok_or(Error)?;
|
||||
let aggnonce = aggnonces
|
||||
.get(&outcome)
|
||||
.ok_or(Error::MissingNonce(String::from("aggnonces for outcome")))?;
|
||||
|
||||
// Hash the outcome TX.
|
||||
let sighash = funding_spend_info.sighash_tx_outcome(outcome_tx)?;
|
||||
@@ -277,7 +298,7 @@ where
|
||||
.event
|
||||
.locking_points
|
||||
.get(outcome_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let adaptor_sig = musig2::adaptor::aggregate_partial_signatures(
|
||||
funding_spend_info.key_agg_ctx(),
|
||||
@@ -325,7 +346,7 @@ pub(crate) fn verify_outcome_tx_aggregated_signatures(
|
||||
// win something.
|
||||
let relevant_outcomes: BTreeSet<Outcome> = params
|
||||
.win_conditions_claimable_by_pubkey(our_pubkey)
|
||||
.ok_or(Error)?
|
||||
.ok_or(Error::InvalidKey)?
|
||||
.into_iter()
|
||||
.map(|win_cond| win_cond.outcome)
|
||||
.collect();
|
||||
@@ -334,7 +355,10 @@ pub(crate) fn verify_outcome_tx_aggregated_signatures(
|
||||
let batch: Vec<BatchVerificationRow> = relevant_outcomes
|
||||
.into_iter()
|
||||
.map(|outcome| {
|
||||
let outcome_tx = outcome_build_out.outcome_txs.get(&outcome).ok_or(Error)?;
|
||||
let outcome_tx = outcome_build_out
|
||||
.outcome_txs
|
||||
.get(&outcome)
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let sighash = outcome_build_out
|
||||
.funding_spend_info
|
||||
@@ -347,9 +371,13 @@ pub(crate) fn verify_outcome_tx_aggregated_signatures(
|
||||
.event
|
||||
.locking_points
|
||||
.get(outcome_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let &signature = outcome_tx_signatures.get(&outcome_index).ok_or(Error)?;
|
||||
let &signature = outcome_tx_signatures.get(&outcome_index).ok_or(
|
||||
Error::MissingSignature(String::from(
|
||||
"outcome index outcome tx signatures",
|
||||
)),
|
||||
)?;
|
||||
BatchVerificationRow::from_adaptor_signature(
|
||||
joint_pubkey,
|
||||
sighash,
|
||||
@@ -360,7 +388,9 @@ pub(crate) fn verify_outcome_tx_aggregated_signatures(
|
||||
|
||||
// One signature for the optional expiry transaction.
|
||||
Outcome::Expiry => {
|
||||
let signature = expiry_tx_signature.ok_or(Error)?.lift_nonce()?;
|
||||
let signature = expiry_tx_signature
|
||||
.ok_or(Error::MissingSignature(String::from("expiry tx signature")))?
|
||||
.lift_nonce()?;
|
||||
BatchVerificationRow::from_signature(joint_pubkey, sighash, signature)
|
||||
}
|
||||
};
|
||||
@@ -383,7 +413,10 @@ pub(crate) fn outcome_tx_prevout<'x>(
|
||||
outcome: &Outcome,
|
||||
block_delay: u16,
|
||||
) -> Result<(TxIn, &'x TxOut), Error> {
|
||||
let outcome_tx = outcome_build_out.outcome_txs().get(outcome).ok_or(Error)?;
|
||||
let outcome_tx = outcome_build_out
|
||||
.outcome_txs()
|
||||
.get(outcome)
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let outcome_input = TxIn {
|
||||
previous_output: OutPoint {
|
||||
@@ -394,7 +427,10 @@ pub(crate) fn outcome_tx_prevout<'x>(
|
||||
..TxIn::default()
|
||||
};
|
||||
|
||||
let prevout = outcome_tx.output.get(0).ok_or(Error)?;
|
||||
let prevout = outcome_tx
|
||||
.output
|
||||
.get(0)
|
||||
.ok_or(Error::InvalidInput("missing outcome tx output"))?;
|
||||
|
||||
Ok((outcome_input, prevout))
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(crate) fn build_split_txs(
|
||||
let outcome_spend_info = &outcome_build_output
|
||||
.outcome_spend_infos()
|
||||
.get(&outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// Fee estimation
|
||||
let input_weight = outcome_spend_info.input_weight_for_split_tx();
|
||||
@@ -72,7 +72,10 @@ pub(crate) fn build_split_txs(
|
||||
// payout_values is a btree, so outputs are automatically sorted by player.
|
||||
let mut split_tx_outputs = Vec::with_capacity(payout_map.len());
|
||||
for (player_index, payout_value) in payout_values {
|
||||
let player = params.players.get(player_index).ok_or(Error)?;
|
||||
let player = params
|
||||
.players
|
||||
.get(player_index)
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)?;
|
||||
let split_spend_info = SplitSpendInfo::new(
|
||||
player,
|
||||
¶ms.market_maker,
|
||||
@@ -131,7 +134,7 @@ pub(crate) fn partial_sign_split_txs(
|
||||
|
||||
let win_conditions_to_sign = params
|
||||
.win_conditions_controlled_by_pubkey(pubkey)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
if win_conditions_to_sign.is_empty() {
|
||||
return Ok(partial_signatures);
|
||||
}
|
||||
@@ -140,15 +143,23 @@ pub(crate) fn partial_sign_split_txs(
|
||||
let split_tx = split_build_out
|
||||
.split_txs()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let aggnonce = aggnonces.get(&win_cond).ok_or(Error)?; // must provide all aggnonces
|
||||
let secnonce = secnonces.remove(&win_cond).ok_or(Error)?; // must provide all secnonces
|
||||
let aggnonce = aggnonces
|
||||
.get(&win_cond)
|
||||
.ok_or(Error::MissingNonce(String::from(
|
||||
"aggnonce for win condition",
|
||||
)))?; // must provide all aggnonces
|
||||
let secnonce = secnonces
|
||||
.remove(&win_cond)
|
||||
.ok_or(Error::MissingNonce(String::from(
|
||||
"secnonces for win condition",
|
||||
)))?; // must provide all secnonces
|
||||
|
||||
let outcome_spend_info = outcome_build_out
|
||||
.outcome_spend_infos()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// Hash the split TX.
|
||||
let sighash = outcome_spend_info.sighash_tx_split(split_tx, &win_cond.player_index)?;
|
||||
@@ -187,22 +198,36 @@ pub(crate) fn verify_split_tx_partial_signatures(
|
||||
) -> Result<(), Error> {
|
||||
let win_conditions_to_sign = params
|
||||
.win_conditions_controlled_by_pubkey(signer_pubkey)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
|
||||
for win_cond in win_conditions_to_sign {
|
||||
let split_tx = split_build_out
|
||||
.split_txs()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let aggnonce = aggnonces.get(&win_cond).ok_or(Error)?; // must provide all aggnonces
|
||||
let pubnonce = pubnonces.get(&win_cond).ok_or(Error)?; // must provide all pubnonces
|
||||
let partial_sig = partial_signatures.get(&win_cond).copied().ok_or(Error)?; // must provide all sigs
|
||||
let aggnonce = aggnonces
|
||||
.get(&win_cond)
|
||||
.ok_or(Error::MissingNonce(String::from(
|
||||
"aggnonces for win condition",
|
||||
)))?; // must provide all aggnonces
|
||||
let pubnonce = pubnonces
|
||||
.get(&win_cond)
|
||||
.ok_or(Error::MissingNonce(String::from(
|
||||
"pubnonce for win condition",
|
||||
)))?; // must provide all pubnonces
|
||||
let partial_sig =
|
||||
partial_signatures
|
||||
.get(&win_cond)
|
||||
.copied()
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"partial signatures for win condition",
|
||||
)))?;
|
||||
|
||||
let outcome_spend_info = outcome_build_out
|
||||
.outcome_spend_infos()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// Hash the split TX.
|
||||
let sighash = outcome_spend_info.sighash_tx_split(split_tx, &win_cond.player_index)?;
|
||||
@@ -243,18 +268,25 @@ where
|
||||
let split_tx = split_build_out
|
||||
.split_txs()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let relevant_partial_sigs = partial_signatures_by_win_cond
|
||||
.remove(&win_cond)
|
||||
.ok_or(Error)?;
|
||||
let relevant_partial_sigs =
|
||||
partial_signatures_by_win_cond
|
||||
.remove(&win_cond)
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"partial signatures by win cond for win condition",
|
||||
)))?;
|
||||
|
||||
let aggnonce = aggnonces.get(&win_cond).ok_or(Error)?;
|
||||
let aggnonce = aggnonces
|
||||
.get(&win_cond)
|
||||
.ok_or(Error::MissingNonce(String::from(
|
||||
"aggnonces for win condition",
|
||||
)))?;
|
||||
|
||||
let outcome_spend_info = outcome_build_out
|
||||
.outcome_spend_infos()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// Hash the split TX.
|
||||
let sighash = outcome_spend_info.sighash_tx_split(split_tx, &win_cond.player_index)?;
|
||||
@@ -283,7 +315,7 @@ pub(crate) fn verify_split_tx_aggregated_signatures(
|
||||
// win something.
|
||||
let relevant_win_conditions: BTreeSet<WinCondition> = params
|
||||
.win_conditions_claimable_by_pubkey(our_pubkey)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
|
||||
let batch: Vec<BatchVerificationRow> = relevant_win_conditions
|
||||
.into_iter()
|
||||
@@ -291,14 +323,18 @@ pub(crate) fn verify_split_tx_aggregated_signatures(
|
||||
let split_tx = split_build_out
|
||||
.split_txs()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let signature = split_tx_signatures.get(&win_cond).ok_or(Error)?;
|
||||
let signature = split_tx_signatures
|
||||
.get(&win_cond)
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"split tx signature for win condition",
|
||||
)))?;
|
||||
|
||||
let outcome_spend_info = outcome_build_out
|
||||
.outcome_spend_infos()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// Expect an untweaked signature by the group, so that the signature
|
||||
// can be used to trigger one of the players' split tapscripts.
|
||||
@@ -335,13 +371,16 @@ pub(crate) fn split_tx_prevout<'x>(
|
||||
let split_tx = split_build_out
|
||||
.split_txs()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let payout_map = params.outcome_payouts.get(&win_cond.outcome).ok_or(Error)?;
|
||||
let payout_map = params
|
||||
.outcome_payouts
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
let split_tx_output_index = payout_map
|
||||
.keys()
|
||||
.position(|&player_index| player_index == win_cond.player_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)?;
|
||||
|
||||
let input = TxIn {
|
||||
previous_output: OutPoint {
|
||||
@@ -352,7 +391,10 @@ pub(crate) fn split_tx_prevout<'x>(
|
||||
..TxIn::default()
|
||||
};
|
||||
|
||||
let prevout = split_tx.output.get(split_tx_output_index).ok_or(Error)?;
|
||||
let prevout = split_tx
|
||||
.output
|
||||
.get(split_tx_output_index)
|
||||
.ok_or(Error::InvalidInput("missing split tx output"))?;
|
||||
|
||||
Ok((input, prevout))
|
||||
}
|
||||
|
||||
149
src/errors.rs
149
src/errors.rs
@@ -1,65 +1,164 @@
|
||||
/// TODO actual error types.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Error;
|
||||
use std::fmt;
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.write_str("generic error")
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
// Fee calculation errors
|
||||
InsufficientFunds,
|
||||
InvalidFeeAmount,
|
||||
DustAmount,
|
||||
WeightOverflow,
|
||||
|
||||
// Contract validation errors
|
||||
DuplicateTicketHash,
|
||||
InvalidPayoutWeight,
|
||||
OutOfBoundsPlayerIndex,
|
||||
InvalidFeeRate,
|
||||
InvalidLocktime,
|
||||
InvalidFundingValue,
|
||||
UnknownOutcome,
|
||||
EmptyPayoutMap,
|
||||
|
||||
// Transaction signing errors
|
||||
InvalidSignature,
|
||||
MissingSignature(String),
|
||||
MissingNonce(String),
|
||||
InvalidKey,
|
||||
|
||||
// Dependencies errors
|
||||
KeyAgg(musig2::errors::KeyAggError),
|
||||
Tweak(musig2::errors::TweakError),
|
||||
Verify(musig2::errors::VerifyError),
|
||||
Signing(musig2::errors::SigningError),
|
||||
InvalidPoint(secp::errors::InvalidPointBytes),
|
||||
InvalidSecretKeys(musig2::errors::InvalidSecretKeysError),
|
||||
TaprootBuilder(bitcoin::taproot::TaprootBuilderError),
|
||||
IncompleteBuilder(bitcoin::taproot::IncompleteBuilderError),
|
||||
TaprootSighash(bitcoin::sighash::TaprootError),
|
||||
ParseOutcomeIndex(std::num::ParseIntError),
|
||||
|
||||
// General errors
|
||||
InvalidInput(&'static str),
|
||||
Conversion(&'static str),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
// Implement source() to expose inner errors
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
use Error::*;
|
||||
match self {
|
||||
KeyAgg(e) => Some(e),
|
||||
Tweak(e) => Some(e),
|
||||
Verify(e) => Some(e),
|
||||
Signing(e) => Some(e),
|
||||
InvalidPoint(e) => Some(e),
|
||||
InvalidSecretKeys(e) => Some(e),
|
||||
TaprootBuilder(e) => Some(e),
|
||||
IncompleteBuilder(e) => Some(e),
|
||||
TaprootSighash(e) => Some(e),
|
||||
ParseOutcomeIndex(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Error::*;
|
||||
|
||||
match self {
|
||||
InsufficientFunds => write!(f, "insufficient funds available"),
|
||||
InvalidFeeAmount => write!(f, "invalid fee amount"),
|
||||
DustAmount => write!(f, "output amount would be below dust threshold"),
|
||||
WeightOverflow => write!(f, "transaction weight calculation overflow"),
|
||||
|
||||
DuplicateTicketHash => write!(f, "duplicate ticket hash found"),
|
||||
InvalidPayoutWeight => write!(f, "invalid payout weight"),
|
||||
OutOfBoundsPlayerIndex => write!(f, "player index out of bounds"),
|
||||
InvalidFeeRate => write!(f, "invalid fee rate"),
|
||||
InvalidLocktime => write!(f, "invalid relative locktime"),
|
||||
InvalidFundingValue => write!(f, "invalid funding value"),
|
||||
UnknownOutcome => write!(f, "unknown outcome"),
|
||||
EmptyPayoutMap => write!(f, "empty payout map"),
|
||||
|
||||
InvalidSignature => write!(f, "invalid signature"),
|
||||
MissingSignature(msg) => write!(f, "missing required signature: {}", msg),
|
||||
MissingNonce(msg) => write!(f, "missing required nonce: {}", msg),
|
||||
InvalidKey => write!(f, "invalid key"),
|
||||
|
||||
InvalidInput(msg) => write!(f, "invalid input: {}", msg),
|
||||
Conversion(msg) => write!(f, "conversion error: {}", msg),
|
||||
|
||||
KeyAgg(e) => write!(f, "key aggregation error: {}", e),
|
||||
Tweak(e) => write!(f, "key tweaking error: {}", e),
|
||||
Verify(e) => write!(f, "signature verification error: {}", e),
|
||||
Signing(e) => write!(f, "signing error: {}", e),
|
||||
InvalidPoint(e) => write!(f, "invalid point error: {}", e),
|
||||
InvalidSecretKeys(e) => write!(f, "invalid secret keys: {}", e),
|
||||
TaprootBuilder(e) => write!(f, "taproot builder error: {}", e),
|
||||
IncompleteBuilder(e) => write!(f, "incomplete taproot builder: {}", e),
|
||||
TaprootSighash(e) => write!(f, "taproot sighash error: {}", e),
|
||||
ParseOutcomeIndex(e) => write!(f, "invalid outcome index: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement From for common error types
|
||||
impl From<musig2::errors::KeyAggError> for Error {
|
||||
fn from(_: musig2::errors::KeyAggError) -> Self {
|
||||
Error
|
||||
fn from(e: musig2::errors::KeyAggError) -> Self {
|
||||
Error::KeyAgg(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<musig2::errors::TweakError> for Error {
|
||||
fn from(_: musig2::errors::TweakError) -> Self {
|
||||
Error
|
||||
fn from(e: musig2::errors::TweakError) -> Self {
|
||||
Error::Tweak(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<musig2::errors::VerifyError> for Error {
|
||||
fn from(_: musig2::errors::VerifyError) -> Self {
|
||||
Error
|
||||
fn from(e: musig2::errors::VerifyError) -> Self {
|
||||
Error::Verify(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<musig2::errors::SigningError> for Error {
|
||||
fn from(_: musig2::errors::SigningError) -> Self {
|
||||
Error
|
||||
fn from(e: musig2::errors::SigningError) -> Self {
|
||||
Error::Signing(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<secp::errors::InvalidPointBytes> for Error {
|
||||
fn from(_: secp::errors::InvalidPointBytes) -> Self {
|
||||
Error
|
||||
fn from(e: secp::errors::InvalidPointBytes) -> Self {
|
||||
Error::InvalidPoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<musig2::errors::InvalidSecretKeysError> for Error {
|
||||
fn from(_: musig2::errors::InvalidSecretKeysError) -> Self {
|
||||
Error
|
||||
fn from(e: musig2::errors::InvalidSecretKeysError) -> Self {
|
||||
Error::InvalidSecretKeys(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bitcoin::taproot::TaprootBuilderError> for Error {
|
||||
fn from(_: bitcoin::taproot::TaprootBuilderError) -> Self {
|
||||
Error
|
||||
fn from(e: bitcoin::taproot::TaprootBuilderError) -> Self {
|
||||
Error::TaprootBuilder(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bitcoin::taproot::IncompleteBuilderError> for Error {
|
||||
fn from(_: bitcoin::taproot::IncompleteBuilderError) -> Self {
|
||||
Error
|
||||
fn from(e: bitcoin::taproot::IncompleteBuilderError) -> Self {
|
||||
Error::IncompleteBuilder(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bitcoin::sighash::TaprootError> for Error {
|
||||
fn from(_: bitcoin::sighash::TaprootError) -> Self {
|
||||
Error
|
||||
fn from(e: bitcoin::sighash::TaprootError) -> Self {
|
||||
Error::TaprootSighash(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::ParseIntError> for Error {
|
||||
fn from(e: std::num::ParseIntError) -> Self {
|
||||
Error::ParseOutcomeIndex(e)
|
||||
}
|
||||
}
|
||||
|
||||
139
src/lib.rs
139
src/lib.rs
@@ -202,7 +202,10 @@ impl SigningSession<NonceSharingRound> {
|
||||
let signing_key = signing_key.into();
|
||||
let our_public_key = signing_key.base_point_mul();
|
||||
|
||||
let base_sigmap = dlc.params.sigmap_for_pubkey(our_public_key).ok_or(Error)?;
|
||||
let base_sigmap = dlc
|
||||
.params
|
||||
.sigmap_for_pubkey(our_public_key)
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
|
||||
let our_secret_nonces =
|
||||
base_sigmap.map_values(|_| SecNonce::build(&mut rng).with_seckey(signing_key).build());
|
||||
@@ -344,11 +347,14 @@ impl SigningSession<CoordinatorPartialSignatureSharingRound> {
|
||||
signer_pubkey: Point,
|
||||
partial_signatures: &SigMap<PartialSignature>,
|
||||
) -> Result<(), Error> {
|
||||
let signer_nonces = self
|
||||
.state
|
||||
.received_nonces
|
||||
.get(&signer_pubkey)
|
||||
.ok_or(Error)?;
|
||||
let signer_nonces =
|
||||
self.state
|
||||
.received_nonces
|
||||
.get(&signer_pubkey)
|
||||
.ok_or(Error::MissingNonce(format!(
|
||||
"no nonces received from signer with pubkey {:?}",
|
||||
signer_pubkey
|
||||
)))?;
|
||||
|
||||
contract::outcome::verify_outcome_tx_partial_signatures(
|
||||
&self.dlc.params,
|
||||
@@ -528,21 +534,29 @@ fn validate_sigmaps_completeness<T>(
|
||||
) -> Result<(), Error> {
|
||||
// Must receive signatures/nonces from all players and the market maker.
|
||||
if !received_maps.contains_key(¶ms.market_maker.pubkey) {
|
||||
return Err(Error);
|
||||
return Err(Error::MissingSignature(format!(
|
||||
"market makert pubkey: {}",
|
||||
params.market_maker.pubkey
|
||||
)));
|
||||
}
|
||||
for player in params.players.iter() {
|
||||
if !received_maps.contains_key(&player.pubkey) {
|
||||
return Err(Error);
|
||||
return Err(Error::MissingSignature(format!(
|
||||
"player pubkey: {}",
|
||||
player.pubkey
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
for (&signer_pubkey, sigmap) in received_maps.iter() {
|
||||
// The expected sigmap each signer must provide nonces/signatures for.
|
||||
let base_sigmap = params.sigmap_for_pubkey(signer_pubkey).ok_or(Error)?;
|
||||
let base_sigmap = params
|
||||
.sigmap_for_pubkey(signer_pubkey)
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
|
||||
// All signers' sigmaps must match exactly.
|
||||
if !sigmap.is_mirror(&base_sigmap) {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidSignature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,25 +687,30 @@ impl SignedContract {
|
||||
.event
|
||||
.locking_points
|
||||
.get(outcome_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
// Invalid attestation.
|
||||
if &attestation.base_point_mul() != locking_point {
|
||||
return Err(Error)?;
|
||||
return Err(Error::InvalidSignature)?;
|
||||
}
|
||||
|
||||
let mut outcome_tx = self
|
||||
.unsigned_outcome_tx(outcome_index)
|
||||
.ok_or(Error)?
|
||||
.ok_or(Error::UnknownOutcome)?
|
||||
.clone();
|
||||
|
||||
let adaptor_signature = self
|
||||
.signatures
|
||||
.outcome_tx_signatures
|
||||
.get(&outcome_index)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::MissingSignature(format!(
|
||||
"adaptor signature for outcome index {}",
|
||||
outcome_index
|
||||
)))?;
|
||||
|
||||
let compact_sig: CompactSignature = adaptor_signature.adapt(attestation).ok_or(Error)?;
|
||||
let compact_sig: CompactSignature = adaptor_signature
|
||||
.adapt(attestation)
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
|
||||
outcome_tx.input[0].witness.push(compact_sig.serialize());
|
||||
Ok(outcome_tx)
|
||||
@@ -728,26 +747,28 @@ impl SignedContract {
|
||||
.players
|
||||
.get(win_cond.player_index)
|
||||
.cloned()
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)?;
|
||||
|
||||
// Verify the preimage will unlock this specific player's split TX
|
||||
// condition.
|
||||
if sha256(&ticket_preimage) != winner.ticket_hash {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidInput("ticket preimage does not match hash"));
|
||||
}
|
||||
|
||||
let signature = self
|
||||
.signatures
|
||||
.split_tx_signatures
|
||||
.get(win_cond)
|
||||
.ok_or(Error)?;
|
||||
let signature =
|
||||
self.signatures
|
||||
.split_tx_signatures
|
||||
.get(win_cond)
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"split tx signature for win condition",
|
||||
)))?;
|
||||
|
||||
let outcome_spend_info = self
|
||||
.dlc
|
||||
.outcome_tx_build
|
||||
.outcome_spend_infos()
|
||||
.get(&win_cond.outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = outcome_spend_info.witness_tx_split(
|
||||
signature,
|
||||
@@ -757,7 +778,7 @@ impl SignedContract {
|
||||
|
||||
let mut split_tx = self
|
||||
.unsigned_split_tx(&win_cond.outcome)
|
||||
.ok_or(Error)?
|
||||
.ok_or(Error::UnknownOutcome)?
|
||||
.clone();
|
||||
|
||||
split_tx.input[0].witness = witness;
|
||||
@@ -945,7 +966,7 @@ impl SignedContract {
|
||||
.outcome_tx_build
|
||||
.outcome_spend_infos()
|
||||
.get(outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = outcome_spend_info.witness_tx_reclaim(
|
||||
reclaim_tx,
|
||||
@@ -978,7 +999,7 @@ impl SignedContract {
|
||||
) -> Result<(), Error> {
|
||||
let market_maker_secret_key = market_maker_secret_key.into();
|
||||
if market_maker_secret_key.base_point_mul() != self.dlc.params.market_maker.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
@@ -1025,14 +1046,18 @@ impl SignedContract {
|
||||
) -> Result<(), Error> {
|
||||
let market_maker_secret_key = market_maker_secret_key.into();
|
||||
if market_maker_secret_key.base_point_mul() != self.dlc.params.market_maker.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
let (mut expected_input, expected_prevout) = self.funding_close_tx_input_and_prevout();
|
||||
|
||||
// The caller can use whatever sequence they want.
|
||||
expected_input.sequence = close_tx.input.get(input_index).ok_or(Error)?.sequence;
|
||||
expected_input.sequence = close_tx
|
||||
.input
|
||||
.get(input_index)
|
||||
.ok_or(Error::InvalidInput("transaction input index out of bounds"))?
|
||||
.sequence;
|
||||
|
||||
check_input_matches_expected(
|
||||
close_tx,
|
||||
@@ -1081,7 +1106,7 @@ impl SignedContract {
|
||||
) -> Result<(), Error> {
|
||||
let market_maker_secret_key = market_maker_secret_key.into();
|
||||
if market_maker_secret_key.base_point_mul() != self.dlc.params.market_maker.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
@@ -1089,7 +1114,11 @@ impl SignedContract {
|
||||
self.outcome_close_tx_input_and_prevout(outcome)?;
|
||||
|
||||
// The caller can use whatever sequence they want.
|
||||
expected_input.sequence = close_tx.input.get(input_index).ok_or(Error)?.sequence;
|
||||
expected_input.sequence = close_tx
|
||||
.input
|
||||
.get(input_index)
|
||||
.ok_or(Error::InvalidInput("transaction input index out of bounds"))?
|
||||
.sequence;
|
||||
|
||||
check_input_matches_expected(
|
||||
close_tx,
|
||||
@@ -1104,7 +1133,7 @@ impl SignedContract {
|
||||
.outcome_tx_build
|
||||
.outcome_spend_infos()
|
||||
.get(outcome)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = outcome_spend_info.witness_tx_close(
|
||||
close_tx,
|
||||
@@ -1132,7 +1161,7 @@ impl SignedContract {
|
||||
.split_tx_build
|
||||
.split_spend_infos()
|
||||
.get(win_cond)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = split_spend_info.witness_tx_win(
|
||||
win_tx,
|
||||
@@ -1172,13 +1201,13 @@ impl SignedContract {
|
||||
.players
|
||||
.get(win_cond.player_index)
|
||||
.cloned()
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)?;
|
||||
|
||||
let player_secret_key = player_secret_key.into();
|
||||
if player_secret_key.base_point_mul() != winner.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
} else if sha256(&ticket_preimage) != winner.ticket_hash {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidInput("ticket preimage does not match hash"));
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
@@ -1214,7 +1243,7 @@ impl SignedContract {
|
||||
.split_tx_build
|
||||
.split_spend_infos()
|
||||
.get(win_cond)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = split_spend_info.witness_tx_reclaim(
|
||||
reclaim_tx,
|
||||
@@ -1246,7 +1275,7 @@ impl SignedContract {
|
||||
) -> Result<(), Error> {
|
||||
let market_maker_secret_key = market_maker_secret_key.into();
|
||||
if market_maker_secret_key.base_point_mul() != self.dlc.params.market_maker.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
@@ -1295,7 +1324,7 @@ impl SignedContract {
|
||||
) -> Result<(), Error> {
|
||||
let market_maker_secret_key = market_maker_secret_key.into();
|
||||
if market_maker_secret_key.base_point_mul() != self.dlc.params.market_maker.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
@@ -1303,7 +1332,11 @@ impl SignedContract {
|
||||
self.split_sellback_tx_input_and_prevout(win_cond)?;
|
||||
|
||||
// The caller can use whatever sequence they want.
|
||||
expected_input.sequence = sellback_tx.input.get(input_index).ok_or(Error)?.sequence;
|
||||
expected_input.sequence = sellback_tx
|
||||
.input
|
||||
.get(input_index)
|
||||
.ok_or(Error::InvalidInput("transaction input index out of bounds"))?
|
||||
.sequence;
|
||||
|
||||
check_input_matches_expected(
|
||||
sellback_tx,
|
||||
@@ -1318,7 +1351,7 @@ impl SignedContract {
|
||||
.split_tx_build
|
||||
.split_spend_infos()
|
||||
.get(win_cond)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = split_spend_info.witness_tx_sellback(
|
||||
sellback_tx,
|
||||
@@ -1360,7 +1393,7 @@ impl SignedContract {
|
||||
) -> Result<(), Error> {
|
||||
let market_maker_secret_key = market_maker_secret_key.into();
|
||||
if market_maker_secret_key.base_point_mul() != self.dlc.params.market_maker.pubkey {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidKey);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
@@ -1368,7 +1401,11 @@ impl SignedContract {
|
||||
self.split_sellback_tx_input_and_prevout(win_cond)?;
|
||||
|
||||
// The caller can use whatever sequence they want.
|
||||
expected_input.sequence = close_tx.input.get(input_index).ok_or(Error)?.sequence;
|
||||
expected_input.sequence = close_tx
|
||||
.input
|
||||
.get(input_index)
|
||||
.ok_or(Error::InvalidInput("transaction input index out of bounds"))?
|
||||
.sequence;
|
||||
|
||||
check_input_matches_expected(
|
||||
close_tx,
|
||||
@@ -1383,7 +1420,7 @@ impl SignedContract {
|
||||
.split_tx_build
|
||||
.split_spend_infos()
|
||||
.get(win_cond)
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::UnknownOutcome)?;
|
||||
|
||||
let witness = split_spend_info.witness_tx_close(
|
||||
close_tx,
|
||||
@@ -1407,23 +1444,29 @@ fn check_input_matches_expected<T: Borrow<TxOut>>(
|
||||
expected_input: &TxIn,
|
||||
expected_prevout: &TxOut,
|
||||
) -> Result<(), Error> {
|
||||
let input = tx.input.get(input_index).ok_or(Error)?;
|
||||
let input = tx
|
||||
.input
|
||||
.get(input_index)
|
||||
.ok_or(Error::InvalidInput("transaction input index out of bounds"))?;
|
||||
if input != expected_input {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidInput("input does not match expected"));
|
||||
}
|
||||
|
||||
let prevout = match prevouts {
|
||||
Prevouts::All(all_prevouts) => all_prevouts.get(input_index).ok_or(Error)?.borrow(),
|
||||
Prevouts::All(all_prevouts) => all_prevouts
|
||||
.get(input_index)
|
||||
.ok_or(Error::InvalidInput("prevout index out of bounds"))?
|
||||
.borrow(),
|
||||
Prevouts::One(i, prevout) => {
|
||||
if i != &input_index {
|
||||
return Err(Error)?;
|
||||
return Err(Error::InvalidInput("prevout index mismatch"));
|
||||
}
|
||||
prevout.borrow()
|
||||
}
|
||||
};
|
||||
|
||||
if prevout != expected_prevout {
|
||||
return Err(Error);
|
||||
return Err(Error::InvalidInput("prevout does not match expected"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -20,8 +20,12 @@ impl std::str::FromStr for Outcome {
|
||||
match s {
|
||||
"exp" => Ok(Outcome::Expiry),
|
||||
s => {
|
||||
let index_str = s.strip_prefix("att").ok_or(Error)?;
|
||||
let outcome_index = index_str.parse().map_err(|_| Error)?;
|
||||
let index_str = s.strip_prefix("att").ok_or(Error::Conversion(
|
||||
"invalid outcome format, expected 'att' prefix",
|
||||
))?;
|
||||
let outcome_index = index_str
|
||||
.parse::<usize>()
|
||||
.map_err(Error::ParseOutcomeIndex)?;
|
||||
Ok(Outcome::Attestation(outcome_index))
|
||||
}
|
||||
}
|
||||
@@ -72,10 +76,14 @@ impl std::str::FromStr for WinCondition {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (prefix, suffix) = s.split_once(":").ok_or(Error)?;
|
||||
let (prefix, suffix) = s.split_once(":").ok_or(Error::Conversion(
|
||||
"invalid win condition format, missing ':' separator",
|
||||
))?;
|
||||
let outcome: Outcome = prefix.parse()?;
|
||||
let player_index_str = suffix.strip_prefix("p").ok_or(Error)?;
|
||||
let player_index = player_index_str.parse().map_err(|_| Error)?;
|
||||
let player_index_str = suffix.strip_prefix("p").ok_or(Error::Conversion(
|
||||
"invalid win condition format, expected 'p' prefix",
|
||||
))?;
|
||||
let player_index = player_index_str.parse()?;
|
||||
Ok(WinCondition {
|
||||
outcome,
|
||||
player_index,
|
||||
|
||||
@@ -103,7 +103,10 @@ impl FundingSpendInfo {
|
||||
if pubkey == mm_pubkey {
|
||||
Ok(market_maker_secret_key)
|
||||
} else {
|
||||
player_secret_keys.get(&pubkey).ok_or(Error).copied()
|
||||
player_secret_keys
|
||||
.get(&pubkey)
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)
|
||||
.copied()
|
||||
}
|
||||
})
|
||||
.collect::<Result<_, Error>>()?;
|
||||
|
||||
@@ -58,7 +58,7 @@ impl OutcomeSpendInfo {
|
||||
let winners: BTreeMap<PlayerIndex, &Player> = winner_indexes
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let player = all_players.get(i).ok_or(Error)?;
|
||||
let player = all_players.get(i).ok_or(Error::OutOfBoundsPlayerIndex)?;
|
||||
Ok((i, player.borrow()))
|
||||
})
|
||||
.collect::<Result<_, Error>>()?;
|
||||
@@ -216,7 +216,10 @@ impl OutcomeSpendInfo {
|
||||
script_pubkey: self.script_pubkey(),
|
||||
value: self.outcome_value,
|
||||
}];
|
||||
let split_script = self.winner_split_scripts.get(player_index).ok_or(Error)?;
|
||||
let split_script = self
|
||||
.winner_split_scripts
|
||||
.get(player_index)
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)?;
|
||||
let leaf_hash = TapLeafHash::from_script(split_script, LeafVersion::TapScript);
|
||||
|
||||
let sighash = SighashCache::new(split_tx).taproot_script_spend_signature_hash(
|
||||
@@ -238,12 +241,12 @@ impl OutcomeSpendInfo {
|
||||
let split_script = self
|
||||
.winner_split_scripts
|
||||
.get(player_index)
|
||||
.ok_or(Error)?
|
||||
.ok_or(Error::OutOfBoundsPlayerIndex)?
|
||||
.clone();
|
||||
let control_block = self
|
||||
.spend_info
|
||||
.control_block(&(split_script.clone(), LeafVersion::TapScript))
|
||||
.ok_or(Error)?;
|
||||
.ok_or(Error::InvalidKey)?;
|
||||
|
||||
let mut witness = Witness::new();
|
||||
witness.push(signature.serialize());
|
||||
@@ -319,7 +322,12 @@ impl OutcomeSpendInfo {
|
||||
if pubkey == mm_pubkey {
|
||||
Ok(market_maker_secret_key)
|
||||
} else {
|
||||
player_secret_keys.get(&pubkey).ok_or(Error).copied()
|
||||
player_secret_keys
|
||||
.get(&pubkey)
|
||||
.ok_or(Error::MissingSignature(String::from(
|
||||
"player secret keys for pubkey",
|
||||
)))
|
||||
.copied()
|
||||
}
|
||||
})
|
||||
.collect::<Result<_, Error>>()?;
|
||||
|
||||
Reference in New Issue
Block a user