<p>I'm experimenting with creating and sending Taproot transactions programmatically and encountered an issue with the Schnorr signature. I'm trying to send simple transaction with one V1_P2TR input and one V1_P2TR output, using what I understand to be a key path spend approach, without any scripts. However, when I attempt to send the transaction, my node rejects it with the error:
<code>mandatory-script-verify-flag-failed (Invalid Schnorr signature)</code></p>
<p>I'm using the following dependencies in my Rust project:</p>
<pre><code>bitcoin = { version = &quot;0.30.1&quot;, features = [&quot;rand&quot;] }
ord-bitcoincore-rpc = &quot;0.17.1&quot;   # (a forked version of bitcoincore-rpc, though I believe this detail is not crucial to the issue).

</code></pre>
<p>Here's the relevant part of my code:</p>
<pre><code>fn create_and_send_tmp_tx(client: &amp;Client, utxo: &amp;ListUnspentResultEntry, fee_rate: f64, key_pair: &amp;UntweakedKeyPair, address_to: &amp;Address) -&gt; Result&lt;Txid&gt; {

    let secp256k1 = Secp256k1::new();

    // Verifying that UTXO can be spent by provided key pair
    let (public_key, _parity) = XOnlyPublicKey::from_keypair(&amp;key_pair);
    let address = Address::p2tr(&amp;secp256k1, public_key, None, Network::Bitcoin);
    let address_script_pubkey = address.script_pubkey();
    let utxo_script_pubkey = utxo.script_pub_key.clone();
    if ! (address_script_pubkey == utxo_script_pubkey) {
        bail!(&quot;Can't spend utxo&quot;);
    }

    let mut tx = Transaction {
        input: vec![TxIn {
            previous_output: OutPoint {
                txid: utxo.txid,
                vout: utxo.vout,
            },
            script_sig: Builder::new().into_script(),
            witness: Witness::new(),
            sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
        }],
        output: vec![TxOut {
            script_pubkey: address_to.script_pubkey(),
            value: 0,   // tmp value for estimation
        }],
        lock_time: LockTime::ZERO,
        version: 2,
    };

    tx.input[0].witness.push(
        Signature::from_slice(&amp;[0; SCHNORR_SIGNATURE_SIZE])
            .unwrap()
            .to_vec(),
    );

    let fee = Amount::from_sat((fee_rate * tx.vsize()).round() as u64);
    info!(&quot;Fee: {:?}&quot;, fee.to_sat());
    tx.output[0].value = utxo.amount.to_sat() - fee.to_sat();
    tx.input[0].witness.clear();

    let prevouts = vec![TxOut {script_pubkey: utxo.script_pub_key.clone(), value: utxo.amount.to_sat()}];

    let mut sighash_cache = SighashCache::new(&amp;tx);
    let sighash = sighash_cache.taproot_key_spend_signature_hash(
        0,
        &amp;Prevouts::All(&amp;prevouts),
        TapSighashType::Default,
    ).expect(&quot;Failed to compute sighash&quot;);

    let msg = secp256k1::Message::from_slice(sighash.as_ref()).expect(&quot;should be cryptographically secure hash&quot;);
    let sig = secp256k1.sign_schnorr(&amp;msg, &amp;key_pair);

    tx.input[0].witness.push(
        Signature {
            sig,
            hash_ty: TapSighashType::Default,
        }
            .to_vec(),
    );

    let signed_tx_bytes = consensus::encode::serialize(&amp;tx);
    // let signed_tx_bytes = client.sign_raw_transaction_with_wallet(&amp;tx, None, None)?.hex;

    let txid = match client.send_raw_transaction(&amp;signed_tx_bytes) {
        Ok(txid) =&gt; txid,
        Err(err) =&gt; {
            return Err(anyhow!(&quot;Failed to send transaction: {err}\n&quot;))
        }
    };
    info!(&quot;Tx sent: {:?}&quot;, txid);
    Ok(txid)
}
</code></pre>
<p>The wallet I'm using is connected to my node. If I use the
<code>client.sign_raw_transaction_with_wallet(&amp;tx, None, None)?.hex;</code> function, which allows the node to replace my witness with a correct one, the transaction is accepted. This suggests that my inputs and outputs are constructed correctly, and the issue likely lies with how I'm generating the signature.</p>
<p>I've successfully sent transactions using a script path spend with a similar approach, using <code>sighash_cache.taproot_script_spend_signature_hash</code>, without the node's intervention in signing.</p>
<p>Could someone help me identify what I'm doing wrong?</p>

CC BY-SA 4.0
2024-03-01T11:31:49.590Z
Have you looked at this example it might be helpful? https://github.com/rust-bitcoin/rust-bitcoin/blob/master/bitcoin/examples/sign-tx-taproot.rs I am having similar issues to you as well by the way...
qdrs
CC BY-SA 4.0
2024-03-01T14:29:11.170Z
Thanks, the problem was that I was using an untweaked key pair for signing.
`let sig = secp256k1.sign_schnorr(&msg, &key_pair.tap_tweak(&secp256k1, None).to_inner());`
fixed the issue.
Skwern
<signature><taproot><p2tr>
Issue with constructing a correct Schnorr Signature for a Taproot transaction
Skwern
# Issue with constructing a correct Schnorr Signature for a Taproot transaction

(Asked by: username_0 on 2024-03-01)

I'm experimenting with creating and sending Taproot transactions programmatically and encountered an issue with the Schnorr signature. I'm trying to send simple transaction with one V1\_P2TR input and one V1\_P2TR output, using what I understand to be a key path spend approach, without any scripts. However, when I attempt to send the transaction, my node rejects it with the error:
`mandatory-script-verify-flag-failed (Invalid Schnorr signature)`

I'm using the following dependencies in my Rust project:

```
bitcoin = { version = "0.30.1", features = ["rand"] }
ord-bitcoincore-rpc = "0.17.1"   # (a forked version of bitcoincore-rpc, though I believe this detail is not crucial to the issue).

```

Here's the relevant part of my code:

```
fn create_and_send_tmp_tx(client: &Client, utxo: &ListUnspentResultEntry, fee_rate: f64, key_pair: &UntweakedKeyPair, address_to: &Address) -> Result<Txid> {

    let secp256k1 = Secp256k1::new();

    // Verifying that UTXO can be spent by provided key pair
    let (public_key, _parity) = XOnlyPublicKey::from_keypair(&key_pair);
    let address = Address::p2tr(&secp256k1, public_key, None, Network::Bitcoin);
    let address_script_pubkey = address.script_pubkey();
    let utxo_script_pubkey = utxo.script_pub_key.clone();
    if ! (address_script_pubkey == utxo_script_pubkey) {
        bail!("Can't spend utxo");
    }

    let mut tx = Transaction {
        input: vec![TxIn {
            previous_output: OutPoint {
                txid: utxo.txid,
                vout: utxo.vout,
            },
            script_sig: Builder::new().into_script(),
            witness: Witness::new(),
            sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
        }],
        output: vec![TxOut {
            script_pubkey: address_to.script_pubkey(),
            value: 0,   // tmp value for estimation
        }],
        lock_time: LockTime::ZERO,
        version: 2,
    };

    tx.input[0].witness.push(
        Signature::from_slice(&[0; SCHNORR_SIGNATURE_SIZE])
            .unwrap()
            .to_vec(),
    );

    let fee = Amount::from_sat((fee_rate * tx.vsize()).round() as u64);
    info!("Fee: {:?}", fee.to_sat());
    tx.output[0].value = utxo.amount.to_sat() - fee.to_sat();
    tx.input[0].witness.clear();

    let prevouts = vec![TxOut {script_pubkey: utxo.script_pub_key.clone(), value: utxo.amount.to_sat()}];

    let mut sighash_cache = SighashCache::new(&tx);
    let sighash = sighash_cache.taproot_key_spend_signature_hash(
        0,
        &Prevouts::All(&prevouts),
        TapSighashType::Default,
    ).expect("Failed to compute sighash");

    let msg = secp256k1::Message::from_slice(sighash.as_ref()).expect("should be cryptographically secure hash");
    let sig = secp256k1.sign_schnorr(&msg, &key_pair);

    tx.input[0].witness.push(
        Signature {
            sig,
            hash_ty: TapSighashType::Default,
        }
            .to_vec(),
    );

    let signed_tx_bytes = consensus::encode::serialize(&tx);
    // let signed_tx_bytes = client.sign_raw_transaction_with_wallet(&tx, None, None)?.hex;

    let txid = match client.send_raw_transaction(&signed_tx_bytes) {
        Ok(txid) => txid,
        Err(err) => {
            return Err(anyhow!("Failed to send transaction: {err}\n"))
        }
    };
    info!("Tx sent: {:?}", txid);
    Ok(txid)
}

```

The wallet I'm using is connected to my node. If I use the
`client.sign_raw_transaction_with_wallet(&tx, None, None)?.hex;` function, which allows the node to replace my witness with a correct one, the transaction is accepted. This suggests that my inputs and outputs are constructed correctly, and the issue likely lies with how I'm generating the signature.

I've successfully sent transactions using a script path spend with a similar approach, using `sighash_cache.taproot_script_spend_signature_hash`, without the node's intervention in signing.

Could someone help me identify what I'm doing wrong?

**Tags:** signature, taproot, p2tr

> **Comments:**
> **username_1**: Have you looked at this example it might be helpful? https://github.com/rust-bitcoin/rust-bitcoin/blob/master/bitcoin/examples/sign-tx-taproot.rs I am having similar issues to you as well by the way...  
> **username_0**: Thanks, the problem was that I was using an untweaked key pair for signing. `let sig = secp256k1.sign_schnorr(&msg, &key_pair.tap_tweak(&secp256k1, None).to_inner());` fixed the issue.  

---

## 0 answers

