# Ledger Bitcoin application client
## Overview
Client library for Ledger Bitcoin application.
Main repository and documentation: https://github.com/LedgerHQ/app-bitcoin-new
## Install
If you just want to communicate through TCP socket (for example with the Speculos emulator), there is no dependency:
```bash
$ pip install ledger_bitcoin
```
otherwise, [hidapi](https://github.com/trezor/cython-hidapi) must be installed as an extra dependency:
```bash
$ pip install ledger_bitcoin[hid]
```
## Getting started
The main method exported by the library is `createClient`, which queries the hardware wallet for the version of the running app, and then returns the appropriate implementation of the `Client` class.
See the documentation of the class and the example below for the supported methods.
When running on a legacy version of the app (below version `2.0.0`), only the features that were available on the app are supported. Any unsopported method (e.g.: multisig registration or addresses, taproot addresses) will raise a `NotImplementedError`.
### Running with speculos
It is possible to run the app and the library with the [speculos](https://github.com/LedgerHQ/speculos) emulator.
⚠️ Currently, speculos does not correctly emulate the version of the app, always returning a dummy value; in order to use the library, it is necessary to set the `SPECULOS_APPNAME` environment variable before starting speculos, for example with:
```
$ export SPECULOS_APPNAME="Bitcoin Test:2.1.0"
```
Similarly, to test the library behavior on a legacy version of the app, one can set the version to `1.6.5` (the final version of the 1.X series).
The expected application name is `Bitcoin` for mainnet, `Bitcoin Test` for testnet.
### Example
The following example showcases all the main methods of the `Client`'s interface.
If you are not using the context manager syntax when creating the client, remember to call the `stop()` method to release the communication channel.
Testing the `sign_psbt` method requires producing a valid PSBT (with any external tool that supports either PSBTv0 or PSBTv2), and provide the corresponding wallet policy; it is skipped by default in the following example.
```python
from typing import Optional
from ledger_bitcoin import createClient, Chain, MultisigWallet, MultisigWallet, WalletPolicy, AddressType, TransportClient
from ledger_bitcoin.psbt import PSBT
def main():
# speculos on default host/port
# with createClient(TransportClient(), chain=Chain.TEST) as client:
# Ledger Nano connected via USB
with createClient(chain=Chain.TEST) as client:
# ==> Get the master key fingerprint
fpr = client.get_master_fingerprint().hex()
print(f"Master key fingerprint: {fpr}")
# ==> Get and display on screen the first taproot address
first_taproot_account_pubkey = client.get_extended_pubkey("m/86'/1'/0'")
first_taproot_account_policy = WalletPolicy(
"",
"tr(@0/**)",
[
f"[{fpr}/86'/1'/0']{first_taproot_account_pubkey}/**"
],
)
first_taproot_account_address = client.get_wallet_address(
first_taproot_account_policy,
None,
change=0,
address_index=0,
display=True # show address on the wallet's screen
)
print(f"First taproot account receive address: {first_taproot_account_address}")
# ==> Register a multisig wallet named "Cold storage"
our_pubkey = client.get_extended_pubkey("m/48'/1'/0'/2'")
other_key_info = "[76223a6e/48'/1'/0'/2']tpubDE7NQymr4AFtewpAsWtnreyq9ghkzQBXpCZjWLFVRAvnbf7vya2eMTvT2fPapNqL8SuVvLQdbUbMfWLVDCZKnsEBqp6UK93QEzL8Ck23AwF/**"
multisig_policy = MultisigWallet(
name="Cold storage",
address_type=AddressType.WIT,
threshold=2,
keys_info=[
other_key_info, # some other bitcoiner
f"[{fpr}/48'/1'/0'/2']{our_pubkey}", # that's us
],
)
policy_id, policy_hmac = client.register_wallet(multisig_policy)
print(f"Policy hmac: {policy_hmac.hex()}. Store it safely (together with the policy).")
assert policy_id == multisig_policy.id # should never fail
# ==> Derive and show an address for "Cold storage"
multisig_address = client.get_wallet_address(multisig_policy, policy_hmac, change=0, address_index=0, display=True)
print(f"Multisig wallet address: {multisig_address}")
# ==> Sign a psbt
# TODO: set a wallet policy and a valid psbt file in order to test psbt signing
psbt_filename: Optional[str] = None
signing_policy: Optional[WalletPolicy] = None
signing_policy_hmac: Optional[bytes] = None
if not psbt_filename or not signing_policy:
print("Nothing to sign :(")
return
raw_psbt_base64 = open(psbt_filename, "r").read()
psbt = PSBT()
psbt.deserialize(raw_psbt_base64)
result = client.sign_psbt(psbt, signing_policy, signing_policy_hmac)
print("Returned signatures:")
print(result)
if __name__ == "__main__":
main()
```
Raw data
{
"_id": null,
"home_page": "https://github.com/LedgerHQ/app-bitcoin-new",
"name": "ledger-bitcoin",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "",
"author": "Ledger",
"author_email": "hello@ledger.fr",
"download_url": "https://files.pythonhosted.org/packages/b2/80/5cfc334832a4d783e224a0e05f944f37d7f018871a56c62025c237564b7c/ledger_bitcoin-0.3.0.tar.gz",
"platform": null,
"description": "# Ledger Bitcoin application client\n\n## Overview\n\nClient library for Ledger Bitcoin application.\n\nMain repository and documentation: https://github.com/LedgerHQ/app-bitcoin-new\n\n## Install\n\nIf you just want to communicate through TCP socket (for example with the Speculos emulator), there is no dependency:\n\n```bash\n$ pip install ledger_bitcoin\n```\n\notherwise, [hidapi](https://github.com/trezor/cython-hidapi) must be installed as an extra dependency:\n\n```bash\n$ pip install ledger_bitcoin[hid]\n```\n\n## Getting started\n\nThe main method exported by the library is `createClient`, which queries the hardware wallet for the version of the running app, and then returns the appropriate implementation of the `Client` class.\n\nSee the documentation of the class and the example below for the supported methods.\n\nWhen running on a legacy version of the app (below version `2.0.0`), only the features that were available on the app are supported. Any unsopported method (e.g.: multisig registration or addresses, taproot addresses) will raise a `NotImplementedError`.\n\n### Running with speculos\n\nIt is possible to run the app and the library with the [speculos](https://github.com/LedgerHQ/speculos) emulator.\n\n\u26a0\ufe0f Currently, speculos does not correctly emulate the version of the app, always returning a dummy value; in order to use the library, it is necessary to set the `SPECULOS_APPNAME` environment variable before starting speculos, for example with:\n\n```\n$ export SPECULOS_APPNAME=\"Bitcoin Test:2.1.0\"\n```\n\nSimilarly, to test the library behavior on a legacy version of the app, one can set the version to `1.6.5` (the final version of the 1.X series).\n\nThe expected application name is `Bitcoin` for mainnet, `Bitcoin Test` for testnet.\n\n### Example\n\nThe following example showcases all the main methods of the `Client`'s interface.\n\nIf you are not using the context manager syntax when creating the client, remember to call the `stop()` method to release the communication channel.\n\nTesting the `sign_psbt` method requires producing a valid PSBT (with any external tool that supports either PSBTv0 or PSBTv2), and provide the corresponding wallet policy; it is skipped by default in the following example.\n\n\n```python\nfrom typing import Optional\nfrom ledger_bitcoin import createClient, Chain, MultisigWallet, MultisigWallet, WalletPolicy, AddressType, TransportClient\nfrom ledger_bitcoin.psbt import PSBT\n\n\ndef main():\n # speculos on default host/port\n # with createClient(TransportClient(), chain=Chain.TEST) as client:\n\n # Ledger Nano connected via USB\n with createClient(chain=Chain.TEST) as client:\n # ==> Get the master key fingerprint\n\n fpr = client.get_master_fingerprint().hex()\n print(f\"Master key fingerprint: {fpr}\")\n\n # ==> Get and display on screen the first taproot address\n\n first_taproot_account_pubkey = client.get_extended_pubkey(\"m/86'/1'/0'\")\n first_taproot_account_policy = WalletPolicy(\n \"\",\n \"tr(@0/**)\",\n [\n f\"[{fpr}/86'/1'/0']{first_taproot_account_pubkey}/**\"\n ],\n )\n first_taproot_account_address = client.get_wallet_address(\n first_taproot_account_policy,\n None,\n change=0,\n address_index=0,\n display=True # show address on the wallet's screen\n )\n\n print(f\"First taproot account receive address: {first_taproot_account_address}\")\n\n # ==> Register a multisig wallet named \"Cold storage\"\n\n our_pubkey = client.get_extended_pubkey(\"m/48'/1'/0'/2'\")\n other_key_info = \"[76223a6e/48'/1'/0'/2']tpubDE7NQymr4AFtewpAsWtnreyq9ghkzQBXpCZjWLFVRAvnbf7vya2eMTvT2fPapNqL8SuVvLQdbUbMfWLVDCZKnsEBqp6UK93QEzL8Ck23AwF/**\"\n\n multisig_policy = MultisigWallet(\n name=\"Cold storage\",\n address_type=AddressType.WIT,\n threshold=2,\n keys_info=[\n other_key_info, # some other bitcoiner\n f\"[{fpr}/48'/1'/0'/2']{our_pubkey}\", # that's us\n ],\n )\n\n policy_id, policy_hmac = client.register_wallet(multisig_policy)\n\n print(f\"Policy hmac: {policy_hmac.hex()}. Store it safely (together with the policy).\")\n\n assert policy_id == multisig_policy.id # should never fail\n\n # ==> Derive and show an address for \"Cold storage\"\n\n multisig_address = client.get_wallet_address(multisig_policy, policy_hmac, change=0, address_index=0, display=True)\n print(f\"Multisig wallet address: {multisig_address}\")\n\n # ==> Sign a psbt\n\n # TODO: set a wallet policy and a valid psbt file in order to test psbt signing\n psbt_filename: Optional[str] = None\n signing_policy: Optional[WalletPolicy] = None\n signing_policy_hmac: Optional[bytes] = None\n if not psbt_filename or not signing_policy:\n print(\"Nothing to sign :(\")\n return\n\n raw_psbt_base64 = open(psbt_filename, \"r\").read()\n psbt = PSBT()\n psbt.deserialize(raw_psbt_base64)\n\n result = client.sign_psbt(psbt, signing_policy, signing_policy_hmac)\n\n print(\"Returned signatures:\")\n print(result)\n\nif __name__ == \"__main__\":\n main()\n```\n",
"bugtrack_url": null,
"license": "",
"summary": "Client for Ledger Nano Bitcoin application",
"version": "0.3.0",
"project_urls": {
"Bug Tracker": "https://github.com/LedgerHQ/app-bitcoin-new/issues",
"Homepage": "https://github.com/LedgerHQ/app-bitcoin-new"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "460e9ee7e51fd030e8cf3760af4f54b45c7aed19d52d354f23d8c3574002618c",
"md5": "752717a9dfd2893cce6b0619628bb1ae",
"sha256": "e7c33404d02044c3810b294a510f7ad97bc65ab12dbdd180d873f2b4ebc0711a"
},
"downloads": -1,
"filename": "ledger_bitcoin-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "752717a9dfd2893cce6b0619628bb1ae",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 118718,
"upload_time": "2024-02-15T14:45:54",
"upload_time_iso_8601": "2024-02-15T14:45:54.585213Z",
"url": "https://files.pythonhosted.org/packages/46/0e/9ee7e51fd030e8cf3760af4f54b45c7aed19d52d354f23d8c3574002618c/ledger_bitcoin-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "b2805cfc334832a4d783e224a0e05f944f37d7f018871a56c62025c237564b7c",
"md5": "5f33b7cc215d94842b644aff623035d7",
"sha256": "ad9cdeaf33a45562bbd5bae6751025b869a2f81d6eb0267dd062a01f5925a4d5"
},
"downloads": -1,
"filename": "ledger_bitcoin-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "5f33b7cc215d94842b644aff623035d7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 103125,
"upload_time": "2024-02-15T14:47:58",
"upload_time_iso_8601": "2024-02-15T14:47:58.051281Z",
"url": "https://files.pythonhosted.org/packages/b2/80/5cfc334832a4d783e224a0e05f944f37d7f018871a56c62025c237564b7c/ledger_bitcoin-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-15 14:47:58",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "LedgerHQ",
"github_project": "app-bitcoin-new",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "ledger-bitcoin"
}