P2SH address (multisig)

The example for 1-of-1 should only serve as an example. We don't recommend using it in the real world because it is not its intention. Instead of 1-of-1 use P2PKH!

Generate address (1-of-1)

 1import hashlib
 2
 3from buidl.ecc import PrivateKey, Signature
 4from buidl.bech32 import decode_bech32
 5from buidl.helper import decode_base58, big_endian_to_int, SIGHASH_ALL, int_to_byte
 6from buidl.script import P2PKHScriptPubKey, RedeemScript, Script, P2WPKHScriptPubKey
 7from buidl.tx import Tx, TxIn, TxOut
 8
 9h = hashlib.sha256(b'correct horse battery staple').digest()
10private_key = PrivateKey(secret=big_endian_to_int(h), network="signet")
11
12# Create a redeemScript. Similar to a scriptPubKey the redeemScript must be satisfied for the funds
13# to be spent.
14redeem_script = RedeemScript([private_key.point.sec(), 0xac])
15address = redeem_script.address("signet")
16print("Address", address)
17# outputs: Address: 2Msc7itHhx2x8MEkTthvtED9pFC36J7QpQb

Spend from address (1-of-1)

Assuming the previously generated address has received funds, we can spend them. In order to spend them, we'll need information about the transaction id (txid) and a vector of an output (vout). You can get both from an explorer or by querying your running Bitcoin node by running listunspent along with some filters:

bitcoin-cli listunspent 1 9999999 "[\"address\"]"

Note that you must have an address in the watchlist in order to get any output. To add an address to a watchlist run importaddress:

bitcoin-cli importaddress <address> "<label>" false false

 1# we are continuing the code from above
 2
 3txid = bytes.fromhex("65665709c7d83b7efb8e58f5c6e08a3ebefd752dd83d7af9e0022b13388c3780")
 4vout = 1
 5
 6# Specify the amount send to your P2WSH address.
 7COIN = 100000000
 8amount = int(0.001 * COIN)
 9
10# Calculate an amount for the upcoming new UTXO. Set a high fee (5%) to bypass bitcoind minfee
11# setting on regtest.
12amount_less_fee = int(amount * 0.99)
13
14# Create the txin structure, which includes the outpoint. The scriptSig defaults to being empty as
15# is necessary for spending a P2WSH output.
16txin = TxIn(txid, vout)
17
18# Specify a destination address and create the txout.
19h160 = decode_bech32("tb1qqqlcpznqkfa65wqd48mzzghpwzefgpvtvl0a7k")[2]
20txout = TxOut(amount=amount_less_fee, script_pubkey=P2WPKHScriptPubKey(h160))
21
22tx = Tx(1, [txin], [txout], 0, network="signet")
23
24sighash = tx.sig_hash_legacy(0, redeem_script)
25
26# signed sighash
27signed_sighash = private_key.sign(sighash).der() + int_to_byte(SIGHASH_ALL)
28
29txin.script_sig = Script([signed_sighash, redeem_script.raw_serialize()])
30
31tx.check_sig_legacy(
32    0,
33    private_key.point,
34    Signature.parse(signed_sighash[:-1]),
35    redeem_script=redeem_script,
36)
37
38# Done! Print the transaction
39print(tx.serialize().hex())
40# outputs: 010000000180378c38132b02e0f97a3dd82d75fdbe3e8ae0c6f5588efb7e3bd8c709576665010000006c473044022040048e65bb5a58c69d9f514472b5d581f83105422210c134edf20c2ba1d2be50022029069037cc852b83b341a397c0a753bbaf6aefee9e9455483ffaa39666784ad80123210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71acffffffff01b882010000000000160014003f808a60b27baa380da9f62122e170b294058b00000000

Now that we have our signed and encoded transaction, we can broadcast it using sendrawtransaction:

bitcoin-cli sendrawtransaction <transaction>

If the transaction is broadcasted successfully a transaction id will be returned. In this case it was 9987191906843b5c99218cbae7f73d0ae85c0b62b78fdaf9755eb6a4a9856858.

Generate address (2-of-2)

In this example we show how to create a 2-of-2 multisig address. This means that two signatures are required in order to unlock funds.

 1import hashlib
 2
 3from buidl.ecc import PrivateKey, Signature
 4from buidl.helper import decode_base58, big_endian_to_int
 5from buidl.bech32 import decode_bech32, encode_bech32_checksum
 6from buidl.script import P2PKHScriptPubKey, RedeemScript, WitnessScript, P2WPKHScriptPubKey
 7from buidl.tx import Tx, TxIn, TxOut
 8from buidl.witness import Witness
 9
