Skip to main content

bitcoin_rs_script/
taproot.rs

1use secp256k1::{Message, XOnlyPublicKey, schnorr::Signature};
2
3/// Verifies a taproot key-path Schnorr signature.
4#[must_use]
5pub fn verify_taproot_keypath(
6    signature: &Signature,
7    message: &Message,
8    public_key: &XOnlyPublicKey,
9) -> bool {
10    secp256k1::SECP256K1
11        .verify_schnorr(signature, message.as_ref(), public_key)
12        .is_ok()
13}
14
15/// Verifies a tapscript Schnorr signature.
16///
17/// BIP342 changes the message construction and script rules, but the final
18/// Schnorr verification primitive is identical to key-path verification.
19#[must_use]
20pub fn verify_taproot_scriptpath(
21    signature: &Signature,
22    message: &Message,
23    public_key: &XOnlyPublicKey,
24) -> bool {
25    verify_taproot_keypath(signature, message, public_key)
26}
27
28#[cfg(test)]
29mod tests {
30    use secp256k1::{Keypair, Message, Secp256k1, SecretKey, XOnlyPublicKey};
31
32    use super::{verify_taproot_keypath, verify_taproot_scriptpath};
33
34    #[test]
35    fn taproot_helpers_accept_valid_schnorr_signature() {
36        let secp = Secp256k1::new();
37        let secret = match SecretKey::from_byte_array([1; 32]) {
38            Ok(secret) => secret,
39            Err(error) => panic!("fixed secret key should be valid: {error}"),
40        };
41        let keypair = Keypair::from_secret_key(&secp, &secret);
42        let (public_key, _) = XOnlyPublicKey::from_keypair(&keypair);
43        let message = Message::from_digest([2; 32]);
44        let signature = secp.sign_schnorr(message.as_ref(), &keypair);
45
46        assert!(verify_taproot_keypath(&signature, &message, &public_key));
47        assert!(verify_taproot_scriptpath(&signature, &message, &public_key));
48    }
49}