The Cryptonomics™
  • Home
  • Blockchain
  • Bitcoin
  • Ethereum
  • NFTS
  • Altcoin
  • Mining
  • Consulting
Reading: taproot – Musig signature validation mismatch
Share
Please enter CoinGecko Free Api Key to get this plugin works.
The Cryptonomics™The Cryptonomics™
Font ResizerAa
Search
  • Home
  • Blockchain
  • Bitcoin
  • Ethereum
  • NFTS
  • Altcoin
  • Mining
  • Consulting
Follow US
  • About Us
  • Advertising Solutions
  • Privacy
  • Terms
  • Advertise
Copyright © MetaMedia™ Capital Inc, All right reserved
The Cryptonomics™ > Bitcoin > taproot – Musig signature validation mismatch
Bitcoin

taproot – Musig signature validation mismatch

admin
Last updated: September 4, 2024 11:10 pm
admin Published September 4, 2024
Share
taproot – Musig signature validation mismatch


I am attempting to grasp the primitives of Taproot and Musig2 so I can implement them into my software. I am utilizing the rust-bitcoin library and have written a script to create a Taproot tackle from an aggregated Musig2 public key (i’ve funded it with sats on mutinynet), create a tx spending from it & signal it, confirm the signature, after which spend the tx. Nevertheless, I am getting the error mandatory-script-verify-flag-failed (Invalid Schnorr signature) when I attempt to spend the tx. The odd factor is that the in-script sig validation throws no points.

Right here is the related code:


use bitcoin::consensus::Encodable;

use bitcoin::hashes::Hash;
use bitcoin::key::{Keypair, UntweakedPublicKey};
use bitcoin::locktime::absolute;

use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Verification};
use bitcoin::sighash::{Prevouts, SighashCache, TapSighashType};
use bitcoin::{
    transaction, Tackle, Quantity, Community, OutPoint, PublicKey, ScriptBuf, Sequence, Transaction,
    TxIn, TxOut, Txid, Witness, XOnlyPublicKey,
};
use musig2::secp::Level;
use musig2::{
    aggregate_partial_signatures, sign_partial, verify_single, AggNonce, BinaryEncoding,
    KeyAggContext, PubNonce, SecNonce,
};
use rand::thread_rng;

use std::str::FromStr;

const DUMMY_UTXO_AMOUNT: u64 = 100000;
const SPEND_AMOUNT: u64 = 90000;
const SK: &str = "e504905952697837ac0f0dc9e46deb0340ae6468b185ba79e4fa96ebda3964c2";
const SK2: &str = "6bb7bc5dd0c9db0ca8bdba4616ee92afceb432551e0f23b32ea7bcada18da696";
const SK3: &str = "57d44ecb8812680e871732233ed8e4d4e11a25f22aef6c81dbe35a424ad8db41";
const TXID: &str = "726499fb478422087e7a89a04f25de5ed98e87c9881e4a264e8f31143cdee17b";
const VOUT: u32 = 1;

fn principal() {
    let keypair_array = vec![
        Keypair::from_secret_key(&Secp256k1::new(), &SecretKey::from_str(SK).unwrap()),
        Keypair::from_secret_key(&Secp256k1::new(), &SecretKey::from_str(SK2).unwrap()),
        Keypair::from_secret_key(&Secp256k1::new(), &SecretKey::from_str(SK3).unwrap()),
    ];

    let begin = std::time::Prompt::now();
    let tx = construct_refund_transaction(
        TXID,
        VOUT,
        DUMMY_UTXO_AMOUNT,
        SPEND_AMOUNT,
        "tb1p6e4q26tvyc7dmxfnk5zzhkux9d6pqpn0k5qxlw4gvc90unc0m3rq93a0tl",
        keypair_array,
    );
    let length = begin.elapsed();

    println!("Time taken to assemble refund transaction: {:?}", length);

    match tx {
        Okay(hex) => {
            post_tx(hex);
        }
        Err(e) => {
            println!("Error setting up transaction: {:?}", e);
        }
    }
}

