Получение адреса биткойна 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, изменив последний шаг производства адресов

(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:], чтобы избавиться от нулей

Другие вопросы по тегам