Estaba convencido de que la única diferencia entre la generación de direcciones que comienzan con '1' y '3' era simplemente cambiar el prefijo agregado al resumen de 0x00 a 0x05 después de la parte ripemd160.
Aunque las direcciones que obtengo no coinciden con las direcciones de la billetera Bitcoin Core, cuando importo la misma clave privada y también desde https://segwitaddress.org/ . ¿El prefijo 04 sigue igual o también cambia? Hay algo que me estoy perdiendo. ¿Podrias ayudarme por favor? Gracias.
[EDITAR]
Este es el método que tengo en este momento y todavía no funciona. Seguramente estoy malinterpretando algo..
def getPublicAddress(self, digest):
oSk = ecdsa.SigningKey.from_string(digest, curve=ecdsa.SECP256k1)
oVk = oSk.get_verifying_key()
hexlify = codecs.getencoder('hex')
self.pubkey = str(hexlify(b'\04' + oVk.to_string())[0].decode('utf-8'))
ripemd160 = hashlib.new('ripemd160')
keyhash = hashlib.sha256(codecs.decode(self.pubkey, "hex")).digest()
ripemd160.update(keyhash)
redeem_script = hashlib.new('ripemd160')
redeem_script.update(b'\x00\x14' + ripemd160.digest())
prefix = b'\x05'
m = prefix + redeem_script.digest()
checksum = hashlib.sha256(hashlib.sha256(m).digest()).digest()[:4]
return base58.b58encode(m + checksum)
P2PKH, P2SH y Segwit son tipos de direcciones diferentes. Las direcciones de Segwit y P2SH no son las mismas.
Las direcciones P2PKH y P2SH se generan de manera similar. P2PKH toma el hash160 de una clave pública (RIPMED160 del SHA256 de la clave pública), agrega el byte de versión 0x00
al hash160 y Base58 Check lo codifica.
Las direcciones P2SH son la codificación Base58 Check del hash160 de un script (conocido como el script redimido). Utiliza un byte de versión de 0x05
en su lugar. El resto de la codificación es la misma, solo la codificación Base58 Check.
Para las direcciones de Segwit, hay varios tipos. Hay direcciones segwit nativas que siguen el estándar Bech32. También hay direcciones segwit envueltas en P2SH.
Para un P2WPKH (hash de clave pública de pago para presenciar) envuelto en una dirección P2SH, el script de redimir es 0x0014 <hash 160 of the pubkey>
. Ese redimirScript está cifrado y codificado de la forma típica de P2SH.
Para un P2WSH (hash de script de pago para testigo) envuelto en una dirección P2SH, el script de testigo (redeemScript pero para direcciones de segwit) primero se codifica con SHA256. Entonces el P2SH redimeScript es 0x0020 <SHA256 of witnessScript>
. El hash160 de este script redimido luego se codifica en la forma típica de P2SH.
En cuanto a su código, está agregando 0x04
a su clave pública, lo cual es simplemente incorrecto. El 0x04
no es parte de la codificación de la dirección, es parte de la codificación de la clave pública en sí. Su generador de clave pública ya debería estar haciendo esto por usted. Tenga en cuenta que si la clave pública está comprimida, el byte de prefijo será 0x02
o 0x03
(depende del valor Y de la clave pública) en lugar de 0x04
lo que es para las claves públicas sin comprimir.
[Diferencia técnica - Solución de ejemplo]
`
def hash160(self, v):
r = hashlib.new('ripemd160')
r.update(hashlib.sha256(v).digest())
return r
def doublehash256(self, v):
return hashlib.sha256(hashlib.sha256(v).digest())
def ecdsaSECP256k1(self, digest):
# SECP256k1 - Bitcoin elliptic curve
sk = ecdsa.SigningKey.from_string(digest, curve=ecdsa.SECP256k1)
return sk.get_verifying_key()
def publicaddress1(self):
prefix_a = b'\x04'
prefix_b = b'\x00'
digest = self.privkeyhex.digest()
p = prefix_a + self.ecdsaSECP256k1(digest).to_string() # 1 + 32 bytes + 32 bytes
self.pubkey = str(binascii.hexlify(p).decode('utf-8'))
hash160 = self.hash160(p)
m = prefix_b + hash160.digest()
checksum = self.doublehash256(m).digest()[:4]
self.pubaddr1 = base58.b58encode(m + checksum)
def publicaddress3(self):
prefix_even = b'\x02'
prefix_odd = b'\x03'
prefix_a = prefix_odd
prefix_b = b'\x05'
prefix_redeem = b'\x00\x14'
digest = self.privkeyhex.digest()
ecdsa_digest = self.ecdsaSECP256k1(digest).to_string()
x_coord = ecdsa_digest[:int(len(ecdsa_digest)/2)]
y_coord = ecdsa_digest[int(len(ecdsa_digest)/2):]
if (int(binascii.hexlify(y_coord),16) % 2 == 0): prefix_a = prefix_even
p = prefix_a + x_coord
self.pubkeycompressed = str(binascii.hexlify(p).decode('utf-8'))
redeem_script = self.hash160(prefix_redeem + self.hash160(p).digest()).digest() # 20 bytes
m = prefix_b + redeem_script
checksum = self.doublehash256(m).digest()[:4]
self.pubaddr3 = base58.b58encode(m + checksum)`
Segwit dentro de P2SH:
redeem_script = hash160(b'\x00\x14' + hash160(public_key))
0x00 -> Versión testigo, 0x14 -> 20 (o 32) bytes empujar código de operación
prefix = b'\x05'
-> red principal P2SH
checksum = double_sha256(prefix + redeem_script)[:4]
address = base58(prefix + redeem_script + checksum)
fuerte
assert len(string) == curve.baselen, (len(string), curve.baselen) AssertionError: (38, 32)
espera un tamaño de 32 bytes. ¿Debería simplemente ingresar el resumen de la clave privada como el clásico P2PKH? Mientras tanto, pasaré a la parte del valor Y. Muchas gracias.andres chow
fuerte