10# first key
11h = hashlib.sha256(b'correct horse battery staple first').digest()
12private_key1 = PrivateKey(secret=big_endian_to_int(h), network="signet")
13
14# second key
15h = hashlib.sha256(b'correct horse battery staple second').digest()
16private_key2 = PrivateKey(secret=big_endian_to_int(h), network="signet")
17
18# Create a redeem script
19redeem_script = RedeemScript.create_p2sh_multisig(
20    quorum_m=2,
21    pubkey_hexes=[
22        private_key1.point.sec().hex(),
23        private_key2.point.sec().hex(),
24    ],
25    sort_keys=False,
26)
27
28address = redeem_script.address("signet")
29print('Address:', str(address))
30# outputs: 2N3avDJKpr9c8pkRSYgWAsHSVCmRX3ce3w7

Spend from address (2-of-2)

Assuming the previously generated address has received funds, we can spend them. In order to spend them, we'll need information about the transaction id (txid) and a vector of an output (vout). You can get both from an explorer or by querying your running Bitcoin node by running listunspent along with some filters:

bitcoin-cli listunspent 1 9999999 "[\"address\"]"

Note that you must have an address in the watchlist in order to get any output. To add an address to a watchlist run importaddress:

bitcoin-cli importaddress <address> "<label>" false false

 1# we are continuing the code from above
 2
 3txid = bytes.fromhex("20a58d3d90b0a9758f2e717dfb4195f270323221b8be01b06c95551807654b9c")
 4vout = 1
 5
 6# Specify the amount send to your P2WSH address.
 7COIN = 100000000
 8amount = int(0.001 * COIN)
 9
10# Calculate an amount for the upcoming new UTXO. Set a high fee (5%) to bypass bitcoind minfee
11# setting on regtest.
12amount_less_fee = int(amount * 0.99)
13
14# Create the txin structure, which includes the outpoint. The scriptSig defaults to being empty as
15# is necessary for spending a P2WSH output.
16txin = TxIn(txid, vout)
17
18# Specify a destination address and create the txout.
19h160 = decode_bech32("tb1qwp3c26rlgzlq4axergvt04300shexn4f56q5f7")[2]
20txout = TxOut(amount=amount_less_fee, script_pubkey=P2WPKHScriptPubKey(h160))
21
22tx = Tx(1, [txin], [txout], 0, network="signet")
23
24sig1 = tx.get_sig_legacy(0, private_key1, redeem_script=redeem_script)
25sig2 = tx.get_sig_legacy(0, private_key2, redeem_script=redeem_script)
26
27tx.check_sig_legacy(
28    0,
29    private_key1.point,
30    Signature.parse(sig1[:-1]),
31    redeem_script=redeem_script,
32)
33
34tx.check_sig_legacy(
35    0,
36    private_key2.point,
37    Signature.parse(sig2[:-1]),
38    redeem_script=redeem_script,
39)
40
41txin.finalize_p2sh_multisig([sig1, sig2], redeem_script)
42
43print(tx.serialize().hex())
44# outputs: 01000000019c4b65071855956cb001beb821323270f29541fb7d712e8f75a9b0903d8da52001000000da004730440220470aef78655414f73ca5ae0feec073d49a134ba1bc3b3ea85d2aa864a9735a66022029382bd5f497dc8435f75dff4ebe6cb7205064738487bbfd7099fd3741c61bc901483045022100aa867aeba637ef0aa8f6386e06bcce5010afb081087ab4e3d01ce610f57eaa4702204b50096f8225cac48a93cddab25be1463b160c77605b93556cc215ed83c0d0f301475221038d19497c3922b807c91b829d6873ae5bfa2ae500f3237100265a302fdce87b052103d3a9dff5a0bb0267f19a9ee1c374901c39045fbe041c1c168d4da4ce0112595552aeffffffff01b882010000000000160014706385687f40be0af4d91a18b7d62f7c2f934ea900000000

Now that we have our signed and encoded transaction, we can broadcast it using sendrawtransaction:

bitcoin-cli sendrawtransaction <transaction>

If the transaction is broadcasted successfully a transaction id will be returned. In this case it was 8439bee332441a97a5bd01396c95353a26a163287e12bd691e1940b734f77478.

Generate address (1-of-3)

In this example we show how to create a 1-of-3 multisig address. This means that one out of three signatures can unlock and spend bitcoins.

 1import hashlib
 2
 3from buidl.ecc import PrivateKey, Signature
 4from buidl.helper import decode_base58, big_endian_to_int
 5from buidl.bech32 import decode_bech32, encode_bech32_checksum
 6from buidl.script import P2PKHScriptPubKey, RedeemScript, WitnessScript, P2WPKHScriptPubKey
 7from buidl.tx import Tx, TxIn, TxOut
 8from buidl.witness import Witness
 9
