Summary
I'm pulling my hair out trying to understand a couple of concepts. I would like to use a Segwit wallet in my Ledger Nano S to derive unhardened(?) child public keys (and thus segwit addresses) that match those that my Nano produces. I want to do this using Python 3. I believe creating an address like this is called a "watch wallet", or at least it is described in the Bip32 docs like this:
This permits for example a webshop business to let its webserver generate fresh addresses (public key hashes) for each order or for each customer, without giving the webserver access to the corresponding private keys (which are required for spending the received funds). https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Motivation
This is precisely what I'm trying to do using Python for my Django application. I do not want to use a third party service as I'm told not to trust giving out my extended public key (xPub). I want to use the xPub from my Ledger Nano S. Actually, I'm not entirely sure if my Nano S uses an xpub or a zpub, but I have asked this in a separate question.
Does Ledger Nano S Have an xPub or a zPub for Bitcoin wallets?
Regardless, I have converted the "xpub" key to a "zpub" key and attempted to derive new keys from both using MANY different python modules and with zero success in creating an address that matches or even looks similar to that which my Nano produces.
What I've Tried
For the sake of eliminating answers that suggest methods I've already tried here is a complete and exhaustive list of every method I have tried, with links to all similar questions on Stack Exchange.
At first I tried btclib along with Pycoin.
Note: in all examples I'm attempting to use the Bitcoin Testnet
from btclib import bip32
from pycoin.networks.registry import network_for_netcode
network = network_for_netcode("XTN") # XTN for the Testnet, BTC for the Mainnet
xpub = "x(z/t)pub-big-long-key-that-starts-with-tpub-zpub-xpub"
child_xpub_key = bip32.derive(xpub, "84'/1'/0'/0/0")
key = network.parse.bip32(child_xpub_key)
print(str("Key: {}").format(child_xpub_key))
print(str("Address: {}").format(key.address()))
Similar to this question: Derive public key from xpub? I get an error using the "hardened" derivation path and in order for the error to go away I can only use the path 84/1/0/0/0. I have even tried path 0 and 0/0. Of course the path my Nano gives me includes the ('). From my understanding this derivation path is for zpub keys but even if I convert to zpub it doesn't give me an address that matches my Nano. I assume this is because I should be using Bip44 or Bip84 to derive addresses? Correct?
The next thing I attempted to use was bip_utils
from bip_utils import Bip44, Bip44Coins
key_str = "x(z/t)pub-big-long-key-that-starts-with-tpub-zpub-xpub"
bip44_ctx = Bip44.FromExtendedKey(key_str, Bip44Coins.BITCOIN_TESTNET)
This code works, however the example code given at the bip_utils github page suggests that the next line of code should be:
# Derive account 0 for Bitcoin: m/44'/0'/0'
bip44_acc = bip44_ctx.Purpose().Coin().Account(0)
Which gives me the error
Bip44DepthError: Current depth (3) is not suitable for deriving purpose
I already know that I don't want m/44'/0'/0' but the docs give no clue how to change the path and looking at the source code didn't help me either.
Then I tried using pybitcointools by the famous Vitalik Buterin. He even has a part in the docs about "watch wallets":
For security reasons the seed and xprv should ideally be held in cold storage only. If a web application needs to be able to provide addresses on demand, the solution is to use a watch wallet, generated from the xpub. For example, let's take the Dash xpub from a previous example:
from cryptos import *
coin = Dash()
xpub = 'xpub-some-xpub-key'
wallet = coin.watch_wallet(xpub)
wallet.is_watching_only
wallet.new_receiving_address()
wallet.new_change_address()
I'm thinking "this is it, Vitalik is my hero! He know's what I'm looking for!" But NO, this sample code only works for Dash, apparently. When I change the class to Bitcoin(), none of the "watch_wallet" methods described in the docs work, or rather they don't exist. No where is an example of how to make this work with Bitcoin.
Conclusion
Of all the methods I tried, at least btclib didn't give me any errors. I could be using these python modules incorrectly or perhaps there is a better python module/class that I should be using. Any direction would be helpful. Thank you in advance.