Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions goat/src/connectors/kickoff_connectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,22 +172,25 @@ impl TaprootConnector for KickoffConnector {
}
}

/// Output 0 of the next Pre-Kickoff, spent only by the committee-signed
/// Quick-Challenge or Force-Skip-Kickoff. The operator holds no key to it, so
/// it cannot sweep the output and make those penalties unspendable.
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone)]
pub struct ForceSkipConnector {
pub network: Network,
pub operator_taproot_public_key: XOnlyPublicKey,
pub n_of_n_taproot_public_key: XOnlyPublicKey,
}

impl ForceSkipConnector {
pub fn new(network: Network, operator_taproot_public_key: &XOnlyPublicKey) -> Self {
pub fn new(network: Network, n_of_n_taproot_public_key: &XOnlyPublicKey) -> Self {
ForceSkipConnector {
network,
operator_taproot_public_key: *operator_taproot_public_key,
n_of_n_taproot_public_key: *n_of_n_taproot_public_key,
}
}

fn generate_taproot_leaf_0_script(&self) -> ScriptBuf {
generate_pay_to_pubkey_taproot_script(&self.operator_taproot_public_key)
generate_pay_to_pubkey_taproot_script(&self.n_of_n_taproot_public_key)
}

fn generate_taproot_leaf_0_tx_in(&self, input: &Input) -> TxIn {
Expand All @@ -214,7 +217,7 @@ impl TaprootConnector for ForceSkipConnector {
TaprootBuilder::new()
.add_leaf(0, self.generate_taproot_leaf_0_script())
.expect("Unable to add leaf 0")
.finalize(SECP256K1, self.operator_taproot_public_key)
.finalize(SECP256K1, self.n_of_n_taproot_public_key)
.expect("Unable to finalize taproot")
}

Expand Down
207 changes: 185 additions & 22 deletions goat/src/transactions/prekickoff.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bitcoin::{
absolute, consensus, Address, Amount, ScriptBuf, TapSighashType, Transaction, TxIn, TxOut,
};
use musig2::{errors::SigningError, AggNonce, PartialSignature, SecNonce};
use serde::{Deserialize, Serialize};

use crate::{
Expand All @@ -10,10 +11,18 @@ use crate::{
ForceSkipConnector, GuardianConnector, KickoffConnector, PrekickoffConnector,
},
},
contexts::operator::OperatorContext,
contexts::{base::BaseContext, committee::CommitteeContext, operator::OperatorContext},
error::{Error, TransactionError::InsufficientInputAmount},
scripts::p2a_output,
transactions::signing::populate_taproot_input_witness_default,
transactions::{
signing::{
populate_taproot_input_witness_default,
push_taproot_leaf_script_and_control_block_to_witness,
},
signing_musig2::{
generate_taproot_aggregated_signature, generate_taproot_partial_signature,
},
},
};

use super::{base::*, pre_signed::*};
Expand Down Expand Up @@ -266,7 +275,7 @@ impl ForceSkipKickoffTransaction {
input_1,
);

this.pre_sign_and_push(context, kickoff_connector, next_force_skip_connector);
this.pre_sign_and_push(context, kickoff_connector);

this
}
Expand Down Expand Up @@ -325,29 +334,106 @@ impl ForceSkipKickoffTransaction {
);
}

pub fn sign_input_1(
fn sign_input_1_musig2(
&mut self,
context: &CommitteeContext,
sec_nonce: &SecNonce,
agg_nonce: &AggNonce,
) -> Result<PartialSignature, SigningError> {
let input_index = 1;
let sighash_type = TapSighashType::None;
generate_taproot_partial_signature(
context,
self.tx(),
sec_nonce,
agg_nonce,
input_index,
self.prev_outs(),
&self.prev_scripts()[input_index],
sighash_type,
)
}

fn push_input_1_signature(
&mut self,
context: &OperatorContext,
next_force_skip_connector: &ForceSkipConnector,
input_1_sig: bitcoin::taproot::Signature,
) {
let input_index = 1;
pre_sign_taproot_input_default(
self,
let script = self.prev_scripts()[input_index].clone();
let spend_info = next_force_skip_connector.generate_taproot_spend_info();
let tx_mut = self.tx_mut();
tx_mut.input[input_index]
.witness
.push(input_1_sig.serialize());
push_taproot_leaf_script_and_control_block_to_witness(
tx_mut,
input_index,
TapSighashType::None,
next_force_skip_connector.generate_taproot_spend_info(),
&vec![&context.operator_keypair],
&spend_info,
&script,
);
}

pub fn pre_sign(
&mut self,
context: &CommitteeContext,
sec_nonces: &[SecNonce; 1],
agg_nonces: &[AggNonce; 1],
) -> Result<[PartialSignature; 1], SigningError> {
let input_1_sig = self.sign_input_1_musig2(context, &sec_nonces[0], &agg_nonces[0]);
match [input_1_sig]
.into_iter()
.collect::<Result<Vec<PartialSignature>, SigningError>>()
{
Ok(sigs) => Ok(sigs.try_into().unwrap()),
Err(e) => Err(e),
}
}

pub fn aggregate_pre_sigs(
&self,
context: &dyn BaseContext,
partial_signatures: &[Vec<PartialSignature>; 1],
agg_nonces: &[AggNonce; 1],
) -> Result<[bitcoin::taproot::Signature; 1], Error> {
let input_index = 1;
let sig_index = 0;
let sighash_type = TapSighashType::None;
let input_1_sig = match generate_taproot_aggregated_signature(
context,
self.tx(),
&agg_nonces[sig_index],
input_index,
self.prev_outs(),
&self.prev_scripts()[input_index],
sighash_type,
partial_signatures[sig_index].clone(),
) {
Ok(sig) => bitcoin::taproot::Signature {
signature: sig.into(),
sighash_type,
},
Err(_) => return Err(Error::Other("Failed to aggregate signatures")),
};
Ok([input_1_sig])
}

pub fn push_pre_sigs(
&mut self,
next_force_skip_connector: &ForceSkipConnector,
pre_sigs: [bitcoin::taproot::Signature; 1],
) {
self.push_input_1_signature(next_force_skip_connector, pre_sigs[0]);
}

/// Signs input 0. Input 1 spends the force-skip connector and is signed
/// by the committee through `pre_sign`.
pub fn pre_sign_and_push(
&mut self,
context: &OperatorContext,
kickoff_connector: &KickoffConnector,
next_force_skip_connector: &ForceSkipConnector,
) {
self.sign_input_0(context, kickoff_connector);
self.sign_input_1(context, next_force_skip_connector);
}
}
impl BaseTransaction for ForceSkipKickoffTransaction {
Expand Down Expand Up @@ -399,7 +485,7 @@ impl QuickChallengeTransaction {
input_1,
);

this.pre_sign_and_push(context, guardian_connector, next_force_skip_connector);
this.pre_sign_and_push(context, guardian_connector);

this
}
Expand Down Expand Up @@ -460,29 +546,106 @@ impl QuickChallengeTransaction {
);
}

