mrtnetwork / bitcoin

a comprehensive and versatile Go library for all your Bitcoin transaction needs. offers robust support for various Bitcoin transaction types, including spending transactions, Bitcoin address management, Bitcoin Schnorr signatures, BIP-39 mnemonic phrase generation, hierarchical deterministic (HD) wallet derivation, and Secret Storage Definition
BSD 3-Clause "New" or "Revised" License
28 stars 7 forks source link

An error occurs when I enter multiple input signatures #3

Closed yplong closed 10 months ago

yplong commented 10 months ago

How do I sign when I enter multiple input signatures? I made the following attempts:

  1. When I tried to adjust the number of signatures, an error {"code":-26,"message":" non-mandatory-script-verification-flag (Invalid Schnorr signature)"} was reported,
  2. If I only sign once, {"code":-22,"message":"TX decode failed. Make sure the tx has at least one input."} This error is reported.
  3. I adjusted the sighash GetTransactionTaprootDigest method the value of the parameter, SignTaprootTransaction sighash parameters value, or will be signed at the wrong mistake
// Private key of the UTXO owner
    privateKey, _ := keypair.NewECPrivateFromWIF("91qdf1RH6UMZwZTnHUJ3RSiGSZ5EAxvW2FgYFV2HeaLnqZQh4zx")
    // privateKey, _ := keypair.NewECPrivateFromWIF("cUUUyKDSNuDFtAt8deeSHxjHY5p7M9mvBfp32EFxz62H91WFiX9J")

    // Address we want to spend from
    fromAddr := privateKey.GetPublic().ToTaprootAddress()

    // Create an input
    // Insert transaction ID and index of UTXO
    sigTxin1 := scripts.NewTxInput("0924c8ad1b635f7edfb77321f43ba868ae4d71b9976d639fa6a582b4a7bc4714", 0) //500
    sigTxin2 := scripts.NewTxInput("ab03d8f71103ffe1e346ca65635cdad84dcd658e58c3b3ddd529f777c57ec848", 1) //1000

    // Address we want to send funds to
    // addr, _ := btcAddress.P2PKHAddressFromAddress("n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR")
    addr, _ := btcAddress.P2TRAddressFromAddress("tb1pnztcay5kzpyxcfhfmpth4f32re964zupd3k2f8g53rmcnp9kyz9qjukvm2")

    // Create an output: Send 3000 to `n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR`
    txout := scripts.NewTxOutput(
        big.NewInt(500),
        addr.ToScriptPubKey(),
    )

    changeOut := scripts.NewTxOutput(
        big.NewInt(100),
        fromAddr.ToScriptPubKey(),
    )

    // Create a transaction
    tx := scripts.NewBtcTransaction(
        []*scripts.TxInput{sigTxin1, sigTxin2},
        []*scripts.TxOutput{txout, changeOut},
        // []*scripts.TxOutput{txout},
        true, // The transaction contains one or more segwit UTXOs
    )

    // Get the transaction digest for signing input at index 0
    // Arguments:
    // - Index 0
    // - The scriptPubkeys that correspond to all the inputs/UTXOs
    // - The amounts that correspond to all the inputs/UTXOs
    // - Ext_flag: Extension mechanism, default is 0; 1 is for script spending (BIP342)
    // - Script: The script that we are spending when ext_flag is 1
    digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
    digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
    // digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(3000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

    // Sign the transaction
    // Arguments:
    // - Transaction digest related to the index
    // - Signature Hash Type (TAPROOT_SIGHASH_ALL)
    // - Script path (tapleafs)
    // - Tweak: Note that we don't use tapleafs script in this transaction, so the tweak should be set to False
    sig := privateKey.SignTaprootTransaction(digest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true)
    sig2 := privateKey.SignTaprootTransaction(digest2, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true)
    // sig := privateKey.SignTaprootTransaction(digest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true) // set to tue

    // Create a witness signature and set it to the transaction at the current index
    witness := scripts.NewTxWitnessInput(sig)
    // witnessOut := scripts.NewTxWitness(sig)
    tx.Witnesses = append(tx.Witnesses, witness)

    witness2 := scripts.NewTxWitnessInput(sig2)
    tx.Witnesses = append(tx.Witnesses, witness2)

    // Transaction ID
    tx.TxId()

    // In this case, the transaction is segwit, and we must use GetVSize for transaction size
    tx.GetVSize()

    // Transaction digest ready for broadcast
    digestHex := tx.Serialize()

    api := provider.SelectApi(provider.MempoolApi, &address.TestnetNetwork)
    trId, err := api.SendRawTransaction(digestHex)
    if err != nil {
        fmt.Println("err:", err)
    }
    fmt.Println("trid:", trId)
mrtnetwork commented 10 months ago

How do I sign when I enter multiple input signatures? I made the following attempts:

  1. When I tried to adjust the number of signatures, an error {"code":-26,"message":" non-mandatory-script-verification-flag (Invalid Schnorr signature)"} was reported,
  2. If I only sign once, {"code":-22,"message":"TX decode failed. Make sure the tx has at least one input."} This error is reported.
  3. I adjusted the sighash GetTransactionTaprootDigest method the value of the parameter, SignTaprootTransaction sighash parameters value, or will be signed at the wrong mistake
