2

I'm constructing a script to spend multiple distinct UTXOs in a single transaction. I've successfully combined all UTXOs except for two.

Succeeding in spending a P2TR with P2PKH UTXO.

I encountered a problem when attempting to spend a P2TR with one of them P2SH, P2WSH, P2WPKH.

accompanied by a 'non-mandatory-script-verify-flag (Invalid Schnorr signature).

While I managed to dispatch these transactions via an alternative node, none of them have been confirmed as of yet.

tx ids: (bitcoin-testnet)

88558b495f28ff4799a4dacf40ee5cd1666850fed4847b8315256a5ae22527f8 02429ffe2f82f587d8e7a6e925201d3e7f219ac3af3688f2bc5c2770204e5d5c

What could be the cause? thanks

P2TR,P2WPKH to P2PKH

"hex": "02000000000102f80b4d99b2489228e23a25f1a458feefc1129b8ee030dc53d7ad0f112e278bd10000000000fffffffff80b4d99b2489228e23a25f1a458feefc1129b8ee030dc53d7ad0f112e278bd10100000000ffffffff01dc000000000000001976a91451283c275c76d14f0a1b010fa3430f0ade37781c88ac0140d7ac7111d6a732e655ebdf62fe224b1572803b47eb0c70aeb2b4f1a8f287fa20008b9145143f4cd0302ac1420f9602f5728a74d7c91b6c491ca44323d045f0af024730440220608f9ba4da7daa2d70aa24e7aabbf2c47d78ed39c331f40caf7aba7c07aa14750220467cf13de308c0633624fbcc6b41f9a1ef4f4f8568db9ac13ab7f1002e12912c01210220e57408d8f6726a81747f7507f7cdb7b86c502882d8a3bce5c62d39fddf38b100000000",
  "addresses": [
    "mnv5EAfPMTbfatz5ZbT6uUKpK7E5MftBAv",
    "tb1ps7gfq0h0hwu2cql9azz0wcf8rphr6xxeeyenrugd6yf263pxg9tq0fcjvz",
    "tb1qgt0w9wnw9wrs5qxtw7ptpfdz579889t2lcwcsy"
  ],
  "total": 220,
  "fees": 199780,
  "size": 301,
  "vsize": 170,
  "ver": 2,
  "vin_sz": 2,
  "vout_sz": 1,
  "confirmations": 0,
  "inputs": [
    {
      "prev_hash": "d18b272e110fadd753dc30e08e9b12c1effe58a4f1253ae2289248b2994d0bf8",
      "output_index": 0,
      "output_value": 100000,
      "sequence": 4294967295,
      "addresses": [
        "tb1ps7gfq0h0hwu2cql9azz0wcf8rphr6xxeeyenrugd6yf263pxg9tq0fcjvz"
      ],
      "script_type": "pay-to-taproot",
      "age": 2474244,
      "witness": [
        "d7ac7111d6a732e655ebdf62fe224b1572803b47eb0c70aeb2b4f1a8f287fa20008b9145143f4cd0302ac1420f9602f5728a74d7c91b6c491ca44323d045f0af"
      ]
    },
    {
      "prev_hash": "d18b272e110fadd753dc30e08e9b12c1effe58a4f1253ae2289248b2994d0bf8",
      "output_index": 1,
      "output_value": 100000,
      "sequence": 4294967295,
      "addresses": [
        "tb1qgt0w9wnw9wrs5qxtw7ptpfdz579889t2lcwcsy"
      ],
      "script_type": "pay-to-witness-pubkey-hash",
      "age": 2474244,
      "witness": [
        "30440220608f9ba4da7daa2d70aa24e7aabbf2c47d78ed39c331f40caf7aba7c07aa14750220467cf13de308c0633624fbcc6b41f9a1ef4f4f8568db9ac13ab7f1002e12912c01",
        "0220e57408d8f6726a81747f7507f7cdb7b86c502882d8a3bce5c62d39fddf38b1"
      ]
    }
  ],
  "outputs": [
    {
      "value": 220,
      "script": "76a91451283c275c76d14f0a1b010fa3430f0ade37781c88ac",
      "addresses": [
        "mnv5EAfPMTbfatz5ZbT6uUKpK7E5MftBAv"
      ],
      "script_type": "pay-to-pubkey-hash"
    }
  ]

