Bitcoin - Technischer Unterschied zwischen P2PKH- und P2SH-Adressgenerierung?

Ich war überzeugt, dass der einzige Unterschied zwischen der Generierung von Adressen, die mit '1' und '3' beginnen, einfach darin bestand, das dem Digest hinzugefügte Präfix von 0x00 auf 0x05 nach dem Teil von ricemd160 zu ändern.

Obwohl die Adressen, die ich bekomme, nicht mit den Adressen aus der Bitcoin Core-Brieftasche übereinstimmen, wenn derselbe private Schlüssel importiert wird, und auch von https://segwitaddress.org/ . Bleibt das Präfix 04 gleich oder ändert es sich auch? Da fehlt mir etwas. Kannst du bitte helfen? Danke dir.

[BEARBEITEN]

Dies ist die Methode, die ich im Moment habe und immer noch nicht funktioniert. Irgendwas interpretiere ich sicher falsch..

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)

Antworten (2)

P2PKH, P2SH und Segwit sind alle unterschiedliche Adresstypen. Segwit- und P2SH-Adressen sind nicht identisch.

P2PKH- und P2SH-Adressen werden auf ähnliche Weise generiert. P2PKH nimmt den Hash160 eines öffentlichen Schlüssels (RIPMED160 des SHA256 des öffentlichen Schlüssels), hängt das Versionsbyte 0x00an den Hash160 an und Base58 Check codiert es.

P2SH-Adressen sind die Base58-Check-Kodierung des Hash160 eines Skripts (bekannt als RedeemScript). 0x05Es verwendet stattdessen ein Versionsbyte von . Der Rest der Codierung ist die gleiche, nur die Base58-Check-Codierung.

Für Segwit-Adressen gibt es mehrere Typen. Es gibt native Segwit-Adressen, die dem Bech32-Standard folgen. Es gibt auch P2SH-umschlossene Segwit-Adressen.

Für einen P2WPKH (Pay-to-Witness-Pubkey-Hash), der in eine P2SH-Adresse verpackt ist, lautet das Einlöseskript 0x0014 <hash 160 of the pubkey>. Dieses RedeemScript wird auf die typische P2SH-Weise gehasht und codiert.

Für einen P2WSH (Pay-to-Witness-Skript-Hash), der in eine P2SH-Adresse verpackt ist, wird das WitnessScript (RedeemScript, außer für Segwit-Adressen) zuerst mit SHA256 gehasht. Dann ist das P2SH-Einlöseskript 0x0020 <SHA256 of witnessScript>. Der Hash160 dieses RedeemScripts wird dann auf die typische P2SH-Weise kodiert.


Was Ihren Code betrifft, hängen Sie 0x04an Ihren öffentlichen Schlüssel an, der einfach falsch ist. Das 0x04ist nicht Teil der Adresscodierung, sondern Teil der Codierung des öffentlichen Schlüssels selbst. Ihr Public-Key-Generator sollte dies bereits für Sie tun. Beachten Sie, dass, wenn der öffentliche Schlüssel komprimiert ist, das Präfix-Byte entweder 0x02oder 0x03(abhängig vom Y-Wert des öffentlichen Schlüssels) ist, anstatt 0x04dass es für unkomprimierte öffentliche Schlüssel gilt.


[Technischer Unterschied - Beispiellösung]

`

    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)`
Danke für Ihre Hilfe. Im Grunde fehlt mir also der Teil, in dem ich einen komprimierten privaten WIF-Schlüssel eingeben und danach nach dem Y-Wert suchen muss. Das Problem ist nun, dass der ECDSA einen 32-Byte-Input-Digest erwartet und wenn ich base58-komprimiertes WIF 51 Byte + x01 dekodiere und in ECDSA einfüge, erhalte ich diesen Fehler: assert len(string) == curve.baselen, (len(string), curve.baselen) AssertionError: (38, 32)Er erwartet eine Größe von 32 Byte. Soll ich einfach den Digest des privaten Schlüssels wie beim klassischen P2PKH eingeben? Ich werde in der Zwischenzeit in den Y-Wert-Teil springen. Vielen Dank.
Ihre WIF-Decodierung klingt so, als wäre sie falsch. Sie sollten ein 34-Byte-Objekt erhalten. Das erste Byte ist die Versionsnummer und das letzte Byte gibt die Komprimierung an, ist aber nur für WIF relevant. Sie sollten diese zwei Bytes löschen und Sie haben ein 32-Byte-Objekt, das der private Schlüssel ist. Die Handhabung der Komprimierung des öffentlichen Schlüssels erfolgt später, daher müssen Sie sich an etwas anderes erinnern, ob der öffentliche Schlüssel komprimiert ist oder nicht.
Danke dir. Was mir gefehlt hat, war die Aufteilung des Digests (Koordinaten) in jeweils 32 Bytes. Der Rest war einfach umzusetzen. Werde die Lösung aufhängen..

Segwit in P2SH:

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

0x00 -> Witness-Version, 0x14 -> 20 (oder 32) Bytes Push-Opcode

prefix = b'\x05'-> P2SH Hauptnetz

checksum = double_sha256(prefix + redeem_script)[:4]
address = base58(prefix + redeem_script + checksum)
Danke für Ihre Hilfe! Mir ist aufgefallen, dass diese Schritte im Grunde das sind, was sie hier bitcoincore.org/en/segwit_wallet_dev haben ? Kann einiges nicht nachvollziehen. Woher bekommen Sie diese hash160-Funktion? Beziehst du dich auf das ricemd160? Werden diese Snipes nach der Generierung von x00 oder anstelle von platziert? Ich verstehe auch nicht wirklich, was sie mit "der in P2SH-P2WPKH verwendete öffentliche Schlüssel MUSS komprimiert werden, dh 33 Bytes groß sein und mit 0x02 oder 0x03 beginnen" erwähnen.
@fortesp hash160(x) ist reifmd160(sha256(x)). Weitere Informationen darüber, was ein komprimierter öffentlicher Schlüssel ist, finden Sie hier bitcoin.stackexchange.com/questions/3059/…