Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Bitcoin Hardware Wallet Interface
# Dash Hardware Wallet Interface

[![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/HWI.svg)](https://cirrus-ci.com/github/bitcoin-core/HWI)
[![Documentation Status](https://readthedocs.org/projects/hwi/badge/?version=latest)](https://hwi.readthedocs.io/en/latest/?badge=latest)
Comment on lines 3 to 4
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check git remote to understand repository ownership
git remote -v

Repository: dashpay/HWI

Length of output: 166


🏁 Script executed:

# Search for Dash-related references in the codebase
rg -i "dash" --type md --type py --type txt -c

Repository: dashpay/HWI

Length of output: 166


🏁 Script executed:

# Check the full README.md to understand context
head -20 README.md

Repository: dashpay/HWI

Length of output: 166


🏁 Script executed:

# Look for any repository metadata or setup files that indicate fork status
fd -e "setup.py" -e "pyproject.toml" -e ".github" | head -5

Repository: dashpay/HWI

Length of output: 166


Update CI/CD badge URLs to point to the Dash fork.

The badges currently reference github/bitcoin-core/HWI but should reference github/dashpay/HWI to match this repository and the updated title "Dash Hardware Wallet Interface".

🤖 Prompt for AI Agents
In README.md around lines 3 to 4, the CI/CD badge image and link reference
github/bitcoin-core/HWI but should point to github/dashpay/HWI; update both
badge image URLs and their target links to use github/dashpay/HWI (and keep
existing badge parameters intact) so the badges reflect the Dash fork and
repository name.


The Bitcoin Hardware Wallet Interface is a Python library and command line tool for interacting with hardware wallets.
The Dash Hardware Wallet Interface is a Python library and command line tool for interacting with hardware wallets.
It provides a standard way for software to work with hardware wallets without needing to implement device specific drivers.
Python software can use the provided library (`hwilib`). Software in other languages can execute the `hwi` tool.

Expand Down Expand Up @@ -31,7 +31,7 @@ brew install libusb
## Install

```
git clone https://github.com/bitcoin-core/HWI.git
git clone https://github.com/dashpay/HWI.git
cd HWI
poetry install # or 'pip3 install .' or 'python3 setup.py install'
```
Expand Down Expand Up @@ -93,10 +93,11 @@ Documentation for HWI can be found on [readthedocs.io](https://hwi.readthedocs.i

For documentation on devices supported and how they are supported, please check the [device support page](https://hwi.readthedocs.io/en/latest/devices/index.html#support-matrix)

### Using with Bitcoin Core
### Using with Dash Core

See [Using Bitcoin Core with Hardware Wallets](https://hwi.readthedocs.io/en/latest/examples/bitcoin-core-usage.html).

## License

This project is a fork of Bitcoin HWI: https://github.com/bitcoin-core/hwi
This project is available under the MIT License, Copyright Andrew Chow.
9 changes: 6 additions & 3 deletions hwilib/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ def get_parser() -> HWIArgumentParser:
enumerate_parser.set_defaults(func=enumerate_handler)

getmasterxpub_parser = subparsers.add_parser('getmasterxpub', help='Get the extended public key for BIP 44 standard derivation paths. Convenience function to get xpubs given the address type, account, and chain type.')
getmasterxpub_parser.add_argument("--addr-type", help="Get the master xpub used to derive addresses for this address type", type=AddressType.argparse, choices=list(AddressType), default=AddressType.WIT) # type: ignore
# [DASHIFIED] default address type is changed to legacy
getmasterxpub_parser.add_argument("--addr-type", help="Get the master xpub used to derive addresses for this address type", type=AddressType.argparse, choices=list(AddressType), default=AddressType.LEGACY) # type: ignore
getmasterxpub_parser.add_argument("--account", help="The account number", type=int, default=0)
getmasterxpub_parser.set_defaults(func=getmasterxpub_handler)

Expand All @@ -178,7 +179,8 @@ def get_parser() -> HWIArgumentParser:
kparg_group.add_argument('--nokeypool', action='store_false', dest='keypool', help='Indicates that the keys are not to be imported to the keypool', default=False)
getkeypool_parser.add_argument('--internal', action='store_true', help='Indicates that the keys are change keys')
kp_type_group = getkeypool_parser.add_mutually_exclusive_group()
kp_type_group.add_argument("--addr-type", help="The address type (and default derivation path) to produce descriptors for", type=AddressType.argparse, choices=list(AddressType), default=AddressType.WIT) # type: ignore
# [DASHIFIED] default address type is changed to legacy
kp_type_group.add_argument("--addr-type", help="The address type (and default derivation path) to produce descriptors for", type=AddressType.argparse, choices=list(AddressType), default=AddressType.LEGACY) # type: ignore
kp_type_group.add_argument('--all', action='store_true', help='Generate addresses for all standard address types (default paths: ``m/{44,49,84}h/0h/0h/[0,1]/*)``')
Comment on lines +182 to 184
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update help text to reflect legacy-only support.

The default address type is correctly changed to LEGACY, but the help text on Line 184 still references paths m/{44,49,84}h/0h/0h/[0,1]/*. Since only BIP44 (purpose 44) is now supported, the help text should be updated.

Apply this diff to update the help text:

-    kp_type_group.add_argument('--all', action='store_true', help='Generate addresses for all standard address types (default paths: ``m/{44,49,84}h/0h/0h/[0,1]/*)``')
+    kp_type_group.add_argument('--all', action='store_true', help='Generate addresses for all standard address types (default path: ``m/44h/5h/0h/[0,1]/*`` for Dash mainnet)')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# [DASHIFIED] default address type is changed to legacy
kp_type_group.add_argument("--addr-type", help="The address type (and default derivation path) to produce descriptors for", type=AddressType.argparse, choices=list(AddressType), default=AddressType.LEGACY) # type: ignore
kp_type_group.add_argument('--all', action='store_true', help='Generate addresses for all standard address types (default paths: ``m/{44,49,84}h/0h/0h/[0,1]/*)``')
# [DASHIFIED] default address type is changed to legacy
kp_type_group.add_argument("--addr-type", help="The address type (and default derivation path) to produce descriptors for", type=AddressType.argparse, choices=list(AddressType), default=AddressType.LEGACY) # type: ignore
kp_type_group.add_argument('--all', action='store_true', help='Generate addresses for all standard address types (default path: ``m/44h/5h/0h/[0,1]/*`` for Dash mainnet)')
🤖 Prompt for AI Agents
In hwilib/_cli.py around lines 182 to 184, the help text for the --all flag
still references multiple BIP purposes (m/{44,49,84}h/0h/0h/[0,1]/*) even though
the default and supported address type is LEGACY (BIP44) only; update the help
string to reference the legacy/BIP44-only derivation path (e.g.
m/44h/0h/0h/[0,1]/*) so it correctly reflects legacy-only support.

getkeypool_parser.add_argument('--account', help='BIP43 account', type=int, default=0)
getkeypool_parser.add_argument('--path', help='Derivation path, default follows BIP43 convention, e.g. ``m/84h/0h/0h/1/*`` with --addr-type wpkh --internal. If this argument and --internal is not given, both internal and external keypools will be returned.')
Expand All @@ -194,7 +196,8 @@ def get_parser() -> HWIArgumentParser:
group = displayaddr_parser.add_mutually_exclusive_group(required=True)
group.add_argument('--desc', help='Output Descriptor. E.g. wpkh([00000000/84h/0h/0h]xpub.../0/0), where 00000000 must match --fingerprint and xpub can be obtained with getxpub. See doc/descriptors.md in Bitcoin Core')
group.add_argument('--path', help='The BIP 32 derivation path of the key embedded in the address, default follows BIP43 convention, e.g. ``m/84h/0h/0h/1/*``')
displayaddr_parser.add_argument("--addr-type", help="The address type to display", type=AddressType.argparse, choices=list(AddressType), default=AddressType.WIT) # type: ignore
# [DASHIFIED] default address type is changed to legacy
displayaddr_parser.add_argument("--addr-type", help="The address type to display", type=AddressType.argparse, choices=list(AddressType), default=AddressType.LEGACY) # type: ignore
displayaddr_parser.set_defaults(func=displayaddress_handler)

setupdev_parser = subparsers.add_parser('setup', help='Setup a device. Passphrase protection uses the password given by -p. Requires interactive mode')
Expand Down
19 changes: 12 additions & 7 deletions hwilib/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ def find_device(
pass # Ignore things we wouldn't get fingerprints for
return None

def getmasterxpub(client: HardwareWalletClient, addrtype: AddressType = AddressType.WIT, account: int = 0) -> Dict[str, str]:
# [DASHIFIED] default address type is changed to legacy
def getmasterxpub(client: HardwareWalletClient, addrtype: AddressType = AddressType.LEGACY, account: int = 0) -> Dict[str, str]:
"""
Get the master extended public key from a client

Expand Down Expand Up @@ -238,7 +239,8 @@ def getkeypool_inner(
internal: bool = False,
keypool: bool = True,
account: int = 0,
addr_type: AddressType = AddressType.WIT
# [DASHIFIED] default address type is changed to legacy
addr_type: AddressType = AddressType.LEGACY
) -> List[Dict[str, Any]]:
"""
:meta private:
Expand Down Expand Up @@ -276,7 +278,8 @@ def getdescriptor(
master_fpr: bytes,
path: Optional[str] = None,
internal: bool = False,
addr_type: AddressType = AddressType.WIT,
# [DASHIFIED] default address type is changed to legacy
addr_type: AddressType = AddressType.LEGACY,
account: int = 0,
start: Optional[int] = None,
end: Optional[int] = None
Expand Down Expand Up @@ -334,12 +337,12 @@ def getdescriptor(
p &= ~HARDENED_FLAG
path_suffix += "/{}{}".format(p, "h" if hardened else "")
path_suffix += "/*"

# Get the key at the base
if client.xpub_cache.get(path_base) is None:
client.xpub_cache[path_base] = client.get_pubkey_at_path(path_base).to_string()

pubkey = PubkeyProvider(origin, client.xpub_cache.get(path_base, ""), path_suffix)
# [DASHIFIED] default address type is changed to legacy
if addr_type is AddressType.LEGACY:
return PKHDescriptor(pubkey)
elif addr_type is AddressType.SH_WIT:
Expand All @@ -361,7 +364,8 @@ def getkeypool(
internal: bool = False,
keypool: bool = True,
account: int = 0,
addr_type: AddressType = AddressType.WIT,
# [DASHIFIED] default address type is changed to legacy
addr_type: AddressType = AddressType.LEGACY,
addr_all: bool = False
) -> List[Dict[str, Any]]:
"""
Expand Down Expand Up @@ -416,7 +420,6 @@ def getdescriptors(
:raises: BadArgumentError: if an argument is malformed or missing.
"""
master_fpr = client.get_master_fingerprint()

result = {}

for internal in [False, True]:
Expand All @@ -441,7 +444,8 @@ def displayaddress(
client: HardwareWalletClient,
path: Optional[str] = None,
desc: Optional[str] = None,
addr_type: AddressType = AddressType.WIT
# [DASHIFIED] default address type is changed to legacy
addr_type: AddressType = AddressType.LEGACY
) -> Dict[str, str]:
"""
Display an address on the device for client.
Expand All @@ -459,6 +463,7 @@ def displayaddress(
return {"address": client.display_singlesig_address(path, addr_type)}
elif desc is not None:
descriptor = parse_descriptor(desc)
# [DASHIFIED] default address type is changed to legacy
addr_type = AddressType.LEGACY
is_sh = isinstance(descriptor, SHDescriptor)
is_wsh = isinstance(descriptor, WSHDescriptor)
Expand Down
18 changes: 10 additions & 8 deletions hwilib/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ class Chain(Enum):
"""
The blockchain network to use
"""
MAIN = 0 #: Bitcoin Main network
TEST = 1 #: Bitcoin Test network
REGTEST = 2 #: Bitcoin Core Regression Test network
SIGNET = 3 #: Bitcoin Signet
TESTNET4 = 4 #: Bitcoin Test network
# [DASHIFIED] removed SIGNET, TESTNET4 and changed comments
MAIN = 0 #: Dash Main network
TEST = 1 #: Dash Test network
REGTEST = 2 #: Dash Core Regression Test network
# SIGNET = 3 #: Bitcoin Signet
# TESTNET4 = 4 #: Bitcoin Test network

def __str__(self) -> str:
return str(self.name).lower()
Expand All @@ -38,10 +39,11 @@ class AddressType(Enum):
"""
The type of address to use
"""
# [DASHIFIED] removed WIT, SH_WIT and TAP addresses
LEGACY = 1 #: Legacy address type. P2PKH for single sig, P2SH for scripts.
WIT = 2 #: Native segwit v0 address type. P2WPKH for single sig, P2WSH for scripts.
SH_WIT = 3 #: Nested segwit v0 address type. P2SH-P2WPKH for single sig, P2SH-P2WSH for scripts.
TAP = 4 #: Segwit v1 Taproot address type. P2TR always.
# WIT = 2 #: Native segwit v0 address type. P2WPKH for single sig, P2WSH for scripts.
# SH_WIT = 3 #: Nested segwit v0 address type. P2SH-P2WPKH for single sig, P2SH-P2WSH for scripts.
# TAP = 4 #: Segwit v1 Taproot address type. P2TR always.

def __str__(self) -> str:
return str(self.name).lower()
Expand Down
12 changes: 5 additions & 7 deletions hwilib/devices/jade.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,21 @@ class JadeClient(HardwareWalletClient):
MIN_SUPPORTED_FW_VERSION = semver.VersionInfo(0, 1, 32)
PSBT_SUPPORTED_FW_VERSION = semver.VersionInfo(0, 1, 47)

# [DASHIFIED] Signet is hidden
NETWORKS = {Chain.MAIN: 'mainnet',
Chain.TEST: 'testnet',
Chain.SIGNET: 'testnet', # same as far as Jade is concerned
Chain.REGTEST: 'localtest'}

def _network(self) -> str:
if self.chain not in self.NETWORKS:
raise BadArgumentError(f'Unhandled network: {self.chain}')
return self.NETWORKS[self.chain]

ADDRTYPES = {AddressType.LEGACY: 'pkh(k)',
AddressType.WIT: 'wpkh(k)',
AddressType.SH_WIT: 'sh(wpkh(k))'}
# [DASHIFIED] only legacy type is kept
ADDRTYPES = {AddressType.LEGACY: 'pkh(k)'}

MULTI_ADDRTYPES = {AddressType.LEGACY: 'sh(multi(k))',
AddressType.WIT: 'wsh(multi(k))',
AddressType.SH_WIT: 'sh(wsh(multi(k)))'}
# [DASHIFIED] only legacy type is kept
MULTI_ADDRTYPES = {AddressType.LEGACY: 'sh(multi(k))'}

@classmethod
def _convertAddrType(cls, addrType: AddressType, multisig: bool) -> str:
Expand Down
9 changes: 5 additions & 4 deletions hwilib/devices/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,14 @@ def check_keypath(key_path: str) -> bool:
]

# The priority of address types we want for signing.
# [DASHIFIED] re-ordered address types priorities. We have legacy only
# We want to do Taproot first, then segwit, then legacy
# Higher number is lower priority so that sort does not require reversing.
signing_priority = {
AddressType.TAP: 0,
AddressType.WIT: 1,
AddressType.SH_WIT: 2,
AddressType.LEGACY: 3,
AddressType.LEGACY: 0,
# AddressType.TAP: 0,
# AddressType.WIT: 1,
# AddressType.SH_WIT: 2,
}

def handle_chip_exception(e: Union[BTChipException, ApduException], func_name: str) -> bool:
Expand Down
5 changes: 3 additions & 2 deletions hwilib/devices/ledger_bitcoin/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,9 @@ def createClient(comm_client: Optional[TransportClient] = None, chain: Chain = C
base_client = Client(comm_client, chain)
app_name, app_version, _ = base_client.get_version()

if app_name not in ["Bitcoin", "Bitcoin Test", "Bitcoin Legacy", "Bitcoin Test Legacy", "app"]:
raise NotSupportedError(0x6A82, None, "Ledger is not in either the Bitcoin or Bitcoin Testnet app")
# [DASHIFIED] The name of app is updated
if app_name not in ["Dash", "Dash Test", "app"]:
raise NotSupportedError(0x6A82, None, "Ledger is not in either the Dash or Dash Testnet app")

app_version_major, app_version_minor, _ = app_version.split(".", 2)

Expand Down
6 changes: 4 additions & 2 deletions hwilib/hwwclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ def __init__(self, path: str, password: Optional[str], expert: bool, chain: Chai
"""
self.path = path
self.password = password
self.message_magic = b"\x18Bitcoin Signed Message:\n"
# [DASHIFIED] prefix for message signing
self.message_magic = b"\x19DarkCoin Signed Message:\n"
self.chain = chain
self.fingerprint: Optional[str] = None
# {bip32_path: <xpub string>}
self.xpub_cache: Dict[str, str] = {}
self.expert = expert

def get_master_xpub(self, addrtype: AddressType = AddressType.WIT, account: int = 0) -> ExtendedKey:
# [DASHIFIED] default address type is changed to legacy
def get_master_xpub(self, addrtype: AddressType = AddressType.LEGACY, account: int = 0) -> ExtendedKey:
"""
Retrieves a BIP 44 master public key

Expand Down
31 changes: 17 additions & 14 deletions hwilib/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,15 @@ def get_bip44_purpose(addrtype: AddressType) -> int:

:param addrtype: The address type
"""
# [DASHIFIED] commented out unused types of addresses
if addrtype == AddressType.LEGACY:
return 44
elif addrtype == AddressType.SH_WIT:
return 49
elif addrtype == AddressType.WIT:
return 84
elif addrtype == AddressType.TAP:
return 86
# elif addrtype == AddressType.SH_WIT:
# return 49
# elif addrtype == AddressType.WIT:
# return 84
# elif addrtype == AddressType.TAP:
# return 86
else:
raise ValueError("Unknown address type")

Expand All @@ -385,26 +386,28 @@ def get_bip44_chain(chain: Chain) -> int:
"""
Determine the BIP 44 coin type based on the Bitcoin chain type.

For the Bitcoin mainnet chain, this returns 0. For the other chains, this returns 1.
For the Dash mainnet chain, this returns 5. For the other chains, this returns 1.

:param chain: The chain
"""
# [DASHIFIED] changed type of chain to 5 (from 0 [bitcoin])
if chain == Chain.MAIN:
return 0
return 5
else:
return 1

def get_addrtype_from_bip44_purpose(index: int) -> Optional[AddressType]:
purpose = index & ~HARDENED_FLAG

# [DASHIFIED] commented out unused types of addresses
if purpose == 44:
return AddressType.LEGACY
elif purpose == 49:
return AddressType.SH_WIT
elif purpose == 84:
return AddressType.WIT
elif purpose == 86:
return AddressType.TAP
# elif purpose == 49:
# return AddressType.SH_WIT
# elif purpose == 84:
# return AddressType.WIT
# elif purpose == 86:
# return AddressType.TAP
else:
return None

Expand Down