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)`
Thank you for your help. So basically i am missing the part where i need to feed a compressed wif private key and to check for the Y value after. The problem now is that the ECDSA is expecting a 32bytes input digest and if i decode base58 compressed WIF 51bytes + x01 and feed into ECDSA is gives me this error:
assert len(string) == curve.baselen, (len(string), curve.baselen) AssertionError: (38, 32)it is expecting a 32 byte size. Should i just input the digest of the private key like the classic P2PKH? I will jump into the Y value part meanwhile.. Thanks a lot. – fortesp – 2018-05-16T16:23:55.563Your WIF decoding sounds like it is wrong. You should get a 34 byte object. The first byte is the version number, and the last byte indicates compressedness, but is only relevant to WIF. You should drop those two bytes and you will have a 32 byte object that is the private key. Handling public key compression happens later, so you need to have something else remember whether the public key is compressed or not. – Andrew Chow – 2018-05-16T16:32:02.030
Thank you. What i was missing was the separation of the digest (coordinates) in 32 bytes each. The rest was easy to implement. Going to put the solution up.. – fortesp – 2018-05-16T21:20:26.197