1+ # Copyright (C) 2025 The python-bitcoin-utils developers
2+ #
3+ # This file is part of python-bitcoin-utils
4+ #
5+ # It is subject to the license terms in the LICENSE file found in the top-level
6+ # directory of this distribution.
7+ #
8+ # No part of python-bitcoin-utils, including this file, may be copied,
9+ # modified, propagated, or distributed except according to the terms contained
10+ # in the LICENSE file.
11+
12+ """
13+ Example of creating a 2-of-3 multisig PSBT using REAL TESTNET4 UTXOs.
14+
15+ This example demonstrates:
16+ 1. Creating a 2-of-3 multisig P2WSH address (Segwit multisig)
17+ 2. Creating a PSBT for spending from that address using real Testnet4 UTXOs
18+ 3. Setting up the PSBT with proper input information for signing
19+
20+ IMPORTANT: This uses REAL TESTNET4 transactions that can be verified on:
21+ https://blockstream.info/testnet/
22+
23+ Note: This script uses bitcoinutils with setup('testnet'), which defaults to Testnet3.
24+ For Testnet4 compatibility, ensure your UTXOs are from Testnet4 and verify
25+ using a Testnet4-compatible explorer or wallet.
26+
27+ Before running this example:
28+ 1. Get Testnet4 coins from a faucet (e.g., https://faucet.testnet4.dev/)
29+ 2. Create the multisig address shown below
30+ 3. Send Testnet4 coins to that address
31+ 4. Update the UTXO details below with your real transaction
32+ """
33+
34+ from bitcoinutils .setup import setup
35+ from bitcoinutils .transactions import Transaction , TxInput , TxOutput , Locktime
36+ from bitcoinutils .keys import PrivateKey , PublicKey
37+ from bitcoinutils .script import Script
38+ from bitcoinutils .utils import to_satoshis
39+ from bitcoinutils .psbt import PSBT
40+ from bitcoinutils .constants import TYPE_RELATIVE_TIMELOCK
41+
42+
43+ def get_real_testnet_utxo ():
44+ """
45+ STEP-BY-STEP GUIDE TO GET REAL TESTNET4 UTXO:
46+
47+ 1. Visit a Testnet4 block explorer (e.g., https://blockstream.info/testnet/)
48+ 2. Find any recent transaction with outputs
49+ 3. Click on a transaction, copy its TXID
50+ 4. Replace the values below with real Testnet4 data
51+ 5. Verify the TXID works on a Testnet4-compatible explorer
52+
53+ EXAMPLE OF HOW TO FIND REAL DATA:
54+ - Go to a Testnet4-compatible explorer
55+ - Click "Recent Transactions"
56+ - Pick any transaction (e.g., click on a TXID)
57+ - Copy the TXID from the URL
58+ - Check the outputs for amount and vout index
59+ """
60+
61+ # METHOD 1: Use a funding transaction you create yourself
62+ # (Recommended - you control the UTXO)
63+ create_own_funding = True
64+
65+ if create_own_funding :
66+ # TODO: After running this script once:
67+ # 1. Note the multisig address printed below
68+ # 2. Get Testnet4 coins from faucet
69+ # 3. Send coins to the multisig address
70+ # 4. Update these values with YOUR funding transaction
71+ utxo_details = {
72+ 'txid' : 'YOUR_FUNDING_TXID_HERE' , # ← Replace with your funding TXID
73+ 'vout' : 0 , # ← Usually 0, but check the transaction
74+ 'amount' : to_satoshis (0.001 ), # ← Replace with actual amount sent
75+ 'address' : None , # Will be set to multisig address
76+ 'is_placeholder' : True # Set to False when using real data
77+ }
78+ else :
79+ # METHOD 2: Use any existing Testnet4 UTXO (not recommended for production)
80+ # This is just for demonstration - don't spend other people's UTXOs!
81+ utxo_details = {
82+ 'txid' : 'SOME_EXISTING_TESTNET4_TXID' ,
83+ 'vout' : 0 ,
84+ 'amount' : to_satoshis (0.001 ),
85+ 'address' : None ,
86+ 'is_placeholder' : True
87+ }
88+
89+ # Validation
90+ if utxo_details ['is_placeholder' ]:
91+ print (" PLACEHOLDER DATA DETECTED!" )
92+ print (" This PSBT uses placeholder data and won't work on Testnet4." )
93+ print (" Follow these steps to use real Testnet4 data:" )
94+ print ()
95+ print (" STEP 1: Get Testnet4 coins" )
96+ print (" • Visit: https://faucet.testnet4.dev/" )
97+ print (" • Request coins to any address you control" )
98+ print ()
99+ print (" STEP 2: Fund the multisig (run this script first to get address)" )
100+ print (" • Send Testnet4 coins to the multisig address" )
101+ print (" • Wait for confirmation" )
102+ print ()
103+ print (" STEP 3: Update this function" )
104+ print (" • Copy the funding transaction TXID" )
105+ print (" • Set utxo_details['txid'] = 'your_real_txid'" )
106+ print (" • Set utxo_details['amount'] = to_satoshis(your_real_amount)" )
107+ print (" • Set utxo_details['is_placeholder'] = False" )
108+ print ()
109+ print (" STEP 4: Verify" )
110+ print (" • Check on a Testnet4-compatible explorer" )
111+ print (" • Confirm the UTXO exists and amount is correct" )
112+ print ()
113+
114+ return utxo_details
115+
116+
117+ def main ():
118+ # Always call setup() first - using testnet (Testnet3, compatible with Testnet4 with real UTXOs)
119+ setup ('testnet' )
120+
121+ print ("=" * 70 )
122+ print ("Creating 2-of-3 Multisig PSBT with REAL TESTNET4 UTXOs" )
123+ print ("=" * 70 )
124+
125+ # Step 1: Create three private keys (representing Alice, Bob, and Charlie)
126+ print ("\n 1. Creating private keys for Alice, Bob, and Charlie..." )
127+
128+ # Using deterministic keys for consistency (in production, generate securely)
129+ alice_private_key = PrivateKey .from_wif ("cTALNpTpRbbxTCJ2A5Vq88UxT44w1PE2cYqiB3n4hRvzyCev1Wwo" )
130+ alice_public_key = alice_private_key .get_public_key ()
131+ print (f"Alice's public key: { alice_public_key .to_hex ()} " )
132+
133+ # Bob's key
134+ bob_private_key = PrivateKey .from_wif ("cVf3kGh6552jU2rLaKwXTKq5APHPoZqCP4GQzQirWGHFoHQ9rEVt" )
135+ bob_public_key = bob_private_key .get_public_key ()
136+ print (f"Bob's public key: { bob_public_key .to_hex ()} " )
137+
138+ # Charlie's key
139+ charlie_private_key = PrivateKey .from_wif ("cQDvVP5VhYsV3dtHQwQ5dCbL54WuJcvsUgr3LXwhf6vD5mPp9nVy" )
140+ charlie_public_key = charlie_private_key .get_public_key ()
141+ print (f"Charlie's public key: { charlie_public_key .to_hex ()} " )
142+
143+ # Step 2: Create 2-of-3 multisig P2WSH script (Segwit version)
144+ print ("\n 2. Creating 2-of-3 multisig P2WSH script..." )
145+
146+ # Create the multisig witness script (2 of 3) - sorted keys for deterministic addresses
147+ public_keys = sorted ([alice_public_key , bob_public_key , charlie_public_key ],
148+ key = lambda k : k .to_hex ())
149+
150+ witness_script = Script ([
151+ 2 , # Required signatures
152+ public_keys [0 ].to_hex (),
153+ public_keys [1 ].to_hex (),
154+ public_keys [2 ].to_hex (),
155+ 3 , # Total public keys
156+ 'OP_CHECKMULTISIG'
157+ ])
158+
159+ print (f"Witness script: { witness_script .to_hex ()} " )
160+
161+ # Create P2WSH address from the witness script
162+ p2wsh_address = witness_script .to_p2wsh_script_pub_key ().to_address ()
163+ print (f"P2WSH Multisig Address: { p2wsh_address } " )
164+ print (f" Check this address on a Testnet4-compatible explorer" )
165+
166+ # Step 3: Get real Testnet4 UTXO details
167+ print ("\n 3. Getting real Testnet4 UTXO details..." )
168+ utxo = get_real_testnet_utxo ()
169+ utxo ['address' ] = p2wsh_address
170+
171+ print (f"Using UTXO:" )
172+ print (f" TXID: { utxo ['txid' ]} " )
173+ print (f" Vout: { utxo ['vout' ]} " )
174+ print (f" Amount: { utxo ['amount' ]} satoshis ({ utxo ['amount' ] / 100000000 :.8f} BTC)" )
175+ print (f" Address: { utxo ['address' ]} " )
176+
177+ if utxo ['is_placeholder' ]:
178+ print (f" PLACEHOLDER: This TXID is not verifiable" )
179+ print (f" This TXID won't verify - it's just an example format" )
180+ else :
181+ print (f" VERIFY: Check on a Testnet4-compatible explorer" )
182+ print (f" This should show a real Testnet4 transaction" )
183+
184+ # Step 4: Create transaction inputs and outputs
185+ print ("\n 4. Setting up transaction..." )
186+
187+ # Input: Real Testnet4 UTXO
188+ txin = TxInput (utxo ['txid' ], utxo ['vout' ])
189+
190+ # Output: Send to Charlie's P2WPKH address (modern Segwit address)
191+ charlie_p2wpkh_address = charlie_public_key .get_segwit_address ()
192+
193+ # Calculate output amount (leaving some for fees)
194+ fee_amount = to_satoshis (0.0001 ) # 0.0001 BTC fee
195+ send_amount = utxo ['amount' ] - fee_amount
196+
197+ if send_amount <= 0 :
198+ raise ValueError ("UTXO amount too small to cover fees!" )
199+
200+ txout = TxOutput (send_amount , charlie_p2wpkh_address .to_script_pub_key ())
201+
202+ # Create the transaction
203+ tx = Transaction ([txin ], [txout ], Locktime (0 ))
204+ print (f"Unsigned transaction: { tx .serialize ()} " )
205+
206+ # Step 5: Create PSBT
207+ print ("\n 5. Creating PSBT..." )
208+
209+ # Create PSBT from the unsigned transaction
210+ psbt = PSBT (tx )
211+
212+ # Add input information needed for signing P2WSH
213+ # For P2WSH inputs, we need the witness script and witness UTXO info
214+ psbt .add_input_witness_script (0 , witness_script )
215+ psbt .add_input_witness_utxo (0 , utxo ['amount' ], p2wsh_address .to_script_pub_key ())
216+
217+ print (f"PSBT created successfully!" )
218+ print (f"PSBT base64: { psbt .to_base64 ()} " )
219+
220+ # Step 6: Display verification information
221+ print ("\n 6. TESTNET4 VERIFICATION" )
222+ print ("=" * 50 )
223+
224+ if utxo ['is_placeholder' ]:
225+ print (" USING PLACEHOLDER DATA - NOT VERIFIABLE" )
226+ print (" Current TXID is fake and won't verify on explorer" )
227+ print (" To fix this:" )
228+ print (" 1. Get real Testnet4 coins from faucet" )
229+ print (" 2. Send to the multisig address above" )
230+ print (" 3. Update get_real_testnet_utxo() with real data" )
231+ print ()
232+ print (" When ready, verify with a Testnet4-compatible explorer" )
233+ else :
234+ print (" REAL TESTNET4 DATA - VERIFIABLE" )
235+ print (" Verify input transaction on a Testnet4-compatible explorer" )
236+
237+ print (f" Check multisig address balance on a Testnet4-compatible explorer" )
238+ print (f" After signing and broadcasting, check output:" )
239+ print (f" Address: { charlie_p2wpkh_address } " )
240+
241+ # Step 7: Display signing workflow
242+ print ("\n 7. SIGNING WORKFLOW" )
243+ print ("=" * 50 )
244+ print ("This PSBT is ready for the 2-of-3 multisig signing process:" )
245+ print ()
246+ print ("1. Alice signs:" )
247+ print (" - Import PSBT" )
248+ print (" - Sign with Alice's private key" )
249+ print (" - Export partial signature" )
250+ print ()
251+ print ("2. Bob signs:" )
252+ print (" - Import PSBT (with Alice's signature)" )
253+ print (" - Sign with Bob's private key" )
254+ print (" - Export complete signature" )
255+ print ()
256+ print ("3. Finalize and broadcast:" )
257+ print (" - Combine signatures (2 of 3 threshold met)" )
258+ print (" - Finalize PSBT to create broadcastable transaction" )
259+ print (" - Broadcast to Testnet4" )
260+ print (" - Monitor on a Testnet4-compatible explorer" )
261+
262+ # Step 8: Show the structure for educational purposes
263+ print ("\n 8. PSBT STRUCTURE ANALYSIS" )
264+ print ("=" * 50 )
265+ print (f"Global data:" )
266+ print (f" - Unsigned transaction: { tx .serialize ()} " )
267+ print (f" - Version: { psbt .version } " )
268+ print (f" - Transaction type: P2WSH (Segwit multisig)" )
269+
270+ print (f"\n Input 0 data:" )
271+ print (f" - Previous TXID: { utxo ['txid' ]} " )
272+ print (f" - Previous Vout: { utxo ['vout' ]} " )
273+ print (f" - Witness Script: { witness_script .to_hex ()} " )
274+ print (f" - Amount: { utxo ['amount' ]} satoshis" )
275+ print (f" - Script type: P2WSH" )
276+ print (f" - Required signatures: 2 of 3" )
277+
278+ print (f"\n Output 0 data:" )
279+ print (f" - Amount: { send_amount } satoshis" )
280+ print (f" - Fee: { fee_amount } satoshis" )
281+ print (f" - Recipient: { charlie_p2wpkh_address } " )
282+ print (f" - Script type: P2WPKH" )
283+
284+ # Step 9: How to get real Testnet4 coins
285+ print ("\n 9. HOW TO GET REAL TESTNET4 COINS & TXID" )
286+ print ("=" * 50 )
287+ print ("COMPLETE WORKFLOW FOR REAL TESTNET4 DATA:" )
288+ print ()
289+ print ("PHASE 1: Setup" )
290+ print ("1. Run this script AS-IS to get your multisig address" )
291+ print ("2. Copy the P2WSH address from the output above" )
292+ print ()
293+ print ("PHASE 2: Get Testnet4 coins" )
294+ print ("3. Visit Testnet4 faucet:" )
295+ print (" • https://faucet.testnet4.dev/" )
296+ print (" • Request 0.001+ BTC to any address you control" )
297+ print ()
298+ print ("PHASE 3: Fund multisig" )
299+ print ("4. Send Testnet4 coins to your multisig address:" )
300+ print (f" • Send to: { p2wsh_address } " )
301+ print (" • Amount: 0.001 BTC (or whatever you got from faucet)" )
302+ print (" • Wait for 1+ confirmations" )
303+ print ()
304+ print ("PHASE 4: Get real TXID" )
305+ print ("5. Find your funding transaction:" )
306+ print (" • Use a Testnet4-compatible explorer" )
307+ print (" • Search for your multisig address" )
308+ print (" • Click on the funding transaction" )
309+ print (" • Copy the TXID" )
310+ print ()
311+ print ("PHASE 5: Update code" )
312+ print ("6. Edit get_real_testnet_utxo() function:" )
313+ print (" • Set txid = 'your_real_txid_here'" )
314+ print (" • Set amount = to_satoshis(your_actual_amount)" )
315+ print (" • Set is_placeholder = False" )
316+ print ()
317+ print ("PHASE 6: Verify & test" )
318+ print ("7. Re-run this script" )
319+ print (" • Should show REAL TESTNET4 DATA" )
320+ print (" • TXID should be verifiable on a Testnet4 explorer" )
321+ print (" • PSBT should be ready for signing" )
322+ print ()
323+ print ("EXAMPLE of real Testnet4 TXID format:" )
324+ print ("b4c1a58d7f8e9a2b3c4d5e6f1234567890abcdef1234567890abcdef12345678" )
325+ print ("(64 hex characters - yours will look similar)" )
326+ print ()
327+ print (" Your mentor can then verify:" )
328+ print ("• Paste your TXID into a Testnet4-compatible explorer" )
329+ print ("• See real transaction with real UTXOs" )
330+ print ("• Confirm PSBT references actual Testnet4 blockchain data" )
331+
332+ return psbt , {
333+ 'multisig_address' : p2wsh_address ,
334+ 'witness_script' : witness_script .to_hex (),
335+ 'recipient_address' : charlie_p2wpkh_address ,
336+ 'utxo' : utxo
337+ }
338+
339+
340+ if __name__ == "__main__" :
341+ created_psbt , info = main ()
342+
343+ print (f"\n " + "=" * 70 )
344+ print (" PSBT CREATION COMPLETED!" )
345+ print ("=" * 70 )
346+ print (f" PSBT (base64): { created_psbt .to_base64 ()} " )
347+ print ()
348+ print (" NEXT STEPS:" )
349+ print ("1. Fund the multisig address with real Testnet4 coins" )
350+ print ("2. Update the UTXO details in get_real_testnet_utxo()" )
351+ print ("3. Re-run this script" )
352+ print ("4. Sign the PSBT with 2 of the 3 private keys" )
353+ print ("5. Broadcast to Testnet4 and verify on a Testnet4-compatible explorer" )
354+ print ()
355+ print (f" Multisig address: { info ['multisig_address' ]} " )
356+ print (f" Check balance on a Testnet4-compatible explorer" )
0 commit comments