import subprocess, json, os, time V='27.0'; DATA=os.path.expanduser('~/cksim/regtest-data') CLI=os.path.expanduser(f'~/bitcoin-{V}/bin/bitcoin-cli') def bc(*a, wallet=None): base=[CLI,f'-datadir={DATA}','-rpcport=18999','-rpcuser=ck','-rpcpassword=ckms'] if wallet is not None: base.append(f'-rpcwallet={wallet}') r=subprocess.run(base+list(a),capture_output=True,text=True) if r.returncode!=0: raise SystemExit(f'CLI {a} FAIL: {r.stderr.strip()}') return r.stdout.strip() TPUB={'00000001':'tpubREPLACE_WITH_YOUR_SIGNER_1_XPUB', '00000002':'tpubREPLACE_WITH_YOUR_SIGNER_2_XPUB', '00000003':'tpubREPLACE_WITH_YOUR_SIGNER_3_XPUB'} def desc(branch): keys=','.join(f'[{x}/48h/1h/0h/2h]{p}/{branch}/*' for x,p in TPUB.items()) return f'wsh(sortedmulti(2,{keys}))' for i in range(40): try: bc('getblockchaininfo'); break except SystemExit: time.sleep(2) def withck(d): return json.loads(bc('getdescriptorinfo', d))['descriptor'] rcv, chg = withck(desc(0)), withck(desc(1)) # miner wallet (has keys) to fund wl=json.loads(bc('listwallets')) if 'miner' not in wl: bc('createwallet','miner') if 'ckms23-watch' not in wl: bc('createwallet','ckms23-watch','true','true','','false','true') # disable_privkeys, blank, ..., descriptors bc('importdescriptors', json.dumps([{'desc':rcv,'active':True,'internal':False,'timestamp':'now','range':[0,20]}, {'desc':chg,'active':True,'internal':True,'timestamp':'now','range':[0,20]}]), wallet='ckms23-watch') maddr=bc('getnewaddress','','bech32', wallet='ckms23-watch') print('MULTISIG_ADDR', maddr) mineaddr=bc('getnewaddress', wallet='miner') bc('generatetoaddress','101',mineaddr, wallet='miner') bc('sendtoaddress',maddr,'2.5', wallet='miner') bc('generatetoaddress','1',mineaddr, wallet='miner') bal=bc('getbalance', wallet='ckms23-watch') print('WATCH_BALANCE', bal) dest=bc('getnewaddress', wallet='miner') funded=json.loads(bc('walletcreatefundedpsbt','[]',json.dumps([{dest:1.0}]),'0',json.dumps({'fee_rate':2,'change_type':'bech32'}), wallet='ckms23-watch')) open(os.path.expanduser('~/cksim/unsigned.psbt'),'w').write(funded['psbt']) print('UNSIGNED_PSBT_BYTES', len(funded['psbt'])) print('PSBT_HEAD', funded['psbt'][:40]) print('OK')