bitcoinjs / bitcoinjs-lib

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

non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation) #2123

Closed Arunpandiaraja closed 6 days ago

Arunpandiaraja commented 2 weeks ago

Hi, i'm trying to broadcast a transaction using external signature which is ECDSA, transaction is getting signed, but getting this error while broadcasting non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation) , Have checked similar issues but not able to find any solution, Not sure what i'm missing here and raw transaction is huge as well, any help would be much appreciated, My code shown below.

Signature: cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c4cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc14

Signed Raw transaction hex: 02000000000118032d6e5ca1d7f0dd36426890d6f622d0455389584a3e64f891e862b0eabb81210000000000ffffffffb420c0c66431056ba49e75464410fecf6ab0b17b0a330aea42f9fa51198036fe0000000000ffffffffdf80bbb8a789dce8639492f3527172deac03896316edd89eb975d4d04458bf6b0100000000ffffffff8ba794099d320876c30ef491a10e41ebb7e26af4828c80e393f4e34b705759310000000000ffffffffeaf07eebbd7f19e18cdda014a174257e3144ca28fb97e8b5b488f2bf512dcdce0000000000ffffffffb3d3f3fb32e19f730141d3b7df85fd024baea6defb0305fe7fc498db9fffd2440100000000ffffffffe239b7ce80c1d08420e0936a0197a514bd440b60dfea5b14c6ea8153479878a10100000000ffffffff35f5d629f28973310a63cb1494ecff3d810b6d5f6fc8545deb293dd31b095b820000000000ffffffff94886826bb7dc5ea51ce074960f0e79824ae2d86f1da21be0cc5b57550751e960000000000ffffffffe435299e1220cd011b07a4e977cfb766d3d9c67e78d37f946e3dee0bcc98aea30000000000ffffffff6d93c92e95d9eb6053c81e13423102bc70ff597eb94f9b0b97809ad502aa41980100000000ffffffffb3d3f3fb32e19f730141d3b7df85fd024baea6defb0305fe7fc498db9fffd2440000000000fffffffff203c2769df469b5bd13b1b47b10aa4d532f157a2c743abbc2ab92b8b46c4ce50000000000ffffffff6d93c92e95d9eb6053c81e13423102bc70ff597eb94f9b0b97809ad502aa41980000000000ffffffffef51fa9034fd727ed27b1e36bbe9c4000a30b595c3e397091d83f3dd2090bcbc0000000000ffffffff01d25d0458d650f7e83213e3b22dead18780833139f687d2312d28b155085c0a0000000000ffffffff0058484f81c363f763c52f4b2afbe23118b3ae6136d45410b5dafaec23654e6a0000000000ffffffff605dd6ead625ea7e203ecb8cdbe77f1ad8583c15756d4e27e64e6a5de8d952e50000000000ffffffffec58484d921f058eabc627cfc7a3dded0757531f1abc92adb38dbf7783a4ab660000000000ffffffffb2ffe6af7b8d2ff91e22845dbc668c754ca4fee10a8e3f689139120672a308690000000000ffffffff4b2b01504980090436e5de9b7f4d27cb7b6ceb3e3f912f24cd62249be0bf4cbd0100000000ffffffff7fd78caa8d14416a6c223848b8f430b1f7710cc41abd74b6c5026fa95d12e1050000000000ffffffffae4ece7add902892e8bdb0c17eedd2e8db3f0146c5b91a07e1282e90c89124000000000000ffffffff505d218f34e06e7e1e225511c278190a518f28d201dc02800726a4bc210e61b00100000000ffffffff0100e1f50500000000160014b206974d3306ff4fc78acb60b7c0cbfb9c38da5e02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca02483045022100cafcd323d98b66c85002c197bc2fcc6856b5b6887ec8a63da87b3a92cd33a68c02204cb3e368d7695ef815324e0a0066e05256e846c3f08d414d449c54776270bc140121025a3946bdee01e3070fa9de8e706f6b8bb5b724066eae8c2a4d942d7253c67aca00000000

Screenshot 2024-07-02 at 1 39 59 PM

export const getSignedTxHashBtc = async (signature: string, txObject: any) => {
  const { fromAddress, utxos, outputs, feeRate, outputAmount } = txObject

  try {
    if (signature === '') {
      return ''
    }

    const pubKey = localStorageService.generatedPublicKey.get()
    const network = networks.testnet
    const psbt = new Psbt({ network })
    psbt.setVersion(2)
    psbt.setLocktime(0)

    const _signature = signature.slice(0, -2)
    const rawSignature = _signature
    const pubkeyBuffer = Buffer.from(pubKey ?? '', 'hex')
    const signatureBuffer = Buffer.from(rawSignature ?? '', 'hex')

    console.log(_signature, '_signature')

    const txHexes = await Promise.all(
      utxos.map(async (utxo: any) => {
        const txResponse = await getPrevTxHex({ txID: utxo.txid })
        return txResponse
      })
    )

    let inputSum = 0
    utxos.forEach((utxo: any, index: number) => {
      inputSum += utxo.value
      psbt.addInput({
        hash: utxo.txid,
        index: utxo.vout,
        nonWitnessUtxo: Buffer.from(txHexes[index], 'hex'),
        sequence: 4294967295,
      })
    })

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

    const changeAmount = inputSum - outputAmount - feeRate

    if (changeAmount > 0) {
      psbt.addOutput({
        address: fromAddress,
        value: changeAmount,
      })
    }

    const externalSigner: Signer = {
      publicKey: pubkeyBuffer,
      sign: () => signatureBuffer,
    }
    psbt.signAllInputs(externalSigner)
    psbt.finalizeAllInputs()

    const signedTxHex = psbt.extractTransaction(true).toHex()
    console.log('Signed transaction:', signedTxHex)

    const txId = await pushTx(signedTxHex)
    if (txId) {
      return txId
    } else {
      return null
    }
  } catch (error) {
    console.error('Error in getSignedTxHashBtc:', error)
    return null
  }
}
Arunpandiaraja commented 2 weeks ago

