mirror of
https://github.com/conduition/dlctix.git
synced 2026-01-30 13:15:11 +01:00
add methods to construct fully signed outcome/split/expiry transactions
This commit is contained in:
108
src/lib.rs
108
src/lib.rs
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user