I used python-bitcoin-utils Python tool to generate the following code related to the transaction ID 88558b495f28ff4799a4dacf40ee5cd1666850fed4847b8315256a5ae22527f8 which involves inputs from both P2TR and P2WPKH scripts.

def main():
    print(to_satoshis(0.0000022))

    setup('testnet')
    priv1 = PrivateKey("cV3R88re3AZSBnWhBBNdiCKTfwpMKkYYjdiR13HQzsU7zoRNX7JL")
    priv2 = PrivateKey("cSfna7riKJdNU7skpRUx17WYANNsyHTA2FmuzLpFzpp37xpytgob")
    pub1 = priv1.get_public_key()
    pub2 = priv2.get_public_key()
    fromAddress1 = pub1.get_taproot_address()
    fromAddress2 = pub2.get_address()
    # p2tr utxo
    txid1 = 'd18b272e110fadd753dc30e08e9b12c1effe58a4f1253ae2289248b2994d0bf8'
    vout1 = 0
    # P2wpkh UTXO
    txid2 = 'd18b272e110fadd753dc30e08e9b12c1effe58a4f1253ae2289248b2994d0bf8'
    vout2 = 1
    amount1 = 100000
    amount2 = 100000
    amounts = [amount1, amount2]
    script_pubkey1 = fromAddress1.to_script_pub_key()
    script_pubkey2 = fromAddress2.to_script_pub_key()
    utxos_script_pubkeys = [script_pubkey1, script_pubkey2]

    toAddress = P2pkhAddress('mnv5EAfPMTbfatz5ZbT6uUKpK7E5MftBAv')
    txin1 = TxInput(txid1, vout1)
    txin2 = TxInput(txid2, vout2)
    txOut = TxOutput(to_satoshis(0.0000022), toAddress.to_script_pub_key())

    tx = Transaction([txin1, txin2], [txOut], has_segwit=True)
    sig1 = priv1.sign_taproot_input(tx, 0, utxos_script_pubkeys, amounts)
    sig2 = priv2.sign_segwit_input(tx, 1, script=script_pubkey2, amount=amount1)

    tx.witnesses.append(TxWitnessInput([sig1]))
    tx.witnesses.append(TxWitnessInput([sig2, pub2.to_hex()]))
    print("\nRaw signed transaction:\n" + tx.serialize())
15
  • Instead of posting the one that worked, could you post the transaction that failed? My first guess would be that you forgot to add the (empty) witness stack for the non-segwit input.
    – Murch
    Aug 22, 2023 at 14:01
  • @Murch I added an empty witness for the UTXO P2SH at the specified index. I didn't modify the transaction; this is the same failed transaction that got confirmed on another node provider. Aug 22, 2023 at 14:02
  • Okay, cool. Could you still post the failed transaction rather than one that succeeded? It’s hard to diagnose the issue without seeing the transaction that caused the problem.
    – Murch
    Aug 22, 2023 at 14:04
  • @Murch I didn't modify the transaction; this is the same failed transaction that got confirmed on another node provider Aug 22, 2023 at 14:06
  • Oh, my bad. I thought that the second input was P2PKH, instead of P2WPKH.
    – Murch
    Aug 22, 2023 at 14:09

1 Answer 1

2

Since it is a segwit address you should use

fromAddress2 = pub2.get_segwit_address()

Also, while taproot (segwit v1) requires the scriptPubKey when signing, segwit v0 requires the script code; i.e. a template that corresponds to the non-segwit equivalent. For example, for p2wpkh you would use as script code the p2pkh scriptPubKey.

So when signing the segwit input you shouldn't use the scriptPubKey but the p2pkh equivalent for that address. Example https://github.com/karask/python-bitcoin-utils/blob/master/examples/spend_p2wpkh_transaction.py demonstrates this.

I haven't tried these changes to your code yet, but will do with the first opportunity.

EDIT: Just tried the suggested changes it works.

PS. In the meantime, if you try it out yourself please use testmempoolaccept to test tx validity instead of just sending the tx to a node for processing. That will test tx validity without spending the outputs. If there is still an issue I can then test using the same UTXOs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.