// Private key of the UTXO owner
  privateKey, _ := keypair.NewECPrivateFromWIF("91qdf1RH6UMZwZTnHUJ3RSiGSZ5EAxvW2FgYFV2HeaLnqZQh4zx")
  // privateKey, _ := keypair.NewECPrivateFromWIF("cUUUyKDSNuDFtAt8deeSHxjHY5p7M9mvBfp32EFxz62H91WFiX9J")

  // Address we want to spend from
  fromAddr := privateKey.GetPublic().ToTaprootAddress()

  // Create an input
  // Insert transaction ID and index of UTXO
  sigTxin1 := scripts.NewTxInput("0924c8ad1b635f7edfb77321f43ba868ae4d71b9976d639fa6a582b4a7bc4714", 0) //500
  sigTxin2 := scripts.NewTxInput("ab03d8f71103ffe1e346ca65635cdad84dcd658e58c3b3ddd529f777c57ec848", 1) //1000

  // Address we want to send funds to
  // addr, _ := btcAddress.P2PKHAddressFromAddress("n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR")
  addr, _ := btcAddress.P2TRAddressFromAddress("tb1pnztcay5kzpyxcfhfmpth4f32re964zupd3k2f8g53rmcnp9kyz9qjukvm2")

  // Create an output: Send 3000 to `n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR`
  txout := scripts.NewTxOutput(
      big.NewInt(500),
      addr.ToScriptPubKey(),
  )

  changeOut := scripts.NewTxOutput(
      big.NewInt(100),
      fromAddr.ToScriptPubKey(),
  )

  // Create a transaction
  tx := scripts.NewBtcTransaction(
      []*scripts.TxInput{sigTxin1, sigTxin2},
      []*scripts.TxOutput{txout, changeOut},
      // []*scripts.TxOutput{txout},
      true, // The transaction contains one or more segwit UTXOs
  )

  // Get the transaction digest for signing input at index 0
  // Arguments:
  // - Index 0
  // - The scriptPubkeys that correspond to all the inputs/UTXOs
  // - The amounts that correspond to all the inputs/UTXOs
  // - Ext_flag: Extension mechanism, default is 0; 1 is for script spending (BIP342)
  // - Script: The script that we are spending when ext_flag is 1
  digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
  digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
  // digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(3000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

  // Sign the transaction
  // Arguments:
  // - Transaction digest related to the index
  // - Signature Hash Type (TAPROOT_SIGHASH_ALL)
  // - Script path (tapleafs)
  // - Tweak: Note that we don't use tapleafs script in this transaction, so the tweak should be set to False
  sig := privateKey.SignTaprootTransaction(digest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true)
  sig2 := privateKey.SignTaprootTransaction(digest2, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true)
  // sig := privateKey.SignTaprootTransaction(digest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true) // set to tue

  // Create a witness signature and set it to the transaction at the current index
  witness := scripts.NewTxWitnessInput(sig)
  // witnessOut := scripts.NewTxWitness(sig)
  tx.Witnesses = append(tx.Witnesses, witness)

  witness2 := scripts.NewTxWitnessInput(sig2)
  tx.Witnesses = append(tx.Witnesses, witness2)

  // Transaction ID
  tx.TxId()

  // In this case, the transaction is segwit, and we must use GetVSize for transaction size
  tx.GetVSize()

  // Transaction digest ready for broadcast
  digestHex := tx.Serialize()

  api := provider.SelectApi(provider.MempoolApi, &address.TestnetNetwork)
  trId, err := api.SendRawTransaction(digestHex)
  if err != nil {
      fmt.Println("err:", err)
  }
  fmt.Println("trid:", trId)

There are 2 problems in this transaction 1 - This sigTxin1 := scripts.NewTxInput("0924c8ad1b635f7edfb77321f43ba868ae4d71b9976d639fa6a582b4a7bc4714", 0) has already been spent in transaction 5e2bf9e87ec42cfaf1b3b732e5983bdfaa8c95151b8189b6a773d1c0d304145c 2- If your UTX is P2TR, you must enter all the transaction UTXO,s amounts (not the sum of them), and script pub keys. in this case, if your selected UTXO's is pt2r

digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