pub fn sign_input_1(
fn sign_input_1_musig2(
&mut self,
context: &CommitteeContext,
sec_nonce: &SecNonce,
agg_nonce: &AggNonce,
) -> Result<PartialSignature, SigningError> {
let input_index = 1;
let sighash_type = TapSighashType::None;
generate_taproot_partial_signature(
context,
self.tx(),
sec_nonce,
agg_nonce,
input_index,
self.prev_outs(),
&self.prev_scripts()[input_index],
sighash_type,
)
}

fn push_input_1_signature(
&mut self,
context: &OperatorContext,
next_force_skip_connector: &ForceSkipConnector,
input_1_sig: bitcoin::taproot::Signature,
) {
let input_index = 1;
pre_sign_taproot_input_default(
self,
let script = self.prev_scripts()[input_index].clone();
let spend_info = next_force_skip_connector.generate_taproot_spend_info();
let tx_mut = self.tx_mut();
tx_mut.input[input_index]
.witness
.push(input_1_sig.serialize());
push_taproot_leaf_script_and_control_block_to_witness(
tx_mut,
input_index,
TapSighashType::None,
next_force_skip_connector.generate_taproot_spend_info(),
&vec![&context.operator_keypair],
&spend_info,
&script,
);
}

pub fn pre_sign(
&mut self,
context: &CommitteeContext,
sec_nonces: &[SecNonce; 1],
agg_nonces: &[AggNonce; 1],
) -> Result<[PartialSignature; 1], SigningError> {
let input_1_sig = self.sign_input_1_musig2(context, &sec_nonces[0], &agg_nonces[0]);
match [input_1_sig]
.into_iter()
.collect::<Result<Vec<PartialSignature>, SigningError>>()
{
Ok(sigs) => Ok(sigs.try_into().unwrap()),
Err(e) => Err(e),
}
}

pub fn aggregate_pre_sigs(
&self,
context: &dyn BaseContext,
partial_signatures: &[Vec<PartialSignature>; 1],
agg_nonces: &[AggNonce; 1],
) -> Result<[bitcoin::taproot::Signature; 1], Error> {
let input_index = 1;
let sig_index = 0;
let sighash_type = TapSighashType::None;
let input_1_sig = match generate_taproot_aggregated_signature(
context,
self.tx(),
&agg_nonces[sig_index],
input_index,
self.prev_outs(),
&self.prev_scripts()[input_index],
sighash_type,
partial_signatures[sig_index].clone(),
) {
Ok(sig) => bitcoin::taproot::Signature {
signature: sig.into(),
sighash_type,
},
Err(_) => return Err(Error::Other("Failed to aggregate signatures")),
};
Ok([input_1_sig])
}

pub fn push_pre_sigs(
&mut self,
next_force_skip_connector: &ForceSkipConnector,
pre_sigs: [bitcoin::taproot::Signature; 1],
) {
self.push_input_1_signature(next_force_skip_connector, pre_sigs[0]);
}

/// Signs input 0. Input 1 spends the force-skip connector and is signed
/// by the committee through `pre_sign`.
pub fn pre_sign_and_push(
&mut self,
context: &OperatorContext,
guardian_connector: &GuardianConnector,
next_force_skip_connector: &ForceSkipConnector,
) {
self.sign_input_0(context, guardian_connector);
self.sign_input_1(context, next_force_skip_connector);
}
}
impl BaseTransaction for QuickChallengeTransaction {
Expand Down
Loading