npub1xu…g43rm on Nostr: #[cfg(feature = "nostr")] use frost_secp256k1_tr as frost; // MUST use the -tr ...
#[cfg(feature = "nostr")]
use frost_secp256k1_tr as frost; // MUST use the -tr variant for BIP-340/Nostr
#[cfg(feature = "nostr")]
use rand::thread_rng;
#[cfg(feature = "nostr")]
use serde_json::json;
#[cfg(feature = "nostr")]
use sha2::{Digest, Sha256};
#[cfg(feature = "nostr")]
use std::collections::BTreeMap;
#[cfg(feature = "nostr")]
use hex;
#[cfg(feature = "nostr")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = thread_rng();
let (max_signers, min_signers) = (3, 2);
// 1. Setup Nostr Event Metadata
let pubkey_hex = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; // Example
let created_at = 1712050000;
let kind = 1;
let content = "Hello from ROAST threshold signatures!";
// 2. Serialize for Nostr ID (per NIP-01)
let event_json = json!([
0,
pubkey_hex,
created_at,
kind,
[],
content
]).to_string();
let mut hasher = Sha256::new();
hasher.update(event_json.as_bytes());
let event_id = hasher.finalize(); // This 32-byte hash is our signing message
// 3. FROST/ROAST Key Generation
let (shares, pubkey_package) = frost::keys::generate_with_dealer(
max_signers,
min_signers,
frost::keys::IdentifierList::Default,
&mut rng,
)?;
// 4. ROAST Coordination Simulation (Round 1: Commitments)
// In ROAST, the coordinator keeps a "session" open and collects commitments
let mut session_commitments = BTreeMap::new();
let mut signer_nonces = BTreeMap::new();
// Signers 1 and 3 respond first (Signer 2 is offline/slow)
for &id_val in &[1, 3] {
let id = frost::Identifier::try_from(id_val as u16)?;
let (nonces, comms) = frost::round1::commit(shares[&id].signing_share(), &mut rng);
session_commitments.insert(id, comms);
signer_nonces.insert(id, nonces);
}
// 5. Round 2: Signing the Nostr ID
let signing_package = frost::SigningPackage::new(session_commitments, &event_id);
let mut signature_shares = BTreeMap::new();
for (id, nonces) in signer_nonces {
let key_package: frost::keys::KeyPackage = shares[&id].clone().try_into()?;
let share = frost::round2::sign(&signing_package, &nonces, &key_package)?;
signature_shares.insert(id, share);
}
// 6. Aggregate into a BIP-340 Signature
let group_signature = frost::aggregate(
&signing_package,
&signature_shares,
&pubkey_package,
)?;
// 7. Verification (using BIP-340 logic)
pubkey_package.verifying_key().verify(&event_id, &group_signature)?;
println!("Nostr Event ID: {}", hex::encode(event_id));
println!("Threshold Signature (BIP-340): {}", hex::encode(group_signature.serialize()?));
println!("Successfully signed Nostr event using ROAST/FROST!");
Ok(())
}
#[cfg(not(feature = "nostr"))]
fn main() {
println!("This example requires the 'nostr' feature. Please run with: cargo run --example frost_bip_340 --features nostr");
}
Published at
2026-04-04 01:42:57 UTCEvent JSON
{
"id": "0b05d623136a78d7127b6b575be0f87c2b912b1b2bf40340fdda42a50cd5326a",
"pubkey": "37306250e62aafbe17f1fd1ee25a539f268e044d7516734c6e389c6de7273ac9",
"created_at": 1775266977,
"kind": 1,
"tags": [
[
"file",
"examples/frost_bip_340.rs"
],
[
"version",
"0.3.3"
]
],
"content": "#[cfg(feature = \"nostr\")]\nuse frost_secp256k1_tr as frost; // MUST use the -tr variant for BIP-340/Nostr\n#[cfg(feature = \"nostr\")]\nuse rand::thread_rng;\n#[cfg(feature = \"nostr\")]\nuse serde_json::json;\n#[cfg(feature = \"nostr\")]\nuse sha2::{Digest, Sha256};\n#[cfg(feature = \"nostr\")]\nuse std::collections::BTreeMap;\n#[cfg(feature = \"nostr\")]\nuse hex;\n#[cfg(feature = \"nostr\")]\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n let mut rng = thread_rng();\n let (max_signers, min_signers) = (3, 2);\n\n // 1. Setup Nostr Event Metadata\n let pubkey_hex = \"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"; // Example\n let created_at = 1712050000;\n let kind = 1;\n let content = \"Hello from ROAST threshold signatures!\";\n \n // 2. Serialize for Nostr ID (per NIP-01)\n let event_json = json!([\n 0,\n pubkey_hex,\n created_at,\n kind,\n [],\n content\n ]).to_string();\n \n let mut hasher = Sha256::new();\n hasher.update(event_json.as_bytes());\n let event_id = hasher.finalize(); // This 32-byte hash is our signing message\n\n // 3. FROST/ROAST Key Generation\n let (shares, pubkey_package) = frost::keys::generate_with_dealer(\n max_signers,\n min_signers,\n frost::keys::IdentifierList::Default,\n \u0026mut rng,\n )?;\n\n // 4. ROAST Coordination Simulation (Round 1: Commitments)\n // In ROAST, the coordinator keeps a \"session\" open and collects commitments\n let mut session_commitments = BTreeMap::new();\n let mut signer_nonces = BTreeMap::new();\n\n // Signers 1 and 3 respond first (Signer 2 is offline/slow)\n for \u0026id_val in \u0026[1, 3] {\n let id = frost::Identifier::try_from(id_val as u16)?;\n let (nonces, comms) = frost::round1::commit(shares[\u0026id].signing_share(), \u0026mut rng);\n session_commitments.insert(id, comms);\n signer_nonces.insert(id, nonces);\n }\n\n // 5. Round 2: Signing the Nostr ID\n let signing_package = frost::SigningPackage::new(session_commitments, \u0026event_id);\n let mut signature_shares = BTreeMap::new();\n\n for (id, nonces) in signer_nonces {\n let key_package: frost::keys::KeyPackage = shares[\u0026id].clone().try_into()?;\n let share = frost::round2::sign(\u0026signing_package, \u0026nonces, \u0026key_package)?;\n signature_shares.insert(id, share);\n }\n\n // 6. Aggregate into a BIP-340 Signature\n let group_signature = frost::aggregate(\n \u0026signing_package,\n \u0026signature_shares,\n \u0026pubkey_package,\n )?;\n\n // 7. Verification (using BIP-340 logic)\n pubkey_package.verifying_key().verify(\u0026event_id, \u0026group_signature)?;\n\n println!(\"Nostr Event ID: {}\", hex::encode(event_id));\n println!(\"Threshold Signature (BIP-340): {}\", hex::encode(group_signature.serialize()?));\n println!(\"Successfully signed Nostr event using ROAST/FROST!\");\n\n Ok(())\n}\n\n#[cfg(not(feature = \"nostr\"))]\nfn main() {\n println!(\"This example requires the 'nostr' feature. Please run with: cargo run --example frost_bip_340 --features nostr\");\n}\n",
"sig": "8fa32a6988da45b512aeb3ff4076c966bdeb7823866dbb2f728bf93b1bd2c0572b7efbb83aa0cf4e4aee9fa22b7de7779c930272b6a9702b06e9eaec7330ebc8"
}