10# first key
11h = hashlib.sha256(b'correct horse battery staple first').digest()
12private_key1 = PrivateKey(secret=big_endian_to_int(h), network="signet")
13
14# second key
15h = hashlib.sha256(b'correct horse battery staple second').digest()
16private_key2 = PrivateKey(secret=big_endian_to_int(h), network="signet")
17
18# third key
19h = hashlib.sha256(b'correct horse battery staple third').digest()
20private_key3 = PrivateKey(secret=big_endian_to_int(h), network="signet")
21
22# Create a redeem script
23redeem_script = RedeemScript.create_p2sh_multisig(
24    quorum_m=1,  # how many signatures are required
25    pubkey_hexes=[
26        private_key1.point.sec().hex(),
27        private_key2.point.sec().hex(),
28        private_key3.point.sec().hex(),
29    ],
30    sort_keys=False,
31)
32
33address = redeem_script.address("signet")
34print('Address:', str(address))
35# outputs: 2N5LksNHu7mL8EjjgXiqDoebL1Sft18ZGJs

Spend from address (1-of-3)

Assuming the previously generated address has received funds, we can spend them. In order to spend them, we'll need information about the transaction id (txid) and a vector of an output (vout). You can get both from an explorer or by querying your running Bitcoin node by running listunspent along with some filters:

bitcoin-cli listunspent 1 9999999 "[\"address\"]"

Note that you must have an address in the watchlist in order to get any output. To add an address to a watchlist run importaddress:

bitcoin-cli importaddress <address> "<label>" false false

 1# we are continuing the code from above
 2
 3txid = bytes.fromhex("b82897726ee94a0ffe019bcbc8ff22e5c9d5207659807bb5dec84a3722901175")
 4vout = 1
 5
 6# Specify the amount send to your P2WSH address.
 7COIN = 100000000
 8amount = int(0.001 * COIN)
 9
10# Calculate an amount for the upcoming new UTXO. Set a high fee (5%) to bypass bitcoind minfee
11# setting on regtest.
12amount_less_fee = int(amount * 0.99)
13
14# Create the txin structure, which includes the outpoint. The scriptSig defaults to being empty as
15# is necessary for spending a P2WSH output.
16txin = TxIn(txid, vout)
17
18# Specify a destination address and create the txout.
19h160 = decode_bech32("tb1qwp3c26rlgzlq4axergvt04300shexn4f56q5f7")[2]
20txout = TxOut(amount=amount_less_fee, script_pubkey=P2WPKHScriptPubKey(h160))
21
22tx = Tx(1, [txin], [txout], 0, network="signet")
23
24sig2 = tx.get_sig_legacy(0, private_key2, redeem_script=redeem_script)
25
26tx.check_sig_legacy(
27    0,
28    private_key2.point,
29    Signature.parse(sig2[:-1]),
30    redeem_script=redeem_script,
31)
32
33# we will finalize this transaction using the second signature
34txin.finalize_p2sh_multisig([sig2], redeem_script)
35
36print(tx.serialize().hex())
37# outputs: 010000000175119022374ac8deb57b80597620d5c9e522ffc8cb9b01fe0f4ae96e729728b801000000b40047304402201faabc28b75db74f2cff5137eb278a26693106304835d129e6bc9522f463fcd90220613987ed01655781cca27f762e58222fa006d5161804f10a7ab8b04104c25aee014c695121038d19497c3922b807c91b829d6873ae5bfa2ae500f3237100265a302fdce87b052103d3a9dff5a0bb0267f19a9ee1c374901c39045fbe041c1c168d4da4ce01125955210228769768e3083e084dd5c03b6077d51b7e7a22bd66fc99ff481dcf9b6b80d03053aeffffffff01b882010000000000160014706385687f40be0af4d91a18b7d62f7c2f934ea900000000

Now that we have our signed and encoded transaction, we can broadcast it using sendrawtransaction:

bitcoin-cli sendrawtransaction <transaction>

If the transaction is broadcasted successfully a transaction id will be returned. In this case it was 7ad8a4df0b38eab28b21e43950ce5243d3dbdf92a209620b5bffb07ec7417d18.

Generate address (2-of-3)

In this example we show how to create a 2-of-3 multisig address. This means that two out of three signatures can unlock and spend bitcoins.

 1import hashlib
 2
 3from buidl.ecc import PrivateKey, Signature
 4from buidl.helper import decode_base58, big_endian_to_int
 5from buidl.bech32 import decode_bech32, encode_bech32_checksum
 6from buidl.script import P2PKHScriptPubKey, RedeemScript, WitnessScript, P2WPKHScriptPubKey
 7from buidl.tx import Tx, TxIn, TxOut
 8from buidl.witness import Witness
 9