pub fn construct_refund_transaction(
    from_txid: &str,
    from_vout: u32,
    from_amount: u64,
    spend_amount: u64,
    to_address: &str,
    keypair_array: Vec,
) -> Consequence> {
    // Step 1: Convert public keys to factors for MuSig2
    let points_vec: Vec = keypair_array
        .iter()
        .map(|keypair| Level::from_hex(&keypair.public_key().to_string()).unwrap())
        .acquire();

    // Step 2: Create a KeyAggContext for MuSig2
    let context = KeyAggContext::new(points_vec).unwrap();

    // Step 3: Get the aggregated public key
    let agg_pubkey: [u8; 33] = context.aggregated_pubkey();
    // println!("Combination pubkey: {:?}", hex::encode(agg_pubkey));

    // Step 4: Create a P2TR tackle from the aggregated public key
    let pubkey = PublicKey::from_slice(&agg_pubkey).unwrap();
    let xonly_pubkey = XOnlyPublicKey::from(pubkey);
    let secp = Secp256k1::new();
    // let tackle = Tackle::p2tr(&secp, xonly_pubkey, None, Community::Signet);
    // println!("P2TR tackle: {}", tackle);

    // Step 5: Get the unspent transaction output (UTXO)
    let (out_point, utxo) = unspent_transaction_output(
        &secp,
        xonly_pubkey,
        Txid::from_str(from_txid).unwrap(),
        from_vout,
        from_amount,
    );

    // Step 6: Get the receiver's tackle
    let tackle = receivers_address(to_address);

    // Step 7: Create the transaction enter
    let enter = TxIn {
        previous_output: out_point,
        script_sig: ScriptBuf::default(), // Empty for P2TR
        sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
        witness: Witness::default(), // Will probably be stuffed after signing
    };

    // Step 8: Create the transaction output
    let spend = TxOut {
        worth: Quantity::from_sat(spend_amount),
        script_pubkey: tackle.script_pubkey(),
    };

    // Step 9: Assemble the unsigned transaction
    let mut unsigned_tx = Transaction {
        model: transaction::Model::TWO,
        lock_time: absolute::LockTime::ZERO,
        enter: vec![input],
        output: vec![spend],
    };
    let input_index = 0;

    // Step 10: Put together for signing
    let sighash_type = TapSighashType::Default;
    let prevouts = vec![utxo];
    let prevouts = Prevouts::All(&prevouts);

    // Step 11: Calculate the sighash
    let mut sighasher = SighashCache::new(&mut unsigned_tx);
    let sighash = sighasher
        .taproot_key_spend_signature_hash(input_index, &prevouts, sighash_type)
        .anticipate("didn't assemble sighash");

    // Step 12: Put together the message to be signed
    let msg = Message::from_digest(sighash.to_byte_array());

    // Step 13: Generate random nonces for MuSig2
    let mut rng = thread_rng();
    let n = keypair_array.len();
    let secret_nonces: Vec = (0..n).map(|_| SecNonce::random(&mut rng)).acquire();
    let public_nonces: Vec = secret_nonces.iter().map(|sn| sn.public_nonce()).acquire();

    // Step 14: Combination nonces
    let agg_nonce = AggNonce::sum(&public_nonces);

    // Step 15: Generate partial signatures
    let mut partial_sigs = Vec::new();
    for (i, keypair) in keypair_array.iter().enumerate() {
        let seckey =
            musig2::secp::Scalar::from_slice(&keypair.secret_key().secret_bytes()).unwrap();
        let partial_sig: musig2::PartialSignature = sign_partial(
            &context,
            seckey,
            secret_nonces[i].clone(),
            &agg_nonce,
            msg.as_ref(),
        )
        .unwrap();
        partial_sigs.push(partial_sig);
        // println!("Partial signature {}: {:?}", i, partial_sig);
    }

    // Step 16: Combination partial signatures
    let aggregated_signature: musig2::CompactSignature =
        aggregate_partial_signatures(&context, &agg_nonce, partial_sigs, msg.as_ref()).unwrap();
    // println!("Aggregated signature: {:?}", aggregated_signature);

    // Step 17: Confirm the aggregated signature
    let verification_result = verify_single(
        Level::from_slice(&agg_pubkey).unwrap(),
        aggregated_signature,
        msg.as_ref(),
    );

    match verification_result {
        Okay(()) => println!("Signature is legitimate"),
        Err(e) => println!("Signature verification failed: {:?}", e),
    }

    // Step 18: Add the signature to the transaction witness
    sighasher
        .witness_mut(input_index)
        .unwrap()
        .push(&aggregated_signature.to_bytes());

    // Step 19: Finalize the signed transaction
    let tx = sighasher.into_transaction();

    // Step 20: Serialize and print the signed transaction
    let mut serialized = Vec::new();
    tx.consensus_encode(&mut serialized).unwrap();
    let hex = serialized.as_hex();
    // println!("Transaction hex: {}", hex);

    Okay(hex.to_string())
}

