talonlab / python-hdwallet

Python-based library for the implementation of a Hierarchical Deterministic (HD) Wallet generator supporting more than 200 cryptocurrencies.
https://hdwallet.readthedocs.io
MIT License
482 stars 151 forks source link

Get addresses from xpublic key #64

Closed bjorntimmer closed 3 days ago

bjorntimmer commented 2 years ago

Hi,

I'm trying the example https://github.com/meherett/python-hdwallet/blob/master/examples/from_xpublic_key.py to use with my Ledger Hardware Wallet to get all addresses to watch my balances.

From Ledger Live I go to my Bitcoin account and go to the account settings where I get the following:

{
  "xpub": "xpub[xPub-key]",
  "index": 0,
  "freshAddressPath": "84'/0'/0'/0/9",
  "id": "js:2:bitcoin:xpub[xPub-key]:native_segwit",
  "blockHeight": 748956
}

And use the following code:

#!/usr/bin/env python3
from hdwallet import HDWallet as HDWallet
from hdwallet.utils import is_root_xpublic_key
from hdwallet.symbols import BTC as SYMBOL

# Strict for root xpublic key
STRICT: bool = False
# Bitcoin root xpublic key
XPUBLIC_KEY: str = "xpub[xPub-key]"

if STRICT:
    # Check root xpublic key
    assert is_root_xpublic_key(
        xpublic_key=XPUBLIC_KEY, symbol=SYMBOL), "Invalid Root XPublic Key."

# Initialize Bitcoin mainnet HDWallet
hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
# Get Bitcoin HDWallet from xpublic key
hdwallet.from_xpublic_key(xpublic_key=XPUBLIC_KEY, strict=False)

for i in range(10):
    print('Index {}'.format(str(i)))
    hdwallet.from_path("m/84/0/0/0/{}".format(str(i)))
    # hdwallet.from_path("m/0/{}".format(str(i)))
    print("P2WPKH Address:", hdwallet.p2wpkh_address())
    print('')

When I set the STRICT variable to True I get the message "Invalid Root Xpublic Key." When I set it to False I'm able to generate addresses, however I don't get addresses that where generated by Ledger Live.

Did get anyone working code for the xpub with Ledger Live?

rfreis commented 2 years ago

Hi @bjorntimmer, I'm not part of the repository's team but will try to help where I can. I might be wrong so, please, consider this as an attempt to help you doing your own research.

When you set STRICT=True it forces you to work only with the private key, because to get the addresses with hardened derivations, you need the private key.

As you are already using unhardened derivation and you want to use the public key in a watch only mode, I suggest you is to keep STRICT=False and change the derivation path.

My first shot would be m/0/9.

Ideally, you should try to get the xpub from the following path: m/84'/0'/0'

I tried something similar but with a bip 44 xpub:

  1. got the xpub for m/44'/0'/0'
  2. derived using the m/0/{index} like you are trying to do
  3. use hdwallet.clean_derivation() before moving to the next index on item 2
rfreis commented 2 years ago

Hi @bjorntimmer, I'm not part of the repository's team but will try to help where I can. I might be wrong so, please, consider this as an attempt to help you doing your own research.

When you set STRICT=True it forces you to work only with the private key, because to get the addresses with hardened derivations, you need the private key.

As you are already using unhardened derivation and you want to use the public key in a watch only mode, I suggest you is to keep STRICT=False and change the derivation path.

My first shot would be m/0/9.

Ideally, you should try to get the xpub from the following path: m/84'/0'/0'

I tried something similar but with a bip 44 xpub:

  1. got the xpub for m/44'/0'/0'
  2. derived using the m/0/{index} like you are trying to do
  3. use hdwallet.clean_derivation() before moving to the next index on item 2

something like this:

hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
hdwallet.from_xpublic_key(xpublic_key=XPUBLIC_KEY, strict=False) # try getting the m/84'/0'/0' xpub instead of the root xpub

for i in range(10):
    print('Index {}'.format(str(i)))
    hdwallet.from_path("m/0/{}".format(str(i)))
    print("P2WPKH Address:", hdwallet.p2wpkh_address())
    print('')
    hdwallet.clean_derivation()