add methods to construct fully signed outcome/split/expiry transactions

This commit is contained in:
conduition
2024-03-08 07:15:17 +00:00
parent 042754bfd8
commit 882e6c4a95
2 changed files with 131 additions and 5 deletions

View File

@@ -17,9 +17,9 @@ use contract::{
};
use errors::Error;
use bitcoin::{OutPoint, TxOut};
use bitcoin::{OutPoint, Transaction, TxOut};
use musig2::{AdaptorSignature, AggNonce, CompactSignature, PartialSignature, PubNonce, SecNonce};
use secp::{Point, Scalar};
use secp::{MaybeScalar, Point, Scalar};
use std::collections::BTreeMap;
@@ -414,4 +414,108 @@ impl SigningSession<CompleteState> {
pub fn signatures(&self) -> &ContractSignatures {
&self.state.signatures
}
/// Return an unsigned outcome transaction.
pub fn unsigned_outcome_tx<'a>(&'a self, outcome_index: usize) -> Option<&'a Transaction> {
self.dlc
.outcome_tx_build
.outcome_txs()
.get(&Outcome::Attestation(outcome_index))
}
/// Return a signed outcome transaction given the oracle's attestation
/// to a specific outcome.
pub fn signed_outcome_tx(
&self,
outcome_index: usize,
attestation: impl Into<MaybeScalar>,
) -> Result<Transaction, Error> {
let attestation = attestation.into();
let locking_point = self
.dlc
.params
.event
.attestation_lock_point(outcome_index)
.ok_or(Error)?;
// Invalid attestation.
if attestation.base_point_mul() != locking_point {
return Err(Error)?;
}
let mut outcome_tx = self
.unsigned_outcome_tx(outcome_index)
.ok_or(Error)?
.clone();
let adaptor_signature = self
.state
.signatures
.outcome_tx_signatures
.get(outcome_index)
.ok_or(Error)?;
let compact_sig: CompactSignature = adaptor_signature.adapt(attestation).ok_or(Error)?;
outcome_tx.input[0].witness.push(compact_sig.serialize());
Ok(outcome_tx)
}
/// Return the signed expiry transaction, if one exists for this contract.
pub fn expiry_tx(&self) -> Option<Transaction> {
let mut expiry_tx = self
.dlc
.outcome_tx_build
.outcome_txs()
.get(&Outcome::Expiry)?
.clone();
let signature: CompactSignature = self.state.signatures.expiry_tx_signature?;
expiry_tx.input[0].witness.push(signature.serialize());
Some(expiry_tx)
}
/// Return the unsigned split transaction for the given outcome.
pub fn unsigned_split_tx<'a>(&'a self, outcome: &Outcome) -> Option<&'a Transaction> {
self.dlc.split_tx_build.split_txs().get(outcome)
}
/// Return a signed split transaction, given the ticket preimage for a specific player.
pub fn signed_split_tx(
&self,
win_cond: &WinCondition,
ticket_preimage: hashlock::Preimage,
) -> Result<Transaction, Error> {
// Verify the preimage will unlock this specific player's split TX
// condition.
if hashlock::sha256(&ticket_preimage) != win_cond.winner.ticket_hash {
return Err(Error)?;
}
let signature = self
.state
.signatures
.split_tx_signatures
.get(win_cond)
.ok_or(Error)?;
let outcome_spend_info = self
.dlc
.outcome_tx_build
.outcome_spend_infos()
.get(&win_cond.outcome)
.ok_or(Error)?;
let witness =
outcome_spend_info.witness_tx_split(signature, ticket_preimage, &win_cond.winner)?;
let mut split_tx = self
.unsigned_split_tx(&win_cond.outcome)
.ok_or(Error)?
.clone();
split_tx.input[0].witness = witness;
Ok(split_tx)
}
}

View File

@@ -13,7 +13,7 @@ use secp::{Point, Scalar};
use crate::{
errors::Error,
hashlock::PREIMAGE_SIZE,
hashlock::{Preimage, PREIMAGE_SIZE},
parties::{MarketMaker, Player},
};
@@ -220,6 +220,28 @@ impl OutcomeSpendInfo {
Ok(sighash)
}
/// Compute a witness for a split transaction which spends from the outcome transaction.
pub(crate) fn witness_tx_split(
&self,
signature: &CompactSignature,
ticket_preimage: Preimage,
winner: &Player,
) -> Result<Witness, Error> {
let split_script = self.winner_split_scripts.get(winner).ok_or(Error)?.clone();
let control_block = self
.spend_info
.control_block(&(split_script.clone(), LeafVersion::TapScript))
.ok_or(Error)?;
let mut witness = Witness::new();
witness.push(signature.serialize());
witness.push(ticket_preimage);
witness.push(split_script);
witness.push(control_block.serialize());
Ok(witness)
}
/// Compute a witness for a reclaim transaction which spends from the outcome transaction.
///
/// This would only be used if none of the attested DLC outcome winners actually paid for
@@ -227,7 +249,7 @@ impl OutcomeSpendInfo {
/// without splitting it into multiple payout contracts and recombining the outputs unnecessarily.
pub(crate) fn witness_tx_reclaim<T: Borrow<TxOut>>(
&self,
split_tx: &Transaction,
reclaim_tx: &Transaction,
input_index: usize,
prevouts: &Prevouts<T>,
market_maker_secret_key: Scalar,
@@ -235,7 +257,7 @@ impl OutcomeSpendInfo {
) -> Result<Witness, Error> {
let leaf_hash = TapLeafHash::from_script(&self.reclaim_script, LeafVersion::TapScript);
let sighash = SighashCache::new(split_tx).taproot_script_spend_signature_hash(
let sighash = SighashCache::new(reclaim_tx).taproot_script_spend_signature_hash(
input_index,
prevouts,
leaf_hash,