How do I pay to a multisig wallet? I am using the Python API (based on python-bitcoinrpc
) that ships with the bitcoin source code, in directory bitcoin/test/functional
. I'm using regtest
.
I call createmultisig()
in order to generate a P2SH address. I create a CTransaction
object. I attempt to append to the vout list a CTxOut
object that pays to the P2SH address, but this operation corrupts the object. I think I need to do something to the P2SH address before passing it as an input to the CTxOut
object, but I can't figure out what.
Below is the script for my unit test. The line where it goes wrong is:
tx1.vout.append(CTxOut(int(8 * COIN), madd))
After that the tx1 object is corrupted and when I subsequently try to print it to the log, the operation fails with the error TypeError: can't concat str to bytes
.
Any idea what I'm doing wrong?
#!/usr/bin/env python3
import pprint
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import CTransaction, CTxIn, CTxOut, COutPoint, ToHex, COIN
from test_framework.script import CScript, CScriptOp, OP_1, OP_DROP, OP_2, OP_HASH160, OP_EQUAL, hash160, OP_TRUE, OP_DUP, OP_EQUALVERIFY, OP_CHECKSIG
from test_framework.util import hex_str_to_bytes, bytes_to_hex_str
from test_framework.address import byte_to_base58
class RawTxTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [[],["-txindex"]]
def run_test(self):
"""Main test logic"""
self.log.info(f"\n DEBUG START")
# initialize node variables
n0 = self.nodes[0]
n1 = self.nodes[1]
# initialize wallet and address
w0 = n1.createwallet(wallet_name="test_wallet")
w0_rpc = n1.get_wallet_rpc('test_wallet')
addr0 = w0_rpc.getnewaddress()
self.log.info(f"\n DEBUG addr0={addr0}")
# TRANSACTION #0 - pay from node 0 to addr0
txid0 = n0.sendtoaddress(addr0, 10.0)
n0.generate(6)
self.sync_all()
# dump the transaction
raw_tx0 = w0_rpc.getrawtransaction(txid0, True)
s = pprint.pformat(raw_tx0)
self.log.info(f"\n DEBUG tx={s}")
# write the balance to the log
bal0 = w0_rpc.getbalance()
self.log.info(f"\n DEBUG bal0={bal0:f}")
# TRANSACTION #1 - Pay from addr0 to multisig
NUM_SIGS=2 # M
NUM_KEYS=3 # N
add = [w0_rpc.getnewaddress() for _ in range(NUM_KEYS)]
pub = [w0_rpc.getaddressinfo(a)["pubkey"] for a in add]
msig = w0_rpc.createmultisig(NUM_KEYS, pub, "bech32")
madd = msig["address"]
self.log.info(f"\n DEBUG madd={madd}")
tx1 = CTransaction()
vout = [v["n"] for v in raw_tx0["vout"] if addr0 in v["scriptPubKey"].get("addresses", [])]
assert len(vout) == 1
v = vout[0]
# append the input
tx1.vin.append(CTxIn(COutPoint(int(txid0, 16), v)))
# dump the tx to the log
s = pprint.pformat(w0_rpc.decoderawtransaction(ToHex(tx1)))
self.log.info(f"\n DEBUG tx={s}")
# append the output
tx1.vout.append(CTxOut(int(8 * COIN), madd))
# dump the tx to the log
s = pprint.pformat(w0_rpc.decoderawtransaction(ToHex(tx1)))
self.log.info(f"\n DEBUG tx={s}")
priv1 = w0_rpc.dumpprivkey(addr0)
tx1 = w0_rpc.signrawtransactionwithkey(ToHex(tx1), [priv1])["hex"]
txid1 = n1.sendrawtransaction(tx1, True)
n0.generate(6)
self.sync_all()
# dump the transaction
raw_tx1 = w0_rpc.getrawtransaction(txid1, True)
s = pprint.pformat(raw_tx1)
self.log.info(f"\n DEBUG tx={s}")
# write the balance to the log
bal1 = w0_rpc.getbalance()
self.log.info(f"\n DEBUG bal1={bal1:f}")
self.log.info(f"\n DEBUG END")
if __name__ == '__main__':
RawTxTest().main()