¿Cómo producir una dirección de bitcoin Hash160?

tl; dr ¿Cómo se debe realizar Hash160, utilizando la mayoría de las herramientas básicas?

================================================== ==

Hola,

Estoy tratando de averiguar cómo funcionan las transacciones en bitcoin.

Cuando elijo entradas para un nuevo tx, quiero asegurarme de que pertenecen a una dirección específica. Sin embargo, los tx existentes no especifican las direcciones de las salidas anteriores, sino que contienen hashes de direcciones.

p.ej:

>> bx fetch-tx 11a1b7ac0a65bd50b7094c720aecd77cfd83d84b1707960fd00dd82a888aab5c --config /home/theo/Desktop/bx-testnet.cfg

{
    hash 11a1b7ac0a65bd50b7094c720aecd77cfd83d84b1707960fd00dd82a888aab5c
    inputs
    {
        input
        {
            address_hash f3b7278583827a049d6be894bf7f516178a0c8e6
            previous_output
            {
                hash 4a3532061d43086299ae9b2409a456bb9638dff32e0858c4ccda27203fb2e4f6
                index 1
            }
            script "[30440220146b8b5b014245a9e27e21122d4dded04c3f39c3a49ac2494743d6f6ae8efff602206d417a4be9c7431ea69699132438510ade1cf8d746607f77d114907762ed1eb301] [023dd2e892290e41bb78efce6ea30a97015ef13eaaa9ebb7b0514485fc365cc391]"
            sequence 4294967295
        }
    }
    lock_time 0
    outputs
    {
        output
        {
            address_hash a73706385fffbf18855f2aee2a6168f29dbb597e
            script "dup hash160 [a73706385fffbf18855f2aee2a6168f29dbb597e] equalverify checksig"
            value 130000000
        }
        output
        {
            address_hash ad6e80394af99ece5d7701bf2f457480b93965b7
            script "dup hash160 [ad6e80394af99ece5d7701bf2f457480b93965b7] equalverify checksig"
            value 49525957813
        }
    }
    version 1
}

Digamos, quiero verificar cuál de las salidas se puede enviar desde la dirección mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa, así que tomo su Hash160 en Python:

>> hashlib.new('ripemd160', hashlib.sha256("mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa".encode('utf-8')).digest()).hexdigest()
'748598cd9b004aecf8a2d97464fb1f2a90562ffe'

Ese no es el resultado que esperaba:a73706385fffbf18855f2aee2a6168f29dbb597e

Mientras tanto, este servicio en línea calcula el hash correctamente.

¿Cómo hago Hash160 una dirección de bitcoin, preferiblemente en Python?

Si bien no puedo ayudar con Python, es posible que desee consultar " righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html ", tiene una buena explicación. Además, no estoy seguro de entender su lógica con las direcciones desde las que gastar. Usaría los UTXO de transacciones anteriores... Así que usaría "listunspent" en la billetera central, para ver la ID de tx anterior y el vout, para usar en una transacción nueva (sin firmar). Un hash de dirección no se puede revertir fácilmente, por eso tenemos funciones hash. Fácil de ir en una dirección, imposible de revertir. Eso nos da la seguridad en toda la red bitcoin.
@pebwindkraft Pero necesito el script en la salida anterior para firmar, ¿no? Además, me parece que entendí mal la idea de codificación. Hash160 claramente tiene bytes más grandes que 3a (58 decimal), por lo que mi función de decodificación no tiene sentido
sí, le das el scriptPubKey, mira el famoso ejemplo de 19 pasos y algo de Python al final: aquí: bitcoin.stackexchange.com/questions/3374/…

Respuestas (4)

La dirección ya es un hash, junto con una suma de comprobación de 4 bytes y un byte de versión. Para pasar de una dirección a un hash160, no tiene que calcular sha256 o ripemd160 de nada. Solo tiene que decodificarlo de base58 a hexadecimal y descartar la basura no deseada.

Si tomas mvm74FACaagz94rjWbNmW2EmhJdmEGcxpay decodificas base58, obtienes

 6FA73706385FFFBF18855F2AEE2A6168F29DBB597EF59C240B
 VVxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxSSSSSSSS

El primer byte (6F) es el byte de versión; Descártalo. Los últimos 4 bytes son la suma de comprobación; Descártalo. Los 20 bytes restantes son el hash160 de la clave pública correspondiente a la dirección.

Ya respondí esta pregunta en StackOverflow , pero supongo que volveré a publicar la respuesta aquí.

Finalmente lo he logrado. Algunas revelaciones en mi respuesta pueden parecerle obvias y básicas, pero espero que sean útiles para las personas que son completamente nuevas en Bitcoin (como yo).


Wiki dice que puedo obtener Hash160 invirtiendo el último paso de la producción de direcciones

ingrese la descripción de la imagen aquí(Hash160 está resaltado)

Este paso es codificar una cadena de bytes con el alfabeto base58

b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

Este alfabeto carece de 0, I, l, O, ya que estos símbolos son fáciles de confundir. Y eso es lo último que desea hacer cuando un símbolo incorrecto puede llevarlo a perder una gran cantidad de dinero.