You need to add all UTXO, pubkey script and amounts to generate transaction summary, like

    digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey(),fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(500),big.NewInt(1000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
    digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey(),fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(500),big.NewInt(1000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

I suggest you use the transaction builder, or see its code to understand how it works.

transactionbuilder example

yplong commented 10 months ago

How do I sign when I enter multiple input signatures? I made the following attempts:

  1. When I tried to adjust the number of signatures, an error {"code":-26,"message":" non-mandatory-script-verification-flag (Invalid Schnorr signature)"} was reported,
  2. If I only sign once, {"code":-22,"message":"TX decode failed. Make sure the tx has at least one input."} This error is reported.
  3. I adjusted the sighash GetTransactionTaprootDigest method the value of the parameter, SignTaprootTransaction sighash parameters value, or will be signed at the wrong mistake
// Private key of the UTXO owner
    privateKey, _ := keypair.NewECPrivateFromWIF("91qdf1RH6UMZwZTnHUJ3RSiGSZ5EAxvW2FgYFV2HeaLnqZQh4zx")
    // privateKey, _ := keypair.NewECPrivateFromWIF("cUUUyKDSNuDFtAt8deeSHxjHY5p7M9mvBfp32EFxz62H91WFiX9J")

    // Address we want to spend from
    fromAddr := privateKey.GetPublic().ToTaprootAddress()

    // Create an input
    // Insert transaction ID and index of UTXO
    sigTxin1 := scripts.NewTxInput("0924c8ad1b635f7edfb77321f43ba868ae4d71b9976d639fa6a582b4a7bc4714", 0) //500
    sigTxin2 := scripts.NewTxInput("ab03d8f71103ffe1e346ca65635cdad84dcd658e58c3b3ddd529f777c57ec848", 1) //1000

    // Address we want to send funds to
    // addr, _ := btcAddress.P2PKHAddressFromAddress("n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR")
    addr, _ := btcAddress.P2TRAddressFromAddress("tb1pnztcay5kzpyxcfhfmpth4f32re964zupd3k2f8g53rmcnp9kyz9qjukvm2")

    // Create an output: Send 3000 to `n4bkvTyU1dVdzsrhWBqBw8fEMbHjJvtmJR`
    txout := scripts.NewTxOutput(
        big.NewInt(500),
        addr.ToScriptPubKey(),
    )

    changeOut := scripts.NewTxOutput(
        big.NewInt(100),
        fromAddr.ToScriptPubKey(),
    )

    // Create a transaction
    tx := scripts.NewBtcTransaction(
        []*scripts.TxInput{sigTxin1, sigTxin2},
        []*scripts.TxOutput{txout, changeOut},
        // []*scripts.TxOutput{txout},
        true, // The transaction contains one or more segwit UTXOs
    )

    // Get the transaction digest for signing input at index 0
    // Arguments:
    // - Index 0
    // - The scriptPubkeys that correspond to all the inputs/UTXOs
    // - The amounts that correspond to all the inputs/UTXOs
    // - Ext_flag: Extension mechanism, default is 0; 1 is for script spending (BIP342)
    // - Script: The script that we are spending when ext_flag is 1
    digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
    digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
    // digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(3000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

    // Sign the transaction
    // Arguments:
    // - Transaction digest related to the index
    // - Signature Hash Type (TAPROOT_SIGHASH_ALL)
    // - Script path (tapleafs)
    // - Tweak: Note that we don't use tapleafs script in this transaction, so the tweak should be set to False
    sig := privateKey.SignTaprootTransaction(digest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true)
    sig2 := privateKey.SignTaprootTransaction(digest2, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true)
    // sig := privateKey.SignTaprootTransaction(digest, constant.TAPROOT_SIGHASH_ALL, []interface{}{}, true) // set to tue

    // Create a witness signature and set it to the transaction at the current index
    witness := scripts.NewTxWitnessInput(sig)
    // witnessOut := scripts.NewTxWitness(sig)
    tx.Witnesses = append(tx.Witnesses, witness)

    witness2 := scripts.NewTxWitnessInput(sig2)
    tx.Witnesses = append(tx.Witnesses, witness2)

    // Transaction ID
    tx.TxId()

    // In this case, the transaction is segwit, and we must use GetVSize for transaction size
    tx.GetVSize()

    // Transaction digest ready for broadcast
    digestHex := tx.Serialize()

    api := provider.SelectApi(provider.MempoolApi, &address.TestnetNetwork)
    trId, err := api.SendRawTransaction(digestHex)
    if err != nil {
        fmt.Println("err:", err)
    }
    fmt.Println("trid:", trId)

There are 2 problems in this transaction 1 - This sigTxin1 := scripts.NewTxInput("0924c8ad1b635f7edfb77321f43ba868ae4d71b9976d639fa6a582b4a7bc4714", 0) has already been spent in transaction 5e2bf9e87ec42cfaf1b3b732e5983bdfaa8c95151b8189b6a773d1c0d304145c 2- If your UTX is P2TR, you must enter all the transaction UTXO,s amounts (not the sum of them), and script pub keys. in this case, if your selected UTXO's is pt2r

digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(1500)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

You need to add all UTXO, pubkey script and amounts to generate transaction summary, like

  digest := tx.GetTransactionTaprootDigest(0, []*scripts.Script{fromAddr.ToScriptPubKey(),fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(500),big.NewInt(1000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)
  digest2 := tx.GetTransactionTaprootDigest(1, []*scripts.Script{fromAddr.ToScriptPubKey(),fromAddr.ToScriptPubKey()}, []*big.Int{big.NewInt(500),big.NewInt(1000)}, 0, scripts.NewScript(), constant.TAPROOT_SIGHASH_ALL)

I suggest you use the transaction builder, or see its code to understand how it works.

transactionbuilder example

That's it. Thank you very much