Sorry if this is a bit misleading, but I was actually doing this with Litecoin as opposed to Bitcoin, but the algorithm is exactly the same and I'm pretty sure that the answer will be too. In fact, am almost certain when I look, there's going to be the same problem with Bitcoin too. I'm having difficulty generating the correct SegWit address for a given public key as below:
Litecoin address generation (which is the same as Bitcoin)
- I take a compressed public key: 03861b83752e0c47cac36fc5980ae8956f41f6d9792a51f68a6bd5f66cc7364b48
- SHA256 on the public key.
- RipeMD160 the result from 2.
- Add a prefix byte, in my case, 0x3a which is the LTC_TESTNET
- Double SHA256 result of 4 and add the first four bytes of this result to the end of the RipeMD160.
- Finally, Base58 encode.
All seems dandy, right? Out pops the SegWit address: QhQxSZvVDWr3JvoKsYVC6BBW3DqkGhesrF
However, I'm pretty sure this address isn't right. When I import the private key into Electrum-LTC as a (p2wpkh-p2sh:) the address it generates for this private key is: QYyWqgyWSm1AJWph32GnyY7eamG1wUDruk
Now I believe that Electrum-LTC is right and there's something that I'm missing when generating a SegWit address and there's more to address generation than just changing the network prefix. My Public Key Hash is:
e444ac77800cdf904b928fc4642ab6fb6d4d696c
and Electrum-LTC's Public Key Hash is:
87b3e5bf5b2a1381e6549020d245e45b9ac76c82
Since the values are so VERY different, it suggests that the initial SHA256 is not hashing just the public key alone and that I'm missing something. I'm about out of ideas and am hoping someone has the answer and about the only thing I can find in the source, in the chainparams.cpp was this:
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
Anyone know what I might be doing wrong?
Update - Someone asked for the code - so here it is
#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <cstdint>
#include <iostream>
#include <vector>
std::string base58Encode(const std::vector<uint8_t>& data)
{
const uint8_t mapping[] = {
'1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z' };
std::vector<uint8_t> digits((data.size() * 137) / 100);
size_t digitslen = 1;
for (size_t i = 0; i < data.size(); i++)
{
uint32_t carry = static_cast<uint32_t>(data[i]);
for (size_t j = 0; j < digitslen; j++)
{
carry = carry + static_cast<uint32_t>(digits[j] << 8);
digits[j] = static_cast<uint8_t>(carry % 58);
carry /= 58;
}
for (; carry; carry /= 58)
digits[digitslen++] = static_cast<uint8_t>(carry % 58);
}
std::string result;
for (size_t i = 0; i < data.size() && !data[i]; i++)
result.push_back(mapping[0]);
for (size_t i = 0; i < digitslen; i++)
result.push_back(mapping[digits[digitslen - 1 - i]]);
return result;
}
std::vector<uint8_t> SHA256_Hash(const std::vector<uint8_t>& data)
{
std::vector<uint8_t> SHA256_Digest(SHA256_DIGEST_LENGTH);
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, data.data(), data.size());
SHA256_Final(SHA256_Digest.data(), &ctx);
return SHA256_Digest;
}
std::vector<uint8_t> RipeMD160_Hash(const std::vector<uint8_t>& data)
{
std::vector<uint8_t> RipeMD160_Digest(RIPEMD160_DIGEST_LENGTH);
RIPEMD160_CTX ctx;
RIPEMD160_Init(&ctx);
RIPEMD160_Update(&ctx, data.data(), data.size());
RIPEMD160_Final(RipeMD160_Digest.data(), &ctx);
return RipeMD160_Digest;
}
std::string getWalletAddress(const std::vector<uint8_t>& public_key, uint8_t id)
{
std::vector<uint8_t> addr_hash = SHA256_Hash(key);
std::vector<uint8_t> addr_ripe = RipeMD160_Hash(addr_hash);
addr_ripe.insert(addr_ripe.begin(), id);
addr_hash = SHA256_Hash(SHA256_Hash(addr_ripe));
addr_ripe.insert(addr_ripe.end(), addr_hash.begin(), addr_hash.begin() + sizeof(uint32_t));
return base58Encode(addr_ripe);
}
int main(int argc, char** argv)
{
const uint8_t LTC_TESTNET = 0x3a;
std::vector<uint8_t> public_key{
0x03, 0x86, 0x1b, 0x83, 0x75, 0x2e, 0x0c, 0x47, 0xca, 0xc3, 0x6f, 0xc5, 0x98, 0x0a, 0xe8, 0x95,
0x6f, 0x41, 0xf6, 0xd9, 0x79, 0x2a, 0x51, 0xf6, 0x8a, 0x6b, 0xd5, 0xf6, 0x6c, 0xc7, 0x36, 0x4b, 0x48};
std::cout << "Wallet Address: " << getWalletAddress(public_key, LTC_TESTNET) << std::endl;
// The above ouputs: QhQxSZvVDWr3JvoKsYVC6BBW3DqkGhesrF
// But it should be: QYyWqgyWSm1AJWph32GnyY7eamG1wUDruk
return 0;
}