Получение адреса биткойна Hash160 в python
tl;dr Как выполнить Hash160, используя большинство основных инструментов python?
================================================== ==
Привет,
Я пытаюсь выяснить, как работают транзакции в биткойнах.
Когда я выбираю входы для новой передачи, я хочу убедиться, что они принадлежат определенному адресу. Однако существующие txs не указывают адреса предыдущих выходов, а вместо этого содержат хеш-адрес адреса.
например:
>> 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
}
Скажем, я хочу проверить, какой из выходов можно отправить с адреса mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa
Поэтому я беру свой Hash160 в Python:
>> hashlib.new('ripemd160', hashlib.sha256("mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa".encode('utf-8')).digest()).hexdigest()
'748598cd9b004aecf8a2d97464fb1f2a90562ffe'
Это не тот результат, которого я ожидал: a73706385fffbf18855f2aee2a6168f29dbb597e
Между тем, этот онлайн-сервис правильно вычисляет хэш.
Как я могу использовать Hash160 для биткойн-адреса в Python?
2 ответа
Наконец-то я это сделал. Некоторые откровения в моем ответе могут показаться вам очевидными и основополагающими, но я надеюсь, что они будут полезны для пользователей, совершенно новых для биткойнов (таких как я).
========
Вики говорит, что я могу получить Hash160, изменив последний шаг производства адресов
Этот шаг кодирует строку байтов с алфавитом base58
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
Этому алфавиту не хватает 0, I, l, O, так как эти символы легко перепутать. И это последнее, что вы хотите сделать, когда один неправильный символ может привести к потере большого количества денег.
Таким образом, нам нужно повернуть mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa
в байтовую строку. Байты идут в шестнадцатеричном формате и могут варьироваться от 0x00
(0 десятичных) до 0xff
(255 десятичных). И помните, что у нас есть специальный алфавит b58: расшифровка адреса с помощью utf-8 или других стандартов кодирования приведет к бессмысленности.
Сначала я подумал, что могу легко декодировать адрес с помощью этой функции:
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'
Но результат не похож на хеш, который я посмотрел в транзакции (a73706385fffbf18855f2aee2a6168f29dbb597e
). Таким образом, я узнал, что понятия не имею, как происходит декодирование. Что делать, если Hash160 имеет 0xff
? В b58 такого символа нет, так как 58 в гексе - это просто 0x3a
, При декодировании b58 мы не можем обрабатывать каждый символ независимо. Весь адрес составляет одно гигантское число, записанное в числовой системе base58 (его первая цифра соответствует 58**34).
Чтобы получить байтовую строку, я сначала превратил это число в десятичную и только потом в байтовую строку.
Если вы знаете, как избежать этого обхода и получать байты напрямую - прокомментируйте
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'
Этот вывод выглядит как то, что мне нужно (a7370638...
) но имеет слишком много нулей. Не смотри что первый байт (6f
) не соответствует: он не имеет ничего общего с Hash160, который нам нужен, только версия протокола.
Это, вероятно, ошибка точности. Чтобы справиться с этим, я использовал mpmath
который позволяет вам работать с целыми числами точно.
from mpmath import *
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 = mpf(dec % 256)
dec = 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)
Примените точную операцию по модулю, и мы наконец сможем получить Hash160. Просто убедитесь, что вы обрезали первые и последние 4 байта, которые содержат проверку жирных пальцев.
x = decode('mvm74FACaagz94rjWbNmW2EmhJdmEGcxpa')
print(x)
>> 6fa73706385fffbf18855f2aee2a6168f29dbb597ef59c240b
print(x[2:-8])
>> a73706385fffbf18855f2aee2a6168f29dbb597e
Ура! Как в сделке!
Похоже, что вы пытались сделать это самостоятельно, и я не думаю, что ваше решение будет эффективным, когда дело доходит до производительности.
Логика, которой вы придерживались, верна, но все можно сделать лучше, зная, что в base58 есть пакет для декодирования и кодирования, и он называется base58.
python -m pip install base58
Ниже приведен простой способ получить хеш-код адреса биткойна pinemd160, закодированный в base58 (python 2.7):
>>>import base58
>>>adr58 = '1Q2TWHE3GMdB6BZKafqwxXtWAWgFt5Jvm3'
>>>adr160 = base58.b58decode_check(adr58).encode('hex')[2:]
>>>print (adr160)
fc916f213a3d7f1369313d5fa30f6168f9446a2d
обратите внимание на decod_check, чтобы рассмотреть контрольную сумму и [2:], чтобы избавиться от нулей