示例#1
0
 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())
示例#2
0
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,
    })
示例#3
0
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
示例#4
0
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,
    })
示例#5
0
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))
示例#6
0
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({})
示例#7
0
 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',
         )
示例#8
0
 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)
示例#9
0
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
示例#10
0
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(),
    })
示例#11
0
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(),
    })
示例#12
0
    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')
示例#13
0
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})
示例#14
0
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())
示例#15
0
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
示例#16
0
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})
示例#17
0
def sync():
    wallet_name = request.json['wallet_name']
    wallet = Wallet.open(wallet_name)
    wallet.sync()
    return jsonify({})
示例#18
0
 def test_open_wallet_file_doesnt_exist(self):
     with self.assertRaises(FileNotFoundError):
         Wallet.open('test_open_wallet_doesnt_exist')