mirror of
https://github.com/conduition/dlctix.git
synced 2026-01-30 05:05:06 +01:00
add cooperative split closing transaction signing
This allows the market maker to cooperatively close individual payout contracts with specific winners, if some of an outcome's winners cooperate but others do not.
This commit is contained in:
61
src/lib.rs
61
src/lib.rs
@@ -618,6 +618,13 @@ impl SignedContract {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn outcome_close_tx_input_and_prevout<'a>(
|
||||
&'a self,
|
||||
outcome: &Outcome,
|
||||
) -> Result<(TxIn, &'a TxOut), Error> {
|
||||
contract::outcome::outcome_tx_prevout(&self.dlc.outcome_tx_build, outcome, 0)
|
||||
}
|
||||
|
||||
pub fn split_win_tx_input_and_prevout<'a>(
|
||||
&'a self,
|
||||
win_cond: &WinCondition,
|
||||
@@ -712,7 +719,7 @@ impl SignedContract {
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
let (mut expected_input, expected_prevout) =
|
||||
self.outcome_reclaim_tx_input_and_prevout(outcome)?;
|
||||
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;
|
||||
@@ -886,6 +893,58 @@ impl SignedContract {
|
||||
sellback_tx.input[input_index].witness = witness;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sign a cooperative closing transaction which spends a player's split transaction output.
|
||||
/// The market maker can use this method once they have issued off-chain payouts to this
|
||||
/// winning player. Once the player has her off-chain payout, they can send their secret
|
||||
/// key to the market maker to let him reclaim all the on-chain capital efficiently.
|
||||
pub fn sign_split_close_tx_input<T: Borrow<TxOut>>(
|
||||
&self,
|
||||
win_cond: &WinCondition,
|
||||
close_tx: &mut Transaction,
|
||||
input_index: usize,
|
||||
prevouts: &Prevouts<T>,
|
||||
market_maker_secret_key: impl Into<Scalar>,
|
||||
player_secret_key: impl Into<Scalar>,
|
||||
) -> 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);
|
||||
}
|
||||
|
||||
// Confirm we're signing the correct input
|
||||
let (mut expected_input, expected_prevout) =
|
||||
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;
|
||||
|
||||
check_input_matches_expected(
|
||||
close_tx,
|
||||
prevouts,
|
||||
input_index,
|
||||
&expected_input,
|
||||
expected_prevout,
|
||||
)?;
|
||||
|
||||
let split_spend_info = self
|
||||
.dlc
|
||||
.split_tx_build
|
||||
.split_spend_infos()
|
||||
.get(win_cond)
|
||||
.ok_or(Error)?;
|
||||
|
||||
let witness = split_spend_info.witness_tx_close(
|
||||
close_tx,
|
||||
input_index,
|
||||
prevouts,
|
||||
market_maker_secret_key,
|
||||
player_secret_key.into(),
|
||||
)?;
|
||||
|
||||
close_tx.input[input_index].witness = witness;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate that a given `Transaction` and `Prevouts` set match the expected
|
||||
|
||||
@@ -318,4 +318,37 @@ impl SplitSpendInfo {
|
||||
|
||||
Ok(witness)
|
||||
}
|
||||
|
||||
/// Derive the witness for a cooperative closing transaction which spends from
|
||||
/// a single player's split TX output. The market maker must provide the secret
|
||||
/// key given by the player after a complete off-chain payout.
|
||||
pub(crate) fn witness_tx_close<T: Borrow<TxOut>>(
|
||||
&self,
|
||||
close_tx: &Transaction,
|
||||
input_index: usize,
|
||||
prevouts: &Prevouts<T>,
|
||||
market_maker_secret_key: Scalar,
|
||||
player_secret_key: Scalar,
|
||||
) -> Result<Witness, Error> {
|
||||
let mm_pubkey = market_maker_secret_key.base_point_mul();
|
||||
let sighash = SighashCache::new(close_tx).taproot_key_spend_signature_hash(
|
||||
input_index,
|
||||
prevouts,
|
||||
TapSighashType::Default,
|
||||
)?;
|
||||
|
||||
let ordered_seckeys = self.tweaked_ctx.pubkeys().into_iter().map(|pubkey| {
|
||||
if pubkey == &mm_pubkey {
|
||||
market_maker_secret_key
|
||||
} else {
|
||||
player_secret_key
|
||||
}
|
||||
});
|
||||
|
||||
let group_seckey: Scalar = self.tweaked_ctx.aggregated_seckey(ordered_seckeys)?;
|
||||
|
||||
let signature: CompactSignature = musig2::deterministic::sign_solo(group_seckey, sighash);
|
||||
let witness = Witness::from_slice(&[signature.serialize()]);
|
||||
Ok(witness)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user