Skip to content

Commit 2db88a0

Browse files
authored
Add state machine tests that sign/verify bitcoin taproot key and script spends (#199)
* add btc_sign_verify test that uses state machines to sign/verify btc taproot key and script spends * dont take a reference of a slice * clean up module imports and references
1 parent 4309bae commit 2db88a0

4 files changed

Lines changed: 152 additions & 22 deletions

File tree

src/btc.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ mod test {
240240
script::{Builder, PushBytesBuf},
241241
},
242242
opcodes::all::*,
243-
taproot::{LeafVersion, TaprootBuilder},
243+
secp256k1::schnorr,
244+
taproot::{self, LeafVersion, TaprootBuilder},
244245
};
245246
use rand_core::OsRng;
246247

@@ -332,7 +333,7 @@ mod test {
332333
let nums_x_data =
333334
hex::decode("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")
334335
.unwrap();
335-
let nums_public_key = bitcoin::XOnlyPublicKey::from_slice(&nums_x_data).unwrap();
336+
let nums_public_key = XOnlyPublicKey::from_slice(&nums_x_data).unwrap();
336337

337338
let spend_info = TaprootBuilder::new()
338339
.add_leaf(1, accept_script.clone())
@@ -357,7 +358,7 @@ mod test {
357358
.expect("Failed to create message");
358359

359360
let schnorr_sig = secp.sign_schnorr(&message, &depositor_keypair);
360-
let taproot_sig = bitcoin::taproot::Signature {
361+
let taproot_sig = taproot::Signature {
361362
signature: schnorr_sig,
362363
sighash_type: TapSighashType::All,
363364
};
@@ -390,9 +391,9 @@ mod test {
390391
Ok(proof) => proof,
391392
};
392393
let proof_bytes = proof.to_bytes();
393-
let schnorr_sig = bitcoin::secp256k1::schnorr::Signature::from_slice(&proof_bytes)
394+
let schnorr_sig = schnorr::Signature::from_slice(&proof_bytes)
394395
.expect("Failed to parse Signature from slice");
395-
let taproot_sig = bitcoin::taproot::Signature {
396+
let taproot_sig = taproot::Signature {
396397
signature: schnorr_sig,
397398
sighash_type: TapSighashType::All,
398399
};
@@ -448,7 +449,7 @@ mod test {
448449

449450
// [1] Verify the correct signature, which should succeed.
450451
let schnorr_sig = secp.sign_schnorr(&message, &tweaked.to_keypair());
451-
let taproot_sig = bitcoin::taproot::Signature {
452+
let taproot_sig = taproot::Signature {
452453
signature: schnorr_sig,
453454
sighash_type: TapSighashType::All,
454455
};
@@ -458,7 +459,7 @@ mod test {
458459

459460
// [2] Verify the correct signature, but with a different sighash type,
460461
// which should fail.
461-
let taproot_sig = bitcoin::taproot::Signature {
462+
let taproot_sig = taproot::Signature {
462463
signature: schnorr_sig,
463464
sighash_type: TapSighashType::None,
464465
};
@@ -470,7 +471,7 @@ mod test {
470471
// which should fail. In this case we've created the signature using
471472
// the untweaked keypair.
472473
let schnorr_sig = secp.sign_schnorr(&message, &keypair);
473-
let taproot_sig = bitcoin::taproot::Signature {
474+
let taproot_sig = taproot::Signature {
474475
signature: schnorr_sig,
475476
sighash_type: TapSighashType::All,
476477
};
@@ -483,7 +484,7 @@ mod test {
483484
let secret_key = secp256k1::SecretKey::new(&mut OsRng);
484485
let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key);
485486
let schnorr_sig = secp.sign_schnorr(&message, &keypair);
486-
let taproot_sig = bitcoin::taproot::Signature {
487+
let taproot_sig = taproot::Signature {
487488
signature: schnorr_sig,
488489
sighash_type: TapSighashType::All,
489490
};
@@ -494,7 +495,7 @@ mod test {
494495
// [5] Same as [4], but using its tweaked key.
495496
let tweaked = keypair.tap_tweak(&secp, merkle_root);
496497
let schnorr_sig = secp.sign_schnorr(&message, &tweaked.to_keypair());
497-
let taproot_sig = bitcoin::taproot::Signature {
498+
let taproot_sig = taproot::Signature {
498499
signature: schnorr_sig,
499500
sighash_type: TapSighashType::All,
500501
};
@@ -573,9 +574,9 @@ mod test {
573574
assert!(proof_deser.verify(&tweaked_public_key.x(), message));
574575

575576
// [1] Verify the correct signature, which should succeed.
576-
let schnorr_sig = bitcoin::secp256k1::schnorr::Signature::from_slice(&proof_bytes)
577+
let schnorr_sig = schnorr::Signature::from_slice(&proof_bytes)
577578
.expect("Failed to parse Signature from slice");
578-
let taproot_sig = bitcoin::taproot::Signature {
579+
let taproot_sig = taproot::Signature {
579580
signature: schnorr_sig,
580581
sighash_type: TapSighashType::All,
581582
};
@@ -585,7 +586,7 @@ mod test {
585586

586587
// [2] Verify the correct signature, but with a different sighash type,
587588
// which should fail.
588-
let taproot_sig = bitcoin::taproot::Signature {
589+
let taproot_sig = taproot::Signature {
589590
signature: schnorr_sig,
590591
sighash_type: TapSighashType::None,
591592
};

src/state_machine/coordinator/fire.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,11 +1592,11 @@ pub mod test {
15921592
coordinator::{
15931593
fire::Coordinator as FireCoordinator,
15941594
test::{
1595-
bad_signature_share_request, check_signature_shares, coordinator_state_machine,
1596-
empty_private_shares, empty_public_shares, equal_after_save_load,
1597-
feedback_messages, feedback_mutated_messages, gen_nonces, invalid_nonce,
1598-
new_coordinator, run_dkg_sign, setup, setup_with_timeouts, start_dkg_round,
1599-
start_signing_round, verify_packet_sigs,
1595+
bad_signature_share_request, btc_sign_verify, check_signature_shares,
1596+
coordinator_state_machine, empty_private_shares, empty_public_shares,
1597+
equal_after_save_load, feedback_messages, feedback_mutated_messages,
1598+
gen_nonces, invalid_nonce, new_coordinator, run_dkg_sign, setup,
1599+
setup_with_timeouts, start_dkg_round, start_signing_round, verify_packet_sigs,
16001600
},
16011601
Config, Coordinator as CoordinatorTrait, State,
16021602
},
@@ -3732,4 +3732,15 @@ pub mod test {
37323732
fn verify_packet_sigs_v2() {
37333733
verify_packet_sigs::<FireCoordinator<v2::Aggregator>, v2::Signer>();
37343734
}
3735+
3736+
#[test]
3737+
#[cfg(feature = "with_v1")]
3738+
fn btc_sign_verify_v1() {
3739+
btc_sign_verify::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
3740+
}
3741+
3742+
#[test]
3743+
fn btc_sign_verify_v2() {
3744+
btc_sign_verify::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
3745+
}
37353746
}

src/state_machine/coordinator/frost.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,10 +1011,10 @@ pub mod test {
10111011
state_machine::coordinator::{
10121012
frost::Coordinator as FrostCoordinator,
10131013
test::{
1014-
bad_signature_share_request, check_signature_shares, coordinator_state_machine,
1015-
empty_private_shares, empty_public_shares, equal_after_save_load, invalid_nonce,
1016-
new_coordinator, run_dkg_sign, setup, start_dkg_round, start_signing_round,
1017-
verify_packet_sigs,
1014+
bad_signature_share_request, btc_sign_verify, check_signature_shares,
1015+
coordinator_state_machine, empty_private_shares, empty_public_shares,
1016+
equal_after_save_load, invalid_nonce, new_coordinator, run_dkg_sign, setup,
1017+
start_dkg_round, start_signing_round, verify_packet_sigs,
10181018
},
10191019
Config, Coordinator as CoordinatorTrait, State,
10201020
},
@@ -1614,4 +1614,15 @@ pub mod test {
16141614
fn verify_packet_sigs_v2() {
16151615
verify_packet_sigs::<FrostCoordinator<v2::Aggregator>, v2::Signer>();
16161616
}
1617+
1618+
#[test]
1619+
#[cfg(feature = "with_v1")]
1620+
fn btc_sign_verify_v1() {
1621+
btc_sign_verify::<FrostCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
1622+
}
1623+
1624+
#[test]
1625+
fn btc_sign_verify_v2() {
1626+
btc_sign_verify::<FrostCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
1627+
}
16171628
}

src/state_machine/coordinator/mod.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,4 +1885,111 @@ pub mod test {
18851885
);
18861886
}
18871887
}
1888+
1889+
use crate::btc::UnsignedTx;
1890+
1891+
use bitcoin::{
1892+
blockdata::script::Builder,
1893+
opcodes::all::*,
1894+
secp256k1::{schnorr, Secp256k1, XOnlyPublicKey},
1895+
taproot::{self, LeafVersion, TaprootBuilder},
1896+
TapSighashType, Witness,
1897+
};
1898+
1899+
/// Create a taproot transaction with key and script spends, then sign/verify each spend path
1900+
pub fn btc_sign_verify<Coordinator: CoordinatorTrait, SignerType: SignerTrait>(
1901+
num_signers: u32,
1902+
keys_per_signer: u32,
1903+
) {
1904+
let (mut coordinators, mut signers) =
1905+
run_dkg::<Coordinator, SignerType>(num_signers, keys_per_signer);
1906+
1907+
let aggregate_public_key = coordinators[0]
1908+
.get_aggregate_public_key()
1909+
.expect("public key");
1910+
let aggregate_xonly_key = XOnlyPublicKey::from_slice(&aggregate_public_key.x().to_bytes())
1911+
.expect("failed to create XOnlyPublicKey");
1912+
1913+
let secp = Secp256k1::new();
1914+
let script = Builder::new()
1915+
.push_x_only_key(&aggregate_xonly_key)
1916+
.push_opcode(OP_CHECKSIG)
1917+
.into_script();
1918+
let spend_info = TaprootBuilder::new()
1919+
.add_leaf(0, script.clone())
1920+
.unwrap()
1921+
.finalize(&secp, aggregate_xonly_key)
1922+
.expect("failed to finalize taproot_spend_info");
1923+
let merkle_root = spend_info.merkle_root();
1924+
let internal_key = spend_info.internal_key();
1925+
let unsigned = UnsignedTx::new(internal_key);
1926+
1927+
// test the key spend
1928+
let sighash = unsigned
1929+
.compute_sighash(&secp, merkle_root)
1930+
.expect("failed to compute taproot sighash");
1931+
let msg: &[u8] = sighash.as_ref();
1932+
1933+
let raw_merkle_root = merkle_root.map(|root| {
1934+
let bytes: [u8; 32] = *root.to_raw_hash().as_ref();
1935+
bytes
1936+
});
1937+
1938+
let OperationResult::SignTaproot(proof) = run_sign::<Coordinator, SignerType>(
1939+
&mut coordinators,
1940+
&mut signers,
1941+
msg,
1942+
SignatureType::Taproot(raw_merkle_root),
1943+
) else {
1944+
panic!("taproot signature failed");
1945+
};
1946+
1947+
let schnorr_sig = schnorr::Signature::from_slice(&proof.to_bytes())
1948+
.expect("Failed to parse Signature from slice");
1949+
let taproot_sig = taproot::Signature {
1950+
signature: schnorr_sig,
1951+
sighash_type: TapSighashType::All,
1952+
};
1953+
1954+
unsigned
1955+
.verify_signature(&secp, &taproot_sig, merkle_root)
1956+
.expect("signature verification failed");
1957+
1958+
// test the script spend
1959+
let sighash = unsigned
1960+
.compute_script_sighash(&secp, merkle_root, &script)
1961+
.expect("failed to compute taproot sighash");
1962+
let msg: &[u8] = sighash.as_ref();
1963+
1964+
let OperationResult::SignSchnorr(proof) = run_sign::<Coordinator, SignerType>(
1965+
&mut coordinators,
1966+
&mut signers,
1967+
msg,
1968+
SignatureType::Schnorr,
1969+
) else {
1970+
panic!("schnorr signature failed");
1971+
};
1972+
1973+
let schnorr_sig = schnorr::Signature::from_slice(&proof.to_bytes())
1974+
.expect("Failed to parse Signature from slice");
1975+
let taproot_sig = taproot::Signature {
1976+
signature: schnorr_sig,
1977+
sighash_type: TapSighashType::All,
1978+
};
1979+
1980+
let control_block = spend_info
1981+
.control_block(&(script.clone(), LeafVersion::TapScript))
1982+
.expect("insert the accept script into the control block");
1983+
1984+
println!("ControlBlock {control_block:?}");
1985+
1986+
let mut witness = Witness::new();
1987+
witness.push(taproot_sig.to_vec());
1988+
witness.push(script.as_bytes());
1989+
witness.push(control_block.serialize());
1990+
1991+
unsigned
1992+
.verify_witness(&secp, witness, merkle_root)
1993+
.expect("signature verification failed");
1994+
}
18881995
}

0 commit comments

Comments
 (0)