Por lo tanto, necesitamos convertirnos mvm74FACaagz94rjWbNmW2EmhJdmEGcxpaen una cadena de bytes. Los bytes van en formato hexadecimal y pueden variar de 0x00(0 decimal) a 0xff(255 decimal). Y tenga en cuenta que tenemos que lidiar con un alfabeto b58 especial: decodificar la dirección con utf-8 u otros estándares de codificación producirá una tontería.

Al principio pensé que podía decodificar fácilmente la dirección con esta función:

def decode(addr):
    b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    decoded = ''
    for i in addr:
        temp = hex(b58.index(i))
        if len(temp) == 3:
            temp = '0' + temp[-1]
        else:
            temp = temp[2:]
        decoded += (temp)
    return (decoded)

decode('mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa')

>> '2c352c06030e090b212127390803312a1d22152c1d010d2c2811242c0d0f23372f21'

Pero el resultado no se parece en nada al hash que busqué en la transacción ( a73706385fffbf18855f2aee2a6168f29dbb597e). De esta manera aprendí que no tenía idea de cómo se hace la decodificación. ¿Qué pasa si Hash160 tiene 0xff? No existe tal símbolo en b58, ya que 58 en hexadecimal es solo 0x3a. Mientras decodificamos b58, no podemos tratar cada símbolo de forma independiente. La dirección completa forma un número gigante escrito en sistema numérico base58 (su primer dígito corresponde a 58**34).

Para obtener la cadena de bytes, primero convertí este número en un decimal y luego en una cadena de bytes.

Si sabe cómo evitar este desvío y obtener bytes directamente, comente

def decode(addr):

    b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

    def base58_to_dec(addr):
        dec = 0
        for i in range(len(addr)):
            dec = int(dec * 58 + b58.index(addr[i]))
        print('Decimal representation')
        print(dec)
        return(dec)

    def dec_to_byte(dec):
        out = ''
        while dec != 0:
            print(dec)
            remn = dec % 256
            dec = int((dec - remn) / 256)
            temp = hex(remn)
            if len(temp) == 3:
                temp = '0' + temp[-1]
            else:
                temp = temp[2:]
            out = temp + out
        return(out)

    dec = base58_to_dec(addr)
    out = dec_to_byte(dec)
    return (out)

decode("mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa")
>> Decimal representation
>> 700858390993795610399098743129153130886272689085970101576715
>> '6fa7370638600000000000000000000000000000000000000b'

Esa salida se parece un poco a lo que necesito ( a7370638...), pero tiene demasiados ceros. No mires que el primer byte ( 6f) no coincide: no tiene nada que ver con el Hash160 que necesitamos, solo la versión del protocolo.

Es probable que se trate de un error de precisión. Para solucionarlo, utilicé mpmathwhich te permite operar con números enteros de forma precisa.

import mpmath as mp
mp.dps = 1000

def decode(addr):

    b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

    def base58_to_dec(addr):
        dec = 0
        for i in range(len(addr)):
            dec = int(dec * 58 + b58.index(addr[i]))
        return(dec)

    def dec_to_byte(dec):
        out = ''
        while dec != 0:
            remn = mp.mpf(dec % 256)
            dec = mp.mpf((dec - remn) / 256)
            temp = hex(int(remn))
            if len(temp) == 3:
                temp = '0' + temp[-1]
            else:
                temp = temp[2:]
            out = temp + out

        return (out)

    dec = base58_to_dec(addr)
    out = dec_to_byte(dec)
    return (out)

Aplique la operación de módulo preciso y finalmente podemos obtener el Hash160. Solo asegúrese de activar los primeros y últimos 4 bytes que llevan el control de dedo gordo.

x = decode('mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa')
print(x)
>> 6fa73706385fffbf18855f2aee2a6168f29dbb597ef59c240b

print(x[2:-8])
>> a73706385fffbf18855f2aee2a6168f29dbb597e

¡Hurra! ¡Al igual que en la transacción!

Probé el último ejemplo de código e imprime el valor incorrecto. print(decode('mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa'))imprime 6fa7370638600000000000000000000000000000000000000ben lugar de 6fa73706385fffbf18855f2aee2a6168f29dbb597ef59c240b(Python 3).

Instale el base58paquete (Ubuntu:) sudo apt-get install base58y luego ejecute lo siguiente:

$ printf mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa | base58 -d | xxd -p
6fa73706385fffbf18855f2aee2a6168f29dbb597ef59c240b

Para decodificar en dirección, use:

$ printf mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa | base58 -d | cut -c2- | xxd -p | head -c40
a73706385fffbf18855f2aee2a6168f29dbb597e

Pitón 3

Instale base58 ( pip3 install base58) y use el siguiente código:

import base58
print(base58.b58decode_check(b'mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa').hex()[2:])

Un trazador de líneas:

$ echo 'import base58; print(base58.b58decode_check(b"mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa").hex()[2:])' | python3
a73706385fffbf18855f2aee2a6168f29dbb597e

Script para convertir archivo con lista de direcciones a los hashes correspondientes:

while read addr; do python3 -c "import base58; print(base58.b58decode_check(b'$addr').hex()[2:])"; done < btc.csv > btc.hash