在 python 中获取 Hash160 比特币地址

Getting Hash160 bitcoin address in python

tl;博士 如何使用最基本的 python 工具执行 Hash160?

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

嗨,

我想弄明白,比特币交易是如何运作的。

当我为新交易选择输入时,我想确保它们属于特定地址。然而,现有的 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

同时,this 在线服务正确计算哈希值。

如何在 Python 中对比特币地址进行 Hash160?

我终于做到了。 我的回答中的一些启示对你来说可能看起来很明显和基本,但我希望它们对比特币完全陌生的人(比如我)有所帮助。

========

Wiki说我把地址生成的最后一步倒过来就可以得到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。只需确保 trim 进行胖手指检查的第一个和最后 4 个字节。

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

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

耶!就像在交易中一样!

看起来你自己尝试做这件事遇到了很多麻烦,而且我认为你的解决方案在性能方面不会有效。

您遵循的逻辑是正确的,但知道有一个包可以在 base58 中解码和编码,它被称为 base58

python -m pip install base58

下面是获取以 base58 (python 2.7) 编码的比特币地址的 ripemd160 哈希值的简单方法:

>>>import base58
>>>adr58 = '1Q2TWHE3GMdB6BZKafqwxXtWAWgFt5Jvm3'
>>>adr160 = base58.b58decode_check(adr58).encode('hex')[2:]
>>>print (adr160)
fc916f213a3d7f1369313d5fa30f6168f9446a2d

注意 decod_check 考虑校验和和 [2:] 去除零