Биткойн — техническая разница между генерацией адресов P2PKH и P2SH?

Я был убежден, что единственная разница между генерацией адресов, начинающихся с «1» и «3», заключалась в простом изменении префикса, добавляемого в дайджест, с 0x00 на 0x05 после частиripemd160.

Хотя адреса, которые я получаю, не совпадают с адресами из кошелька Bitcoin Core при импорте того же приватного ключа, а также из https://segwitaddress.org/ . Префикс 04 остается прежним или тоже меняется? Что-то мне не хватает. Не могли бы вы помочь? Спасибо.

[РЕДАКТИРОВАТЬ]

Это метод, который у меня есть на данный момент, и он все еще не работает. Я точно что-то не так понимаю..

def getPublicAddress(self, digest):

    oSk = ecdsa.SigningKey.from_string(digest, curve=ecdsa.SECP256k1)                    
    oVk = oSk.get_verifying_key()

    hexlify = codecs.getencoder('hex')             
    self.pubkey = str(hexlify(b'\04' + oVk.to_string())[0].decode('utf-8'))

    ripemd160 = hashlib.new('ripemd160')
    keyhash = hashlib.sha256(codecs.decode(self.pubkey, "hex")).digest()
    ripemd160.update(keyhash)


    redeem_script = hashlib.new('ripemd160')
    redeem_script.update(b'\x00\x14' + ripemd160.digest())

    prefix = b'\x05' 
    m = prefix + redeem_script.digest()
    checksum = hashlib.sha256(hashlib.sha256(m).digest()).digest()[:4]        

    return base58.b58encode(m + checksum)

Ответы (2)

P2PKH, P2SH и Segwit — это разные типы адресов. Адреса Segwit и P2SH не совпадают.

Адреса P2PKH и P2SH генерируются аналогичным образом. P2PKH берет хэш160 открытого ключа (RIPMED160 из SHA256 открытого ключа), добавляет байт версии 0x00к хэшу160, и проверка Base58 кодирует его.

Адреса P2SH представляют собой кодировку Base58 Check для хэша160 скрипта (известного как redeemScript). 0x05Вместо этого используется байт версии . Остальная кодировка такая же, только кодировка Base58 Check.

Для адресов Segwit существует несколько типов. Существуют собственные адреса segwit, соответствующие стандарту Bech32. Существуют также адреса segwit, обернутые P2SH.

Для P2WPKH (хэш публичного ключа с оплатой за свидетеля), заключенного в адрес P2SH, redeemScript имеет вид 0x0014 <hash 160 of the pubkey>. Этот redeemScript хешируется и кодируется обычным способом P2SH.

Для P2WSH (хэш сценария с оплатой за свидетель), обернутый в адрес P2SH, свидетельский скрипт (скрипт redeemScript, но для адресов segwit) сначала хешируется с помощью SHA256. Тогда сценарий выкупа P2SH — это 0x0020 <SHA256 of witnessScript>. Хэш160 этого redeemScript затем кодируется обычным способом P2SH.


Что касается вашего кода, вы добавляете 0x04свой открытый ключ, что просто неверно. Это 0x04не часть кодировки адреса, это часть самой кодировки открытого ключа. Ваш генератор открытых ключей уже должен делать это за вас. Обратите внимание, что если открытый ключ сжат, байт префикса будет либо 0x02( 0x03зависит от значения Y открытого ключа), а 0x04не для несжатых открытых ключей.


[Техническое отличие — пример решения]

