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 <hash 160 of the pubkey>
. 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 <SHA256 of witnessScript>
. 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)`