Derivar direcciones de Segwit desde xPub o zPub usando PYTHON

Resumen

Me estoy tirando de los pelos tratando de entender un par de conceptos. Me gustaría usar una billetera Segwit en mi Ledger Nano S para derivar claves públicas secundarias no reforzadas (?) (y, por lo tanto, direcciones segwit) que coincidan con las que produce mi Nano. Quiero hacer esto usando Python 3. Creo que crear una dirección como esta se llama "cartera de reloj", o al menos se describe en los documentos de Bip32 de esta manera:

Esto permite, por ejemplo, que una tienda web permita que su servidor web genere nuevas direcciones (hashes de clave pública) para cada pedido o para cada cliente, sin dar acceso al servidor web a las claves privadas correspondientes (que son necesarias para gastar los fondos recibidos). https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Motivación

Esto es precisamente lo que intento hacer usando Python para mi aplicación Django. No quiero usar un servicio de terceros porque me han dicho que no confíe en dar mi clave pública extendida (xPub). Quiero usar el xPub de mi Ledger Nano S. En realidad, no estoy completamente seguro de si mi Nano S usa un xpub o un zpub, pero lo he preguntado en una pregunta separada.

¿Tiene Ledger Nano S un xPub o un zPub para monederos Bitcoin?

De todos modos, convertí la clave "xpub" en una clave "zpub" e intenté derivar nuevas claves usando MUCHOS módulos de Python diferentes y sin éxito en la creación de una dirección que coincida o incluso se parezca a la que produce mi Nano.

lo que he probado

En aras de eliminar las respuestas que sugieren métodos que ya probé, aquí hay una lista completa y exhaustiva de todos los métodos que probé, con enlaces a todas las preguntas similares en Stack Exchange.

Al principio probé btclib junto con Pycoin .

Nota: en todos los ejemplos, intento usar 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 a esta pregunta: ¿Derivar clave pública de xpub? Recibo un error al usar la ruta de derivación "reforzada" y para que el error desaparezca, solo puedo usar la ruta 84/1/0/0/0. Incluso probé la ruta 0 y 0/0. Por supuesto, la ruta que me da mi Nano incluye el ('). Según tengo entendido, esta ruta de derivación es para claves zpub, pero incluso si la convierto a zpub, no me da una dirección que coincida con mi Nano. Supongo que esto se debe a que debería usar Bip44 o Bip84 para derivar direcciones. ¿Correcto?

Lo siguiente que intenté usar fue 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)

Este código funciona, sin embargo, el código de ejemplo proporcionado en la página de github de bip_utils sugiere que la siguiente línea de código debería ser:

# Derive account 0 for Bitcoin: m/44'/0'/0'
bip44_acc = bip44_ctx.Purpose().Coin().Account(0)

que me da el error

Bip44DepthError: Current depth (3) is not suitable for deriving purpose

Ya sé que no quiero m/44'/0'/0' pero los documentos no dan idea de cómo cambiar la ruta y mirar el código fuente tampoco me ayudó.

Luego probé usando pybitcointools del famoso Vitalik Buterin. Incluso tiene una parte en los documentos sobre "carteras de relojes":

Por razones de seguridad, la semilla y xprv idealmente deben almacenarse en frío únicamente. Si una aplicación web necesita poder proporcionar direcciones a pedido, la solución es usar una billetera de reloj, generada desde el xpub. Por ejemplo, tomemos el Dash xpub de un ejemplo anterior:

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()

Estoy pensando "¡Esto es todo, Vitalik es mi héroe! ¡Él sabe lo que estoy buscando!" Pero NO, este código de muestra solo funciona para Dash, aparentemente. Cuando cambio la clase a Bitcoin(), ninguno de los métodos "watch_wallet" descritos en los documentos funciona, o mejor dicho, no existen. No, donde hay un ejemplo de cómo hacer que esto funcione con Bitcoin.

Conclusión

De todos los métodos que probé, al menos btclib no me dio ningún error. Podría estar usando estos módulos de python incorrectamente o tal vez hay un mejor módulo/clase de python que debería estar usando. Cualquier dirección sería útil. Gracias de antemano.

Podría hacer eso con python-bip32 pero necesitaría convertir el pub de lo que sea a un xpub adecuado antes, consulte github.com/darosior/python-bip32/pull/21 . EDITAR: en realidad también mencionaste xpubs, en este caso puedes consultar el uso aquí github.com/darosior/python-bip32#usage

Respuestas (2)

Dado que no puede atravesar rutas reforzadas con xPubs, el libro mayor no muestra el Master xpub sino el xpub en la dirección de derivación 84/0'/0'o cualquiera que sea su número de cuenta. A partir de ahí, necesita dos derivaciones adicionales de 0/0 para obtener la 84/0'/0'/0/0clave pública final.

La clave pública resultante se puede utilizar para derivar una dirección P2PKH (sin segwit) o ​​una dirección P2WPKH (con segwit). Xpub y Zpub son codificaciones de la misma clave, pero Zpub también le indica a la billetera que obtenga una dirección P2WPKH de ella. Ledger no utiliza este estándar y prefiere codificarlo con la codificación Xpub más general (que no proporciona instrucciones).

Usando mi biblioteca Python de juguete , puede usar el siguiente script para derivar sus direcciones:

from cryptotools import Xpub

MY_XPUB = 'xpub-as-shown-in-ledger'

key = Xpub.decode(MY_XPUB)

pubkey0 = key/0/0
print(pubkey0.address('P2WPKH'))  # Tell the wallet that you want a P2WPKH address

si desea direcciones de testnet, configure envvar CRYPTOTOOLS_NETWOK = 'test'

¡Funcionó a las mil maravillas! Gracias. Eché un buen vistazo al código fuente y también lo bifurqué. Entiendo que tienes un descargo de responsabilidad sobre la seguridad. ¿Hay algo de lo que deba estar al tanto?

Pycoin ahora es compatible con segwit xpub después de esta confirmación . Es capaz de analizar xpub/ypub/zpub y derivar direcciones. Ledger todavía usa un prefijo incorrecto para segwit xpub y es posible que deba convertir xpub a ypub/zpub de manera adecuada