2

I am trying to unlock the P2WSH output of the following transaction:

https://blockstream.info/testnet/tx/b435b180c936564cc902ce3d7011643836a74d671159d494f59536e70d977cd2

The script that I'm paying to is a non-standard script, which can be unlocked by pushing OP_5 OP_0 before evaluation.

Here's the Python code I use to assemble the unlocking transaction:

from bitcoinutils.setup import setup
from bitcoinutils.utils import to_satoshis
from bitcoinutils.transactions import Transaction, TxInput, TxOutput
from bitcoinutils.keys import PrivateKey, P2wshAddress, P2wpkhAddress
from bitcoinutils.script import Script


def main():
    # always remember to setup the network
    setup('testnet')

    p2wsh_witness_script = Script.from_raw('0000018257615179547a75537a537a537a0079537a75527a527a7575615279008763537952795279615179517993517a75517a75619c77777777675279518763537952795279949c7777777767006868')

    fromAddress = P2wshAddress.from_script(p2wsh_witness_script)
    #print(fromAddress.to_string())

    toAddress = P2wpkhAddress.from_address("tb1qelplgcx5q7lj4mgk4sg8tlnq6njvamurvntwha")

    # set values
    txid = 'b435b180c936564cc902ce3d7011643836a74d671159d494f59536e70d977cd2'
    vout = 0
    amount = 0.00001400
    fee = 0.00000200


    # create transaction input from tx id of UTXO
    txin = TxInput(txid, vout)

    txOut1 = TxOutput(to_satoshis(amount - fee), toAddress.to_script_pub_key())

    tx = Transaction([txin], [txOut1], has_segwit=True)

    tx.witnesses.append(Script(['OP_5', 'OP_0', p2wsh_witness_script.to_hex()]))

    # print raw signed transaction ready to be broadcasted
    print("\nRaw signed transaction:\n" + tx.serialize())
    print("\nTxId:", tx.get_txid())


if __name__ == "__main__":
    main()

It produces the following serialized transaction:

02000000000101d27c970de73695f594d45911674da736386411703dce02c94c5636c980b135b40000000000ffffffff01b004000000000000160014cfc3f460d407bf2aed16ac1075fe60d4e4ceef83035500500000018257615179547a75537a537a537a0079537a75527a527a7575615279008763537952795279615179517993517a75517a75619c77777777675279518763537952795279949c777777776700686800000000

Unfortunately, while broadcasting, I get the following error:

sendrawtransaction RPC error: {"code":-22,"message":"TX decode failed. Make sure the tx has at least one input."}

I am not sure what I'm missing. Does the library serialize something wrong? Is the redeeming script too large?

msinkec
  • 23
  • 2

1 Answers1

2

The problem is this line:

tx.witnesses.append(Script(['OP_5', 'OP_0', p2wsh_witness_script.to_hex()]))

Perhaps confusingly, bitcoinutils uses the same API for scripts as it does for witness stacks (also known as "input witnesses" or just "witnesses" here), but they aren't scripts. They don't contain any opcodes, only byte arrays, some of which might be scripts.

In P2WSH the last witness item is the script and the preceding items form the initial stack for script execution, which means you shouldn't use opcodes but the result of running those opcodes:

tx.witnesses.append(Script(['05', '', p2wsh_witness_script.to_hex()]))

It might be a good idea to open an issue in the bitcoinutils repo because it probably shouldn't output an invalid transaction serialization without any warning.

Vojtěch Strnad
  • 5,623
  • 1
  • 8
  • 31
  • 1
    Yep, this was it. Thanks! Managed to broadcast it now: https://blockstream.info/testnet/tx/5e5c4ebde652c06adde980687926a0f91aa261bdeed3e1e18fd045dd1e766e22 – msinkec May 10 '23 at 18:24
  • I'll open an issue for bitcoinutils. Do you know of any other good tools where you can do things like this easily? – msinkec May 10 '23 at 18:25
  • 1
    Thank you both for the feedback. The plan was to create a TxInputWitness object instead of the Script that is passed now. I will prioritize this and it will be much cleaner. – karask May 12 '23 at 09:13