Bitcoin - Technical difference between P2PKH and P2SH address generation? I  was convinced that the only difference between the generation of addresses starting with '1' and '3' was simply changing the prefix added to the digest from 0x00 to 0x05 after the ripemd160 part. 

Although the addresses that i get do not match the addresses from Bitcoin Core wallet, when import the same private key, and also from the https://segwitaddress.org/. 
Does the prefix 04 remains the same or also changes? 
There's something i am missing. Could you please help? Thank you.

[EDIT]

This is the method i have at the moment and still does not work. I am surely misinterpreting something..

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)
P2PKH, P2SH, and Segwit are all different address types. Segwit and P2SH addresses are not the same.

P2PKH and P2SH addresses are generated in a similar way. P2PKH takes the hash160 of a public key (RIPMED160 of the SHA256 of the public key), appends the version byte of 0x00 to the hash160, and Base58 Check encodes it.

P2SH addresses are the Base58 Check encoding of the hash160 of a script (known as the redeemScript). It uses a version byte of 0x05 instead. The rest of the encoding is the same, just Base58 Check encoding.

For Segwit addresses, there are multiple types. There are native segwit addresses which follow the Bech32 standard. There are also P2SH wrapped segwit addresses.

For a P2WPKH (pay to witness pubkey hash) wrapped in a P2SH address, the redeemScript is 0x0014 &lt;hash 160 of the pubkey&gt;. That redeemScript is hashed  and encoded in the typical P2SH way.

For a P2WSH (pay to witness script hash) wrapped in a P2SH address, the witnessScript (redeemScript but for segwit addresses) is first hashed with SHA256. Then the P2SH redeemScript is 0x0020 &lt;SHA256 of witnessScript&gt;. The hash160 of this redeemScript is then encoded in the typical P2SH way.



As for your code, you are appending 0x04 to your public key which is simply incorrect. The 0x04 is not part of the address encoding, it is part of the public key encoding itself. Your public key generator should already be doing this for you. Note that if the public key is compressed that the prefix byte will be either 0x02 or 0x03 (depends on the Y value of the public key) instead of 0x04 which is for uncompressed public keys.



[Technical difference - Example solution]

`

    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)`