// Helper operate to parse and validate the receiver's tackle
fn receivers_address(tackle: &str) -> Tackle {
    tackle
        .parse::
>() .anticipate("a sound tackle") .require_network(Community::Signet) .anticipate("legitimate tackle for signet") } // Helper operate to create an unspent transaction output (UTXO) fn unspent_transaction_output( secp: &Secp256k1, internal_key: UntweakedPublicKey, txid: Txid, vout: u32, quantity: u64, ) -> (OutPoint, TxOut) { let script_pubkey = ScriptBuf::new_p2tr(secp, internal_key, None); let out_point = OutPoint { txid, vout }; let utxo = TxOut { worth: Quantity::from_sat(quantity), script_pubkey, }; (out_point, utxo) } fn post_tx(hex: String) { let consumer = reqwest::blocking::Consumer::new(); let res = consumer.publish("https://mutinynet.com/api/tx").physique(hex).ship(); match res { Okay(res) => { if res.standing().is_success() { println!("Response: {:?}", res.textual content()); } else { let error_text = res.textual content().unwrap_or_default(); if let Some(message) = error_text.break up("message":"").nth(1) { if let Some(error_message) = message.break up(""").subsequent() { println!("Error: {}", error_message); } else { println!("Error: {}", error_text); } } else { println!("Error: {}", error_text); } } } Err(e) => { println!("Error sending request: {:?}", e); } } }

Am I doing something blatantly incorrect?

(these are dummy SKs I generated for the aim of this take a look at)

You Might Also Like

Blue Chip NFTs By no means Died

Groundbreaking: China to Pay Curiosity on Digital Yuan Deposits in Adoption Push

Bitcoin Analysts Say This Should Occur for BTC Worth to Break $90K

Maker price program replace: optimizing liquidity incentives

Will Ethereum Hit $3K Following Tom Lee’s $1B ETH Stake?

Share This Article
Facebook Twitter Email Copy Link Print
Previous Article Assist! My mother and father are hooked on Pi Community crypto tapper Assist! My mother and father are hooked on Pi Community crypto tapper
Next Article Apache Kafka use circumstances: Driving innovation throughout various industries Apache Kafka use circumstances: Driving innovation throughout various industries
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Subscribe to our newslettern

Get Newest Articles Instantly!

- Advertisement -
Ad imageAd image
Popular News
Ether Value Rallied 120% the Final Time Staking Queue Flipped Exit Queue
Ether Value Rallied 120% the Final Time Staking Queue Flipped Exit Queue
The journey to a mature asset administration system
The journey to a mature asset administration system
High 3 Meme Coin Gems Price Shopping for Earlier than Could 2024 – PEPE, WIF, and DOGEVERSE
High 3 Meme Coin Gems Price Shopping for Earlier than Could 2024 – PEPE, WIF, and DOGEVERSE

Follow Us on Socials

We use social media to react to breaking news, update supporters and share information

Facebook Instagram Linkedin Pinterest Tiktok Twitter Youtube
The Cryptonomics™

Cryptonomics Magazine is your premier digital source for blockchain insights, offering cutting-edge research, news, interviews, and ICO updates for everyone from entrepreneurs to institutions. We drive blockchain knowledge and growth.

Subscribe to our newsletter

Always Stay Up to Date

Subscribe to our newsletter to get our newest articles instantly!

Ether Value Rallied 120% the Final Time Staking Queue Flipped Exit Queue
December 30, 2025
Bitmain simply slashed mining rig costs, proving the market’s oldest “Bitcoin rule” is formally lifeless
December 30, 2025
Ethereum Value Momentum Rolls Over, Bearish Transfer Warning
December 30, 2025
Trump’s NFTs Defy The NFT Winter
December 30, 2025
Blue Chip NFTs By no means Died
December 30, 2025
Copyright © The Cryptonomics™ , All right reserved
  • About Us
  • Advertising Solutions
  • Privacy
  • Terms
  • Advertise
Join Us!

Subscribe & Stay Ahead of the Curve with Cryptonomics !

Zero spam, Unsubscribe at any time.
Welcome Back!

Sign in to your account

Lost your password?