bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.62k stars 2.09k forks source link

Getting non-mandatory-script-verify-flag (Invalid Schnorr signature) on taproot key path spending with LEDGER nano #2115

Closed pxr64 closed 1 month ago

pxr64 commented 1 month ago

This is the input that im trying to sign using ledger nano S+

{
  witnessUtxo: {
    script: new Uint8Array([81, 32, 118, 86, 80, 148, 230, 79, 149, 84, 93, 7, 31, 137, 56, 158, 32, 95, 128, 54, 25, 97, 180, 102, 213, 25, 39, 51, 123, 126, 112, 19, 110, 155]),
    value: 546,
  },
  tapKeySig: new Uint8Array([202, 15, 138, 195, 101, 213, 113, 75, 209, 251, 28, 109, 58, 117, 14, 187, 241, 103, 150, 57, 238, 198, 36, 174, 84, 91, 97, 60, 189, 77, 182, 42, 149, 114, 123, 73, 116, 49, 123, 89, 232, 156, 187, 12, 240, 191, 186, 131, 151, 12, 192, 105, 12, 40, 11, 51, 117, 85, 181, 122, 92, 116, 234, 126]),
  tapBip32Derivation: [
    {
      masterFingerprint: new Uint8Array([195, 83, 11, 14]),
      pubkey: new Uint8Array([202, 47, 78, 113, 83, 233, 132, 5, 55, 191, 243, 245, 58, 134, 33, 9, 247, 86, 243, 5, 194, 232, 55, 214, 212, 132, 164, 4, 221, 28, 78, 26]),
      path: "m/86'/0'/0'/0/0",
      leafHashes: [
      ],
    },
  ],
  tapInternalKey: new Uint8Array([202, 47, 78, 113, 83, 233, 132, 5, 55, 191, 243, 245, 58, 134, 33, 9, 247, 86, 243, 5, 194, 232, 55, 214, 212, 132, 164, 4, 221, 28, 78, 26]),
}

Does anybody have an ideea of what I am missing?

NVM it was an issue on xverse side

acsonservice commented 3 weeks ago

It looks like you were able to identify that the issue was on Xverse's side. However, for the sake of completeness and to help anyone else who might encounter a similar problem, here's a detailed look at what could cause the error non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation) and how to resolve it.

Understanding the Error The error non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation) typically occurs when the script verification process fails due to an incorrect or improperly formatted signature. This can be due to several reasons:

Incorrect Signature Format: The signature might not be in the correct format expected by the script. Incorrect Data Signed: The data that was signed might not match the expected input for the signature verification. Non-standard Transactions: Some wallets or tools might create non-standard transactions which some nodes reject. Steps to Diagnose and Fix Check Signature Encoding: Ensure that the signature is DER-encoded and follows the correct format. For ECDSA, the signature should be in the form r || s || v.

Verify Data Signed: Ensure that the exact data being signed is correct and matches the transaction inputs and outputs exactly. This includes checking the transaction's hash and making sure the inputs match the UTXOs being spent.

Use Proper Transaction Library: Use a well-maintained library to create and sign the transaction. This helps to avoid mistakes related to transaction structure and signature encoding. For Bitcoin, bitcoinjs-lib is a popular choice.

Ensure Compatibility: Make sure that all tools and libraries you are using are compatible with each other, especially when dealing with different Bitcoin implementations or testnets.

Example with bitcoinjs-lib Here's a sample code snippet using bitcoinjs-lib to create and sign a transaction:

javascript Copy code const bitcoin = require('bitcoinjs-lib');

// Assuming the following variables are set const network = bitcoin.networks.testnet; // or bitcoin.networks.bitcoin for mainnet const psbt = new bitcoin.Psbt({ network });

const pubKeyBuffer = Buffer.from(pubKey, 'hex'); const signatureBuffer = Buffer.from(signature, 'hex');

// Adding inputs utxos.forEach((utxo, index) => { psbt.addInput({ hash: utxo.txid, index: utxo.vout, witnessUtxo: { script: Buffer.from(utxo.script, 'hex'), value: utxo.value, }, tapBip32Derivation: utxo.tapBip32Derivation, tapInternalKey: Buffer.from(utxo.tapInternalKey, 'hex'), tapKeySig: Buffer.from(utxo.tapKeySig, 'hex'), }); });

// Adding outputs outputs.forEach((output) => { psbt.addOutput({ address: output.address, value: output.amount, }); });

// Signing inputs psbt.signAllInputsHD({ publicKey: pubKeyBuffer, sign: () => signatureBuffer, });

psbt.finalizeAllInputs();

const signedTx = psbt.extractTransaction().toHex(); console.log('Signed transaction:', signedTx); Important Notes SegWit and Taproot Transactions: Ensure that you handle SegWit and Taproot transactions correctly. The input structure and signing process might differ from legacy transactions.

Testing: Always test your transaction on a testnet before broadcasting on the mainnet to avoid potential loss of funds.

Conclusion Handling and debugging Bitcoin transactions can be complex, especially with advanced features like SegWit and Taproot. By ensuring that signatures are correctly formatted, transactions are properly constructed, and using reliable libraries, many common issues can be avoided. If an issue persists, it might be due to the specific implementation of the wallet or tool being used, as was the case with Xverse in your situation.