10# first key
11h = hashlib.sha256(b'correct horse battery staple first').digest()
12private_key1 = PrivateKey(secret=big_endian_to_int(h), network="signet")
13
14# second key
15h = hashlib.sha256(b'correct horse battery staple second').digest()
16private_key2 = PrivateKey(secret=big_endian_to_int(h), network="signet")
17
18# third key
19h = hashlib.sha256(b'correct horse battery staple third').digest()
20private_key3 = PrivateKey(secret=big_endian_to_int(h), network="signet")
21
22# Create a redeem script
23redeem_script = RedeemScript.create_p2sh_multisig(
24    quorum_m=2,  # how many signatures are required
25    pubkey_hexes=[
26        private_key1.point.sec().hex(),
27        private_key2.point.sec().hex(),
28        private_key3.point.sec().hex(),
29    ],
30    sort_keys=False,
31)
32
33address = redeem_script.address("signet")
34print('Address:', str(address))
35# outputs: 2MyaZGLLZsEjRrnJQ3pAuNUNb9NhQnF1UhP

Spend from address (2-of-3)

Assuming the previously generated address has received funds, we can spend them. In order to spend them, we'll need information about the transaction id (txid) and a vector of an output (vout). You can get both from an explorer or by querying your running Bitcoin node by running listunspent along with some filters:

bitcoin-cli listunspent 1 9999999 "[\"address\"]"

Note that you must have an address in the watchlist in order to get any output. To add an address to a watchlist run importaddress:

bitcoin-cli importaddress <address> "<label>" false false

 1# we are continuing the code from above
 2
 3txid = bytes.fromhex("cc75da80c0702fdd340c75b8b7f430b914eaf1ba048055815303ee4ebcebc45f")
 4vout = 0
 5
 6# Specify the amount send to your P2WSH address.
 7COIN = 100000000
 8amount = int(0.001 * COIN)
 9
10# Calculate an amount for the upcoming new UTXO. Set a high fee (5%) to bypass bitcoind minfee
11# setting on regtest.
12amount_less_fee = int(amount * 0.99)
13
14# Create the txin structure, which includes the outpoint. The scriptSig defaults to being empty as
15# is necessary for spending a P2WSH output.
16txin = TxIn(txid, vout)
17
18# Specify a destination address and create the txout.
19h160 = decode_bech32("tb1qwp3c26rlgzlq4axergvt04300shexn4f56q5f7")[2]
20txout = TxOut(amount=amount_less_fee, script_pubkey=P2WPKHScriptPubKey(h160))
21
22tx = Tx(1, [txin], [txout], 0, network="signet")
23
24sig2 = tx.get_sig_legacy(0, private_key2, redeem_script=redeem_script)
25sig3 = tx.get_sig_legacy(0, private_key3, redeem_script=redeem_script)
26
27tx.check_sig_segwit(
28    0,
29    private_key1.point,
30    Signature.parse(sig1[:-1]),
31    redeem_script=redeem_script,
32)
33
34tx.check_sig_legacy(
35    0,
36    private_key2.point,
37    Signature.parse(sig2[:-1]),
38    redeem_script=redeem_script,
39)
40tx.check_sig_legacy(
41    0,
42    private_key3.point,
43    Signature.parse(sig3[:-1]),
44    redeem_script=redeem_script,
45)
46
47# we will finalize this transaction using the second signature
48txin.finalize_p2sh_multisig([sig2, sig3], redeem_script)
49
50print(tx.serialize().hex())
51# outputs: 01000000015fc4ebbc4eee035381558004baf1ea14b930f4b7b8750c34dd2f70c080da75cc00000000fc00473044022007c7275dfa45242e72946adf892e38047a8b69bb5760a5c382ede8a04aeec859022072ac719796ce469693c14fb45e03064be1644df65e6f58c45face48bfd9b4352014730440220778bd0deb867907b05f509a23a22d597587ee1eeb3e01000892909a270fbb9d20220665779c669da7fd1aeb04c70ba0fef853bd08f35625f34e98b883eb22381ace9014c695221038d19497c3922b807c91b829d6873ae5bfa2ae500f3237100265a302fdce87b052103d3a9dff5a0bb0267f19a9ee1c374901c39045fbe041c1c168d4da4ce01125955210228769768e3083e084dd5c03b6077d51b7e7a22bd66fc99ff481dcf9b6b80d03053aeffffffff01b882010000000000160014706385687f40be0af4d91a18b7d62f7c2f934ea900000000

Now that we have our signed and encoded transaction, we can broadcast it using sendrawtransaction:

bitcoin-cli sendrawtransaction <transaction>

If the transaction is broadcasted successfully a transaction id will be returned. In this case it was 1bbdfcf560c1b105aecc5a4a63eb0b6390aa5e2d90667f9c1aa318a12702be46.