Hi @junderw can u help me with this?

acsonservice commented 2 weeks ago

The error you're encountering, "non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation)," typically arises from issues related to the format or structure of the transaction or the signatures themselves. Below, I will outline potential causes and solutions to address this error.

Potential Causes and Solutions

  1. Incorrect Signature Format:

    • Ensure the signatures are in DER format and correctly encoded.
  2. Incorrect Script:

    • Verify that the script and the script types match the inputs and outputs. Ensure that the signatures and public keys are correctly included in the scriptSig.
  3. Mismatched or Incomplete Inputs:

    • Ensure all inputs are correctly referenced and match the UTXOs.
  4. Sequence and Locktime:

    • The sequence numbers and locktime should be correctly set. You have set the sequence to 4294967295, which is generally acceptable, but ensure this is correct for your transaction type.
  5. Correct Handling of Non-Witness UTXOs:

    • Verify that the nonWitnessUtxo buffers are correctly formatted and correspond to the previous transactions.
  6. Signature Placeholder:

    • Ensure the signatures are correctly placed, and that no placeholder (like 0) is incorrectly interpreted as an invalid signature.

Revised Code

Here is a more detailed and slightly revised version of your code to address these potential issues:

import { Psbt, networks } from 'bitcoinjs-lib'
import { getPrevTxHex, pushTx } from './apiService'
import { localStorageService } from './localStorageService'

export const getSignedTxHashBtc = async (signature, txObject) => {
  const { fromAddress, utxos, outputs, feeRate, outputAmount } = txObject

  try {
    if (!signature) {
      throw new Error('Signature is empty')
    }

    const pubKey = localStorageService.generatedPublicKey.get()
    const network = networks.testnet
    const psbt = new Psbt({ network })
    psbt.setVersion(2)
    psbt.setLocktime(0)

    const rawSignature = signature.slice(0, -2)
    const pubkeyBuffer = Buffer.from(pubKey, 'hex')
    const signatureBuffer = Buffer.from(rawSignature, 'hex')

    // Fetch previous transaction hexes
    const txHexes = await Promise.all(
      utxos.map(async (utxo) => {
        const txResponse = await getPrevTxHex({ txID: utxo.txid })
        return txResponse
      })
    )

    // Add inputs
    let inputSum = 0
    utxos.forEach((utxo, index) => {
      inputSum += utxo.value
      psbt.addInput({
        hash: utxo.txid,
        index: utxo.vout,
        nonWitnessUtxo: Buffer.from(txHexes[index], 'hex'),
        sequence: 4294967295,
      })
    })

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

    // Add change output
    const changeAmount = inputSum - outputAmount - feeRate
    if (changeAmount > 0) {
      psbt.addOutput({
        address: fromAddress,
        value: changeAmount,
      })
    }

    // Define the external signer
    const externalSigner = {
      publicKey: pubkeyBuffer,
      sign: () => signatureBuffer,
    }

    // Sign all inputs
    psbt.signAllInputs(externalSigner)

    // Finalize all inputs
    psbt.finalizeAllInputs()

    // Extract the signed transaction
    const signedTxHex = psbt.extractTransaction(true).toHex()
    console.log('Signed transaction:', signedTxHex)

    // Broadcast the transaction
    const txId = await pushTx(signedTxHex)
    if (txId) {
      return txId
    } else {
      throw new Error('Transaction broadcast failed')
    }
  } catch (error) {
    console.error('Error in getSignedTxHashBtc:', error)
    return null
  }
}

Notes:

  1. Error Handling:

    • Improved error handling to provide more informative error messages.
  2. Ensure Signature Buffer:

    • Check the conversion of signatureBuffer to ensure it matches the expected DER format.
  3. Transaction Fees:

    • Ensure the feeRate and outputAmount calculations are correct to avoid any discrepancies.
  4. Public Key and Signature Extraction:

    • Verify the pubKey and signature extracted from local storage or other sources are correctly formatted and valid.

If the error persists, you may need to delve deeper into the specifics of the transaction and the signature to ensure everything aligns correctly with Bitcoin's transaction rules and the script validation logic.