def test_open_wallet_watchonly_doesnt_exist(self): make_wallet_file(self) # watch-only wallet doesn't exist self.assertNotIn(self._testMethodName, self.rpc.listwallets()) # load wallet wallet = Wallet.open(self._testMethodName) # watch-only wallet was created self.assertIn(self._testMethodName, self.rpc.listwallets())
def broadcast(): wallet_name = request.json['wallet_name'] index = request.json['index'] wallet = Wallet.open(wallet_name) txid = wallet.broadcast(index) return jsonify({ 'txid': txid, })
def get_wallets(): from junction import Wallet # FIXME: circular imports wallets = [] wallet_names = get_wallet_names() for wallet_name in wallet_names: wallet = Wallet.open(wallet_name, ensure_watchonly=False) wallets.append(wallet) return wallets
def address(): # TODO: it would be better to generate addresses ahead of time and store them on the wallet # just not sure how to implement that wallet_name = request.json['wallet_name'] wallet = Wallet.open(wallet_name) address = wallet.derive_receiving_address() return jsonify({ 'address': address, })
def create_wallet(): node_args = request.json.pop('node') node = Node(**node_args, wallet_name=request.json['name'], network=request.json['network']) # check that node is reachable node.default_rpc.test() wallet = Wallet.create(**request.json, node=node) return jsonify(wallet.to_dict(True))
def update_node(): wallet_name = request.json.pop('wallet_name') wallet = Wallet.open(wallet_name) node_params = request.json node = Node(**node_params, wallet_name=wallet.name, network=wallet.network) node.default_rpc.test() wallet.node = node wallet.save() return jsonify({})
def test_create_wallet_wrong_parameters(self): wallet_name = self._testMethodName node = make_node(self) # m > n with self.assertRaises(JunctionError): Wallet.create( name=wallet_name, m=3, n=2, node=node, network=node.network, script_type='native', ) # n must be positive with self.assertRaises(JunctionError): Wallet.create( name=wallet_name, m=0, n=1, node=node, network=node.network, script_type='native', ) # m capped at 5 with self.assertRaises(JunctionError): Wallet.create( name=wallet_name, m=3, n=21, node=node, network=node.network, script_type='native', )
def test_save_wallet(self): '''Open and save is idempotent''' # TODO: try to make sure that wallet files can't be overwritten accidentally wallet = make_wallet(self) wallet_name = self._testMethodName wallet_file_path = os.path.join(self.wallet_dir, f'{wallet_name}.json') with open(wallet_file_path, 'r') as f: initial_contents = f.read() wallet = Wallet.open(wallet_name) wallet.save() with open(wallet_file_path, 'r') as f: final_contents = f.read() self.assertEqual(initial_contents, final_contents)
def make_wallet(testcase): wallet_name = testcase._testMethodName node = make_node(testcase) wallet = Wallet.create( name=wallet_name, m=2, n=3, node=node, network=node.network, script_type='native', ) for signer in signers: wallet.add_signer(**signer) return wallet
def sign_psbt(): wallet_name = request.json['wallet_name'] wallet = Wallet.open(wallet_name) fingerprint = request.json['device_id'] index = request.json['index'] old_psbt = wallet.psbts[index] with get_client_and_device(fingerprint, wallet.network) as (client, device): raw_signed_psbt = client.sign_tx(old_psbt)['psbt'] new_psbt = serializations.PSBT() new_psbt.deserialize(raw_signed_psbt) wallet.update_psbt(new_psbt, index) return jsonify({ 'psbt': new_psbt.serialize(), })
def create_psbt(): wallet_name = request.json['wallet_name'] wallet = Wallet.open(wallet_name) api_outputs = request.json['outputs'] outputs = [] for output in api_outputs: output_dict = {output['address']: output['btc']} outputs.append(output_dict) subtract_fees = [ index for index, output in enumerate(api_outputs) if output['subtract_fees'] ] wallet.create_psbt(outputs, subtract_fees=subtract_fees) return jsonify({ 'psbt': wallet.psbts[-1].serialize(), })
def test_add_signers(self): node = make_node(self) wallet = Wallet.create(name=self._testMethodName, m=2, n=3, node=node, script_type='native', network='regtest') # Wallet file created self.assertIn(f'{wallet.name}.json', os.listdir(self.wallet_dir)) # TODO: assert that it has correct attributes # Bitcoin Core watch-only wallet created self.assertIn(wallet.name, self.rpc.listwallets()) # Add first signer wallet.add_signer(**signers[0]) self.assertFalse(wallet.ready()) # Add second signer wallet.add_signer(**signers[1]) self.assertFalse(wallet.ready()) # Can't add same signer twice with self.assertRaises(JunctionError): wallet.add_signer(**signers[1]) # Add third signer wallet.add_signer(**signers[2]) self.assertTrue(wallet.ready()) # Can't add signers once wallet "ready" with self.assertRaises(JunctionError): wallet.add_signer(name='x', fingerprint='x', xpub='x', type='x', derivation_path='x')
def register_device(): wallet_name = request.json['wallet_name'] device_id = request.json['device_id'] wallet = Wallet.open(wallet_name) device = get_device(device_id) fingerprints = [signer.fingerprint for signer in wallet.signers] if device['fingerprint'] not in fingerprints: raise JunctionError( f'No device with fingerprint {device["fingerprint"]} present in wallet {wallet_name}' ) if device['type'] != 'coldcard': raise JunctionError( f'Devices of type {device["type"]} do not support multisig wallet registration' ) with hwi_lock: custom_coldcard.enroll(wallet) # TODO: How to keep track of whether or not this multisig wallet is registered on the coldcard? return jsonify({'ok': True})
def add_signer(): wallet_name = request.json['wallet_name'] signer_name = request.json['signer_name'] device_id = request.json['device_id'] wallet = Wallet.open(wallet_name) with get_client_and_device(device_id, wallet.network) as (client, device): derivation_path = wallet.account_derivation_path() # Get XPUB and validate against wallet.network xpub = client.get_pubkey_at_path(derivation_path)['xpub'] if 'xpub' == xpub[:4] and wallet.network != 'mainnet': raise JunctionError( 'Invalid xpub. Make sure your device is set to the correct chain.' ) if 'tpub' == xpub[:4] and wallet.network == 'mainnet': raise JunctionError( 'Invalid xpub. Make sure your device is set to the correct chain.' ) client.close() wallet.add_signer(name=signer_name, fingerprint=device['fingerprint'], type=device['type'], xpub=xpub, derivation_path=derivation_path) return jsonify(wallet.to_dict())
def prompt_device(): client_group.close() wallet_name = request.json['wallet_name'] wallet = Wallet.open(wallet_name) client_group.prompt_pin(wallet.network) return jsonify({}) # FIXME: what to do here when there's nothing to return
def display_address(): wallet_name = request.json['wallet_name'] address = request.json['address'] device_id = request.json['device_id'] wallet = Wallet.open(wallet_name) device = get_device(device_id) address_info = wallet.node.wallet_rpc.getaddressinfo(address) descriptor = address_info.get('desc') # HWI doesn't cover multisig, so we have to cover separately if wallet.is_multisig(): # Get redeem script if wallet.script_type == ScriptTypes.NATIVE: redeem_script = address_info.get('hex') else: redeem_script = address_info.get('embedded', {}).get('hex') # Make sure we have redeem_script and descriptor if not redeem_script or not descriptor: raise JunctionError('Unknown address') # Grab derivation path portions of descriptor derivation_paths = re.findall(r'\[(.*?)\]', descriptor) # Handle Trezors if device['type'] == 'trezor': # FIXME: give descriptor to custom_trezor.display_multisig_address and have it do this ... derivation_path = '' for path in derivation_paths: slash_index = path.index('/') path = 'm' + path[slash_index:] if derivation_path: assert derivation_path == path else: derivation_path = path with hwi_lock: custom_trezor.display_multisig_address( redeem_script, derivation_path, wallet.network != 'mainnet', device, wallet.script_type) # Handle ColdCards elif device['type'] == 'coldcard': with hwi_lock: custom_coldcard.display_multisig_address( redeem_script, derivation_paths, wallet.script_type == 'native') # Reject everything else else: raise JunctionError( f'Devices of type "{device["type"]}" do not support multisig address display' ) # HWI covers single-sig else: with get_client_and_device(device_id, wallet.network) as (client, device): with hwi_lock: commands.displayaddress(client, desc=descriptor) return jsonify({'ok': True})
def sync(): wallet_name = request.json['wallet_name'] wallet = Wallet.open(wallet_name) wallet.sync() return jsonify({})
def test_open_wallet_file_doesnt_exist(self): with self.assertRaises(FileNotFoundError): Wallet.open('test_open_wallet_doesnt_exist')