Calcular la dirección de Segwit a partir de la dirección pública

Me gustaría calcular la dirección segwit de una dirección pública.

First step: 
take the compressed public address:
1L88S26C5oyjL1gkXsBeYwHHjvGvCcidr9

do a sha256 on that address:
151de228f6bec7635097f7813786830505d04bf56806f11eb056441fdc917d41

do a ripemd160:
9bbfb2424372d687cf35fd7d1e978f85a11157ca

encode with Base58:
usT1ggHqtEAcGd4ZysWqtxaaQmT

Hay algo mal ya que cada dirección de segwit debe comenzar con "3". Alguien me puede ayudar cual es el paso?

Mi fuente de cómo se calcula una dirección de segwit es: ¿Cómo obtener una dirección heredada y una clave privada de la dirección de Segwit P2SH? Cita:

Tiene la clave privada, por lo que puede derivar la dirección de allí. Obtenga la clave pública correspondiente, haga un hash con SHA256 y luego ese resultado con RIPEMD160. Luego realice la codificación Base58Check en él.

junto a mi respuesta a continuación, consulte la respuesta aquí: bitcoin.stackexchange.com/questions/46455/…

Respuestas (3)

En primer lugar, es muy peligroso convertir una dirección pública a una forma diferente y luego usarla, porque el propietario de esa dirección no esperará pagos en otras formas y podría resultar en la pérdida de fondos. Utilice únicamente direcciones que el destinatario le haya pedido que utilice.

Pero hay una diferencia entre una clave pública comprimida y una dirección. Las direcciones de Bitcoin que comienzan con 1son un keyhash codificado en base58 con una suma de verificación. Para obtener el hash de la clave, que es el hash ripemd/sha256 de la clave pública, debe decodificar la base58 y asegurarse de que la suma de comprobación sea correcta. Luego, simplemente use el keyhash en el script de canje, no necesita realizar más hash para formar el script P2WPKH. Tenga en cuenta que no hay forma de saber si una dirección que comienza con un 1 usó una clave pública comprimida o sin comprimir antes de que se gaste, por lo que si intenta convertir una que tiene una clave sin comprimir, sería inutilizable, muy peligroso como dije anteriormente. .

Consulte aquí para obtener más detalles: https://bitcoincore.org/en/segwit_wallet_dev/#creation-of-p2sh-p2wpkh-address

El código Java que hace eso se pegó aquí , pero en su mayoría sin ninguna explicación, así que intentaré explicarlo.

Cuando estaba buscando información al respecto, el mejor ejemplo se encontró en el texto BIP-49 . Partimos de la siguiente línea:

// Address derivation
keyhash = HASH160(account0recvPublickKeyHex) = 0x38971f73930f6c141d977ac4fd4a727c854935b3

Entonces tenemos 20 bytes de hash de clave pública: <keyhash>. Si queremos obtener la clave heredada, simplemente anteponemos < prefixByte > (prefixByte = 0 para BTC main) y obtenemos < prefixByte | clave hash> matriz. Entonces deberíamos hacer HASH160(< prefixByte | keyhash >), y tomar los primeros 4 bytes como suma de verificación. Así que la dirección resultante parece < 0 | keyhash | suma de comprobación > - 25 bytes en total. Es una dirección en su forma hexadecimal. Para obtener un formulario común, solo debemos codificarlo en base58.

Si queremos obtener una dirección segwit, primero queremos obtener <keyhash>. Deberíamos decodificar la dirección codificada en base58 a un <arreglo de direcciones> y verificar la suma de verificación (que los últimos 4 bytes son iguales a HASH160 de los primeros 21). Si todo es correcto, debemos eliminar el primero y los últimos cuatro bytes de <array de direcciones> para obtener un <keyhash>.

El siguiente paso es obtener un <scriptSig>, simplemente anteponiendo 0x0014 bytes a una matriz <keyhash>:

scriptSig = < 0 < keyhash > > = 0x001438971f73930f6c141d977ac4fd4a727c854935b3

Obtenemos segwitBytes hash <scriptSig>:

segwitBytes = HASH160(scriptSig) = 0x336caa13e08b96080a32b5d818d59b4ab3b36742

Y finalmente, obtenemos una dirección, anteponemos p2shHeader (=5 para BTC MainNet, 196 para BTC Testnet) a segwitBytes, y agregamos su suma de verificación:

