Skip to content
Merged
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ categories = ["cryptography"]
default = ["with_p256k1_bindgen"]
with_p256k1_bindgen = ["p256k1/with_bindgen"]
with_v1 = []
testing = []
testing = ["dep:bitcoin", "dep:bitcoinconsensus"]

[dependencies]
bitcoin = { version = "0.32", default-features = false, features = ["serde", "rand-std"], optional = true }
bitcoinconsensus = { version = "0.106.0", default-features = false, optional = true }

aes-gcm = "0.10"
bs58 = "0.5"
elliptic-curve = { version = "0.13.8", features = ["hash2curve"] }
Expand Down
2 changes: 1 addition & 1 deletion src/btc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use bitcoin::{
absolute::LockTime,
consensus::Encodable,
key::TapTweak,
secp256k1::{self, Secp256k1, Verification, XOnlyPublicKey},
sighash::{Prevouts, SighashCache},
taproot::{LeafVersion, Signature},
Expand Down Expand Up @@ -233,6 +232,7 @@ mod test {
traits::{Aggregator, Signer},
v2,
};
use bitcoin::key::TapTweak;

use bitcoin::{
blockdata::{
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![doc = include_str!("../README.md")]

/// test bitcoin/taproot integration using libbitcoin_consensus
#[cfg(test)]
#[cfg(any(test, feature = "testing"))]
pub mod btc;
/// Types which are common to both v1 and v2
#[allow(clippy::op_ref)]
Expand Down
21 changes: 16 additions & 5 deletions src/state_machine/coordinator/fire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1592,11 +1592,11 @@ pub mod test {
coordinator::{
fire::Coordinator as FireCoordinator,
test::{
bad_signature_share_request, check_signature_shares, coordinator_state_machine,
empty_private_shares, empty_public_shares, equal_after_save_load,
feedback_messages, feedback_mutated_messages, gen_nonces, invalid_nonce,
new_coordinator, run_dkg_sign, setup, setup_with_timeouts, start_dkg_round,
start_signing_round, verify_packet_sigs,
bad_signature_share_request, btc_sign_verify, check_signature_shares,
coordinator_state_machine, empty_private_shares, empty_public_shares,
equal_after_save_load, feedback_messages, feedback_mutated_messages,
gen_nonces, invalid_nonce, new_coordinator, run_dkg_sign, setup,
setup_with_timeouts, start_dkg_round, start_signing_round, verify_packet_sigs,
},
Config, Coordinator as CoordinatorTrait, State,
},
Expand Down Expand Up @@ -3732,4 +3732,15 @@ pub mod test {
fn verify_packet_sigs_v2() {
verify_packet_sigs::<FireCoordinator<v2::Aggregator>, v2::Signer>();
}

#[test]
#[cfg(feature = "with_v1")]
fn btc_sign_verify_v1() {
btc_sign_verify::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
}

#[test]
fn btc_sign_verify_v2() {
btc_sign_verify::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
}
}
19 changes: 15 additions & 4 deletions src/state_machine/coordinator/frost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1011,10 +1011,10 @@ pub mod test {
state_machine::coordinator::{
frost::Coordinator as FrostCoordinator,
test::{
bad_signature_share_request, check_signature_shares, coordinator_state_machine,
empty_private_shares, empty_public_shares, equal_after_save_load, invalid_nonce,
new_coordinator, run_dkg_sign, setup, start_dkg_round, start_signing_round,
verify_packet_sigs,
bad_signature_share_request, btc_sign_verify, check_signature_shares,
coordinator_state_machine, empty_private_shares, empty_public_shares,
equal_after_save_load, invalid_nonce, new_coordinator, run_dkg_sign, setup,
start_dkg_round, start_signing_round, verify_packet_sigs,
},
Config, Coordinator as CoordinatorTrait, State,
},
Expand Down Expand Up @@ -1614,4 +1614,15 @@ pub mod test {
fn verify_packet_sigs_v2() {
verify_packet_sigs::<FrostCoordinator<v2::Aggregator>, v2::Signer>();
}

#[test]
#[cfg(feature = "with_v1")]
fn btc_sign_verify_v1() {
btc_sign_verify::<FrostCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
}

#[test]
fn btc_sign_verify_v2() {
btc_sign_verify::<FrostCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
}
}
107 changes: 107 additions & 0 deletions src/state_machine/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1885,4 +1885,111 @@ pub mod test {
);
}
}

use crate::btc::UnsignedTx;

use bitcoin::{
blockdata::script::Builder,
opcodes::all::*,
secp256k1::{Secp256k1, XOnlyPublicKey},
taproot::{self, LeafVersion, TaprootBuilder},
TapSighashType, Witness,
};

/// Create a taproot transaction with key and script spends, then sign/verify each spend path
pub fn btc_sign_verify<Coordinator: CoordinatorTrait, SignerType: SignerTrait>(
num_signers: u32,
keys_per_signer: u32,
) {
let (mut coordinators, mut signers) =
run_dkg::<Coordinator, SignerType>(num_signers, keys_per_signer);

let aggregate_public_key = coordinators[0]
.get_aggregate_public_key()
.expect("public key");
let aggregate_xonly_key = XOnlyPublicKey::from_slice(&aggregate_public_key.x().to_bytes())
.expect("failed to create XOnlyPublicKey");

let secp = Secp256k1::new();
let script = Builder::new()
.push_x_only_key(&aggregate_xonly_key)
.push_opcode(OP_CHECKSIG)
.into_script();
let spend_info = TaprootBuilder::new()
.add_leaf(0, script.clone())
.unwrap()
.finalize(&secp, aggregate_xonly_key)
.expect("failed to finalize taproot_spend_info");
let merkle_root = spend_info.merkle_root();
let internal_key = spend_info.internal_key();
let unsigned = UnsignedTx::new(internal_key);

// test the key spend
let sighash = unsigned
.compute_sighash(&secp, merkle_root)
.expect("failed to compute taproot sighash");
let msg: &[u8] = sighash.as_ref();

let raw_merkle_root = merkle_root.map(|root| {
let bytes: [u8; 32] = *root.to_raw_hash().as_ref();
bytes
});

let OperationResult::SignTaproot(proof) = run_sign::<Coordinator, SignerType>(
&mut coordinators,
&mut signers,
msg,
SignatureType::Taproot(raw_merkle_root),
) else {
panic!("taproot signature failed");
};

let schnorr_sig = bitcoin::secp256k1::schnorr::Signature::from_slice(&proof.to_bytes())
.expect("Failed to parse Signature from slice");
let taproot_sig = taproot::Signature {
signature: schnorr_sig,
sighash_type: TapSighashType::All,
};

unsigned
.verify_signature(&secp, &taproot_sig, merkle_root)
.expect("signature verification failed");

// test the script spend
let sighash = unsigned
.compute_script_sighash(&secp, merkle_root, &script)
.expect("failed to compute taproot sighash");
let msg: &[u8] = sighash.as_ref();

let OperationResult::SignSchnorr(proof) = run_sign::<Coordinator, SignerType>(
&mut coordinators,
&mut signers,
msg,
SignatureType::Schnorr,
) else {
panic!("schnorr signature failed");
};

let schnorr_sig = bitcoin::secp256k1::schnorr::Signature::from_slice(&proof.to_bytes())
.expect("Failed to parse Signature from slice");
let taproot_sig = taproot::Signature {
signature: schnorr_sig,
sighash_type: TapSighashType::All,
};

let control_block = spend_info
.control_block(&(script.clone(), LeafVersion::TapScript))
.expect("insert the accept script into the control block");

println!("ControlBlock {control_block:?}");

let mut witness = Witness::new();
witness.push(taproot_sig.to_vec());
witness.push(script.as_bytes());
witness.push(control_block.serialize());

unsigned
.verify_witness(&secp, witness, merkle_root)
.expect("signature verification failed");
}
}
Loading