`

    def hash160(self, v):
        r = hashlib.new('ripemd160')
        r.update(hashlib.sha256(v).digest())
        return r


    def doublehash256(self, v):
        return hashlib.sha256(hashlib.sha256(v).digest())


    def ecdsaSECP256k1(self, digest):
        # SECP256k1 - Bitcoin elliptic curve 
        sk = ecdsa.SigningKey.from_string(digest, curve=ecdsa.SECP256k1)                    
        return sk.get_verifying_key()

   def publicaddress1(self):

        prefix_a = b'\x04'
        prefix_b = b'\x00'

        digest = self.privkeyhex.digest()

        p = prefix_a + self.ecdsaSECP256k1(digest).to_string() # 1 + 32 bytes + 32 bytes
        self.pubkey = str(binascii.hexlify(p).decode('utf-8'))

        hash160 = self.hash160(p)

        m = prefix_b + hash160.digest()
        checksum = self.doublehash256(m).digest()[:4]        

        self.pubaddr1 = base58.b58encode(m + checksum)                  


    def publicaddress3(self):

        prefix_even = b'\x02'
        prefix_odd = b'\x03'
        prefix_a = prefix_odd
        prefix_b = b'\x05'
        prefix_redeem = b'\x00\x14'


        digest = self.privkeyhex.digest()

        ecdsa_digest = self.ecdsaSECP256k1(digest).to_string()

        x_coord = ecdsa_digest[:int(len(ecdsa_digest)/2)]
        y_coord = ecdsa_digest[int(len(ecdsa_digest)/2):]            

        if (int(binascii.hexlify(y_coord),16) % 2 == 0): prefix_a = prefix_even

        p = prefix_a + x_coord

        self.pubkeycompressed = str(binascii.hexlify(p).decode('utf-8'))


        redeem_script = self.hash160(prefix_redeem + self.hash160(p).digest()).digest() # 20 bytes

        m = prefix_b + redeem_script
        checksum = self.doublehash256(m).digest()[:4]        

        self.pubaddr3 = base58.b58encode(m + checksum)`
Спасибо за помощь. Так что в основном мне не хватает той части, где мне нужно передать сжатый закрытый ключ wif и после этого проверить значение Y. Теперь проблема в том, что ECDSA ожидает входной дайджест размером 32 байта, и если я декодирую сжатый base58 WIF 51 байт + x01 и передаю в ECDSA, это дает мне эту ошибку: assert len(string) == curve.baselen, (len(string), curve.baselen) AssertionError: (38, 32)он ожидает размер 32 байта. Должен ли я просто ввести дайджест закрытого ключа, как классический P2PKH? Тем временем я перейду к части значения Y. Большое спасибо.
Ваше декодирование WIF звучит так, как будто оно неправильное. Вы должны получить 34-байтовый объект. Первый байт — это номер версии, а последний байт указывает на степень сжатия, но относится только к WIF. Вы должны удалить эти два байта, и у вас будет 32-байтовый объект, который является закрытым ключом. Обработка сжатия открытого ключа происходит позже, поэтому вам нужно что-то еще помнить, сжат открытый ключ или нет.
Спасибо. Чего мне не хватало, так это разделения дайджеста (координат) по 32 байта. Остальное было легко реализовать. Выложу решение..

Сегвит внутри P2SH:

redeem_script = hash160(b'\x00\x14' + hash160(public_key))

0x00 -> версия-свидетель, 0x14 -> 20 (или 32) байт кода операции push

prefix = b'\x05'-> Основная сеть P2SH

checksum = double_sha256(prefix + redeem_script)[:4]
address = base58(prefix + redeem_script + checksum)
Спасибо за вашу помощь! Я заметил, что эти шаги в основном такие же, как у них здесь bitcoincore.org/en/segwit_wallet_dev ? Не в состоянии понять несколько вещей. Где взять эту функцию hash160? Вы имеете в виду ripemd160? Эти фрагменты размещаются после создания x00 или вместо? Я также не совсем понимаю, что они упоминают под «открытым ключом, используемым в P2SH-P2WPKH, ДОЛЖЕН быть сжат, т.е. размером 33 байта, и начинаться с 0x02 или 0x03»..
@fortesp hash160(x) — это expiremd160(sha256(x)). Вы можете найти больше информации о том, что такое сжатый открытый ключ, здесь bitcoin.stackexchange.com/questions/3059/…