Crypto

taproot – spending P2TR script path output using python bitcoinutils library

I am currently trying to spend a P2TR output using one of the scripts used to create the address. Below is the python code I tried to construct raw transaction in order to spend the UTXO. The address I am trying to move testnet coins from is tb1pnnr3jysdew9gma9ac9k3pzdy5xp69fka3jh9vkga5ak28yjak0ws3hw834.

from bitcoinlib import transactions
from bitcoinutils.setup import setup
from bitcoinutils.script import Script
from bitcoinutils.utils import *
from bitcoinutils.transactions import Transaction, TxInput, TxOutput, TxWitnessInput
from bitcoinutils.keys import *

utxos = [{'txid': "4390635a95c7e3538dda7c4690f38fbfaa5d93eaf04e1f4f63fb811a884428e2", "vout": 0}]

priv1 = PrivateKey(wif="secret for obvious reason 1")
priv2 = PrivateKey(wif="secret for obvious reason 2")
priv3 = PrivateKey(wif="secret for obvious reason 3")
priv4 = PrivateKey(wif="secret for obvious reason 4")

pub1 = priv1.get_public_key()
pub2 = priv2.get_public_key()
pub3 = priv3.get_public_key()
pub4 = priv4.get_public_key()

def main():
    setup("testnet")

    input_amounts = [79800]
    output_amounts = [79650]

    # taproot script 2 is a simple P2PK with key 2
    tr_script_p2pk2 = Script([pub2.to_x_only_hex(), "OP_CHECKSIG"])

    # taproot script 3 is a simple P2PK with key 3
    tr_script_p2pk3 = Script([pub3.to_x_only_hex(), "OP_CHECKSIG"])

    # taproot script 4 is a simple P2PK with key 4
    tr_script_p2pk4 = Script([pub4.to_x_only_hex(), "OP_CHECKSIG"])

    all_leafs = [[tr_script_p2pk2, tr_script_p2pk3], tr_script_p2pk4]
    input_address = pub1.get_taproot_address(all_leafs)

    input_1 = TxInput(txid=utxos[4]['txid'], txout_index=utxos[4]['vout'])

    output_address = P2pkhAddress(address="moHfMJHAP3LE2aYTp4Q6g5GxnnfTK9muAJ")
    output_1 = TxOutput(amount=output_amounts[0], script_pubkey=output_address.to_script_pub_key())

    tx1 = Transaction(inputs=[input_1], outputs=[output_1], has_segwit=True)
    sig1 = priv3.sign_taproot_input(tx=tx1, txin_index=0, utxo_scripts=[input_address.to_script_pub_key()],
                                    amounts=input_amounts, script_path=True, 
                                    tapleaf_script=tr_script_p2pk3, tweak=False)

    # tagged hashes of leafs
    leaf2 = tapleaf_tagged_hash(tr_script_p2pk2)
    leaf3 = tapleaf_tagged_hash(tr_script_p2pk3)
    leaf4 = tapleaf_tagged_hash(tr_script_p2pk4)

    # If using get_tag_hashed_merkle_root
    merkleroot1 = get_tag_hashed_merkle_root(scripts=all_leafs)

    # If manually constructing the tree
    branch_23 = tapbranch_tagged_hash(leaf2, leaf3)
    merkleroot2 = tapbranch_tagged_hash(branch_23, leaf4)
    assert(merkleroot1.hex() == merkleroot2.hex())

    control_block = ControlBlock(pubkey=pub1, script_to_spend=tr_script_p2pk3, scripts=leaf2+leaf4)
    tx1.witnesses.append(TxWitnessInput([sig1, tr_script_p2pk3.to_hex(), control_block.to_hex()]))
    print("\nRaw signed transaction:\n" + tx1.serialize())

Among the three scripts used to create the address, I am trying to spend the UTXO using the tr_script_p2pk3. The raw transaction I got from executing this code is the following.

02000000 => version
0001 => marker and flag
01 => input count
e22844881a81fb634f1f4ef0ea935daabf8ff390467cda8d53e3c7955a639043 => input txid
00000000 => input vout
00 => scriptSig
ffffffff => nSequence
01 => output count
2237010000000000 => output amount
1976a914553d745f650e2faba231590931c3561e3ab7cb7888ac => scriptPubKey
03 => witness stack count
4060f3d6d5e5782977b53d6f7025ff8c7f70e1cb6a1b1de96117aa499713fcd701fed99f27d0922e58a8a8d731af9f3f9b36d5389e694b23513dd0b0eea6264fa7 => schnorr signature
222030a1dfffabb677eb2d8aa45bc7e24cbc31d67731680fe5be4b524e3359445f37ac => tr_script_p2pk3
61 => control block length
c0 => default leaf version tapscript
cded16e0e749cb161694b20f5bd7737ebc22b40805a415941b3fcc26df30dbb1 => internal key (in this example, pub1)
dff122122208c96d11efdd99a987150f7c422eeb2f9076f40804382dbc95c7b78cbca50495d9c133a60cf93e502e84b421b5640258f549c2bdb6e538eae16e7d => Merkle path (in this case, leaf2 + leaf4)
00000000 => nLocktime

The transaction failed and I received the following message: <sendrawtransaction RPC error: {“code”:-26,”message”:”non-mandatory-script-verify-flag (Witness program hash mismatch)”}> I tried the same code but with an address with two taproot script paths and the transaction was successfully broadcasted. In that case, the Merkle path just have to be the tapleaf tagged hash of the other script that wasn’t being used to unlock the UTXO. I believe the error has something to do with the control block part but I don’t know exactly where my code went wrong. Can somebody point out the problem?

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button