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?
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 mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa
y 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
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 mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa
en 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é mpmath
which 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!
print(decode('mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa'))
imprime 6fa7370638600000000000000000000000000000000000000b
en lugar de 6fa73706385fffbf18855f2aee2a6168f29dbb597ef59c240b
(Python 3).Instale el base58
paquete (Ubuntu:) sudo apt-get install base58
y 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
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
pebwindkraft
loto28
pebwindkraft