1
0
mirror of https://github.com/bitcoin/bips.git synced 2026-03-09 15:53:54 +00:00
Files
bips/bip-0360/ref-impl/rust/examples/schnorr_example.rs
Hunter Beast eae7d9fc57 BIP360: Pay to Merkle Root (P2MR) (#1670)
Review comments and assistance by:
  Armin Sabouri <armins88@gmail.com>
  D++ <82842780+dplusplus1024@users.noreply.github.com>
  Jameson Lopp <jameson.lopp@gmail.com>
  jbride <jbride2001@yahoo.com>
  Joey Yandle <xoloki@gmail.com>
  Jon Atack <jon@atack.com>
  Jonas Nick <jonasd.nick@gmail.com>
  Kyle Crews <kylecrews@Kyles-Mac-Studio.local>
  Mark "Murch" Erhardt <murch@murch.one>
  notmike-5 <notmike-5@users.noreply.github.com>
  Vojtěch Strnad <43024885+vostrnad@users.noreply.github.com>

Co-authored-by: Ethan Heilman <ethan.r.heilman@gmail.com>
Co-authored-by: Isabel Foxen Duke <110147802+Isabelfoxenduke@users.noreply.github.com>
2026-02-11 13:01:47 -08:00

70 lines
3.3 KiB
Rust

use std::env;
use log::info;
use once_cell::sync::Lazy;
use bitcoin::key::{Secp256k1};
use bitcoin::hashes::{sha256::Hash, Hash as HashTrait};
use bitcoin::secp256k1::{Message};
use p2mr_ref::{ acquire_schnorr_keypair, verify_schnorr_signature };
/* Secp256k1 implements the Signing trait when it's initialized in signing mode.
It's important to note that Secp256k1 has different capabilities depending on how it's constructed:
* Secp256k1::new() creates a context capable of both signing and verification
* Secp256k1::signing_only() creates a context that can only sign
* Secp256k1::verification_only() creates a context that can only verify
*/
static SECP: Lazy<Secp256k1<bitcoin::secp256k1::All>> = Lazy::new(Secp256k1::new);
fn main() {
let _ = env_logger::try_init();
// acquire a schnorr keypair (leveraging OS provided random number generator)
let keypair = acquire_schnorr_keypair();
let (secret_key, public_key) = keypair.as_schnorr().unwrap();
let message_bytes = b"hello";
// secp256k1 operates on a 256-bit (32-byte) field, so inputs must be exactly this size
// subsequently, Schnorr signatures on secp256k1 require exactly a 32-byte input (the curve's scalar field size)
let message_hash: Hash = Hash::hash(message_bytes);
let message: Message = Message::from_digest_slice(&message_hash.to_byte_array()).unwrap();
/* The secp256k1 library internally generates a random scalar value (aka: nonce or k-value) for each signature
* Every signature is unique - even if you sign the same message with the same private key multiple times
* The randomness is handled automatically by the secp256k1 implementation
* You get different signatures each time for the same inputs
* The nonce is only needed during signing, not during verification
Schnorr signatures require randomness for security reasons:
* Prevents private key recovery - If the same nonce is used twice, an attacker could potentially derive your private key
* Ensures signature uniqueness - Each signature should be cryptographically distinct
* Protects against replay attacks - Different signatures for the same data
*/
let signature: bitcoin::secp256k1::schnorr::Signature = SECP.sign_schnorr(&message, &secret_key.keypair(&SECP));
info!("Signature created successfully, size: {}", signature.serialize().len());
//let pubkey = public_key;
/*
* The nonce provides security during signing (prevents private key recovery)
* The nonce is mathematically eliminated during verification
* The verifier only needs public information (signature, message, public key)
*/
let schnorr_valid = verify_schnorr_signature(signature, message, *public_key);
info!("schnorr_valid: {}", schnorr_valid);
let aux_rand = [0u8; 32]; // 32 zero bytes; fine for testing
let signature_aux_rand: bitcoin::secp256k1::schnorr::Signature = SECP.sign_schnorr_with_aux_rand(
&message,
&secret_key.keypair(&SECP),
&aux_rand
);
info!("aux_rand signature created successfully, size: {}", signature_aux_rand.serialize().len());
let schnorr_valid_aux_rand = verify_schnorr_signature(signature_aux_rand, message, *public_key);
info!("schnorr_valid_aux_rand: {}", schnorr_valid_aux_rand);
}