// segwitBytes base58check encoded for testnet
address = base58check(p2shHeader | segitBytes) = 2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2 (testnet)

HASH160 es lo siguiente: primero usamos la función SHA256 para hacer hash y luego RIPEMD160 en el resultado. El ejemplo implementado en Java se puede encontrar por ejemplo aquí (función org.bitcoinj.core.Utils.sha256hash160).

es posible que deba usar valores hexadecimales para realizar la conversión. Bitcoin no funciona con cadenas/caracteres... Cuando hago el cálculo en la terminal, obtengo su resultado (por ejemplo, para SHA256 a ripemd160):

printf 151de228f6bec7635097f7813786830505d04bf56806f11eb056441fdc917d41 | openssl dgst -ripemd160
(stdin)= 9bbfb2424372d687cf35fd7d1e978f85a11157ca

Pero este sería el camino equivocado. Primero necesitaría convertir a hexadecimal, así:

$ printf 151de228f6bec7635097f7813786830505d04bf56806f11eb056441fdc917d41 > tmp_sha256.txt
$ printf $( cat tmp_sha256.txt | sed 's/[[:xdigit:]]\{2\}/\\x&/g' ) > tmp_sha256.hex
$ hexdump -C tmp_sha256.hex 
00000000  15 1d e2 28 f6 be c7 63  50 97 f7 81 37 86 83 05  |...(...cP...7...|
00000010  05 d0 4b f5 68 06 f1 1e  b0 56 44 1f dc 91 7d 41  |..K.h....VD...}A|
00000020

entonces puedes convertir al siguiente paso como este:

$ openssl dgst -ripemd160 tmp_sha256.hex 
RIPEMD160(tmp_sha256.hex)= db151e871af66b1323893e3f527e22f7684718af

considerándolo todo:

$ printf 1L88S26C5oyjL1gkXsBeYwHHjvGvCcidr9 > adr.txt
$ printf $( cat adr.txt | sed 's/[[:xdigit:]]\{2\}/\\x&/g' ) >adr.hex
$ openssl dgst -sha256 -binary <adr.hex >tmp_sha256.hex
$ hexdump -C tmp_sha256.hex 
$ openssl dgst -ripemd160  <tmp_sha256.hex

devuelve una cadena ahora (56379c7bcd6b41188854e74169f844e8676cf8b8), que luego se codifica en base58 en esta dirección:

39YteymR86cG7V3Kijg8Gm2ST1r4nTeM1b

¿Cómo obtuviste 56379c7bcd6b41188854e74169f844e8676cf8b8?
cuando no proporciona un archivo de salida a openssl y elimina el "-binario", escribe en la consola: "openssl dgst -ripemd160 <tmp_sha256.hex". ¿Estás en sistemas Windows o Unixoide?
No, no estoy en Windows. realizar el tercer paso devuelve -bash: svn.hex: No existe tal archivo o directorio
Ups, debo ser consistente con mis nombres de archivo. debe ser "openssl dgst -sha256 -binary <adr.hex >tmp_sha256.hex". (Hice varios intentos, y primero usé los nombres de archivo "svn", y luego "adr"...). Acabo de comprobarlo dos veces, ahora funciona.
Sí, lo tengo ahora, pero convertirlo a base58 devuelve la herramienta 2CffxtJsCdzJEaHXHjSkvb12p12P: lenschulwitz.com/base58 ¿Estás usando bash o me perdí algo?
Este enfoque es bastante incorrecto, mira mi respuesta.
También obtengo "CffxtJsCdzJEaHXHjSkvb12p12P" como resultado. Desafortunadamente, no entiendo qué está mal.
Descubrí que falta algo al frente y al final de "56379c7bcd6b41188854e74169f844e8676cf8b8". ¿Me puede ayudar? Parece haber un "05" al frente y "6e1b34ba" al final. ¿Porqué es eso?
no, lo siento, nunca jugué con Java y funciones hash ... pero hay una biblioteca, donde se usa un código similar; tal vez esto ayude: github.com/ValleZ/Paper-Wallet/blob/master/app/src/main/java/ru/…
@Ac como dije, esta respuesta es incorrecta, no debe codificar la dirección, vea mi respuesta