multisig-hsm/reference/build_wallet.py
mineracks 7a17ffd12e Initial public release — multisig HSM reference + recipe book
Open-source 2-of-3 policy-enforced threshold HSM: auto-signs cold→hot treasury
refills under on-device Coldcard policy, no human in the loop. Includes the full
operator manual + quick-start, the reference coordinator/signing code, and a
signer-host bootstrap. No keys, seeds, or secrets — placeholders only.

Live signet demo: https://multisighsm.mineracks.com

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:56:51 +10:00

41 lines
2.2 KiB
Python

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')