<p>I post Rust code below which does what you try to do. I am not sure what's wrong with your code, but I hope my code helps.</p>
<p>Please note that the example you use, <code>1AMGLbW71pvdMuRWkDdmZWibmzRkxa6LVt</code> is not a valid address. With valid checksum it would be <code>1AMGLbW71pvdMuRWkDdmZWibmzRkyByigA</code>.</p>
<p>In my example I use public key <code>032d1e1736727f0957a5137ab93bfbbf6e0c293dd0f65a6c8947b563c1f5827376</code>, which produces address bytes <code>007eefa5452e04cbc2d0f949ce96080aa2afc15c314e858c41</code> (prefix + pubkey hash + checksum), and address <code>1CaBE3CYxhqdjcxKCPqwNhsys1weUnZhHJ</code>.</p>
<p>I provide 3 solutions: one with 'manual' steps, one where the bas58 checksum is done by the base58 library, and the shortest where <code>bitcoin</code> crate is used.</p>
<pre><code>use bitcoin::hashes::Hash;
use bitcoin::util::address::Address;
use bitcoin;

fn hex_to_bytes(hex: &amp;str) -&gt; Vec&lt;u8&gt; {
    hex::decode(hex).unwrap()
}

fn bytes_to_hex(bytes: &amp;Vec&lt;u8&gt;) -&gt; String {
    hex::encode(bytes)
}

fn pubkey_hash(pubkey: &amp;Vec&lt;u8&gt;) -&gt; Vec&lt;u8&gt; {
    let sha = bitcoin::hashes::sha256::Hash::hash(pubkey);
    bitcoin::hashes::ripemd160::Hash::hash(&amp;sha).to_vec()
}

// All manual steps
fn pubkey_to_addr_detailed(pubkey_hex: &amp;str) -&gt; String {
    let pubkey = hex_to_bytes(pubkey_hex);
    let mut pubkey_hash = pubkey_hash(&amp;pubkey);
    let mut address_bytes = Vec::new();
    let prefix: u8 = 0;
    address_bytes.push(prefix);
    address_bytes.append(&amp;mut pubkey_hash);
    let checksum = bitcoin::hashes::sha256d::Hash::hash(&amp;address_bytes).to_vec();
    let mut checksum_truncated = checksum[0..4].to_vec();
    let mut address_bytes_with_checksum = Vec::new();
    address_bytes_with_checksum.append(&amp;mut address_bytes);
    address_bytes_with_checksum.append(&amp;mut checksum_truncated);
    bitcoin::util::base58::encode_slice(&amp;address_bytes_with_checksum)
}

// Base58 checksum done by base58
fn pubkey_to_addr_base58(pubkey_hex: &amp;str) -&gt; String {
    let pubkey = hex_to_bytes(pubkey_hex);
    let mut pubkey_hash = pubkey_hash(&amp;pubkey);
    let mut address_bytes = Vec::new();
    let prefix: u8 = 0;
    address_bytes.push(prefix);
    address_bytes.append(&amp;mut pubkey_hash);
    bitcoin::util::base58::check_encode_slice(&amp;address_bytes)
}

// Using bitcoin::address
fn pubkey_to_addr_bitcoin(pubkey_hex: &amp;str) -&gt; String {
    let pubkey_data = hex_to_bytes(pubkey_hex);
    let pubkey = bitcoin::PublicKey::from_slice(&amp;pubkey_data).unwrap();
    let addr = Address::p2pkh(&amp;pubkey, bitcoin::Network::Bitcoin);
    addr.to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    const PUBKEY1: &amp;str = &quot;032d1e1736727f0957a5137ab93bfbbf6e0c293dd0f65a6c8947b563c1f5827376&quot;;
    const ADDRESS: &amp;str = &quot;1CaBE3CYxhqdjcxKCPqwNhsys1weUnZhHJ&quot;;

    #[test]
    fn test_pubkey_to_addr_detailed() {
        let addr = pubkey_to_addr_detailed(PUBKEY1);
        assert_eq!(addr, ADDRESS);
    }

    #[test]
    fn test_pubkey_to_addr_base58() {
        let addr = pubkey_to_addr_base58(PUBKEY1);
        assert_eq!(addr, ADDRESS);
    }
    
    #[test]
    fn test_pubkey_to_addr_bitcoin() {
        let addr = pubkey_to_addr_bitcoin(PUBKEY1);
        assert_eq!(addr, ADDRESS);
    }

    #[test]
    fn t1() {
        let pubkey = hex_to_bytes(PUBKEY1);
        let mut pubkey_hash = pubkey_hash(&amp;pubkey);
        assert_eq!(bytes_to_hex(&amp;pubkey_hash), &quot;7eefa5452e04cbc2d0f949ce96080aa2afc15c31&quot;);
        let mut address_bytes = Vec::new();
        let prefix: u8 = 0;
        address_bytes.push(prefix);
        address_bytes.append(&amp;mut pubkey_hash);
        assert_eq!(bytes_to_hex(&amp;address_bytes), &quot;007eefa5452e04cbc2d0f949ce96080aa2afc15c31&quot;);
        let checksum = bitcoin::hashes::sha256d::Hash::hash(&amp;address_bytes).to_vec();
        assert_eq!(bytes_to_hex(&amp;checksum), &quot;4e858c4104e953987e6147a2a653a423fa63c4f518c89e9d58c3a3aa40f518e3&quot;);
        let mut checksum_truncated = checksum[0..4].to_vec();
        assert_eq!(bytes_to_hex(&amp;checksum_truncated), &quot;4e858c41&quot;);
        let mut address_bytes_with_checksum = Vec::new();
        address_bytes_with_checksum.append(&amp;mut address_bytes);
        address_bytes_with_checksum.append(&amp;mut checksum_truncated);
        assert_eq!(bytes_to_hex(&amp;address_bytes_with_checksum), &quot;007eefa5452e04cbc2d0f949ce96080aa2afc15c314e858c41&quot;);
        let b58 = bitcoin::util::base58::encode_slice(&amp;address_bytes_with_checksum);
        assert_eq!(b58, ADDRESS);
    }
}
</code></pre>

CC BY-SA 4.0
2023-01-12T19:51:37.163Z
Thank you sir, changing the libraries being used was a very smart idea. It looks like the SSL library used in bitcoin does not process hex strings the same way my sha256 digest function did. By extension ripemd160 also did not hash properly. That is likely what lead to my results being chaotically wrong. There is some kind of conversion where it takes the hex string and turns it into a binary hex dump. [https://medium.com/coinmonks/how-to-generate-a-bitcoin-address-step-by-step-9d7fcbf1ad0b](source) This article uses these SSL flags `xxd -p -r ` which I wouldn't know how to do in rust offhand.
Poseidon
CC BY-SA 4.0
2023-01-12T20:26:07.637Z
@Poseidon **no** SHA256 implementation handles hex input. Whenever you see hex, it's done outside of SHA256 for human interaction.
Pieter Wuille
CC BY-SA 4.0
2023-01-12T21:41:16.700Z
I meant was I was hashing the hex as a string, the rust sha256 digest function accepts strings and string.as_bytes(). Thank you though for clarifying this though.
Poseidon
CC BY-SA 4.0
2023-01-12T12:32:29.333Z
2023-01-12T21:29:15.287Z
2023-01-12T21:29:15.287Z
OptOut
<p>I have followed the steps to generate a p2pkh compressed base58 address and am consistently checking it to find that I get an invalid checksum.</p>
<p>I am using this python code to check the address:
(Checked the solution against the python code and an online address checker, this current python code fails around the encode part for some reason so it cant print out the addr after, it will check the addr still)</p>
<pre><code>import base58
adr = '1AMGLbW71pvdMuRWkDdmZWibmzRkxa6LVt'
adr160 = base58.b58decode_check(adr).encode('hex')[2:]
print(adr160)
</code></pre>
<p>Here is the Rust function to hash a public key:</p>
<pre><code>fn hashPubKey(publicKeyStr: String) -&gt; (String, String)
{
    let sha256_1 = digest(publicKeyStr.as_bytes());
    let mut ripemd160 = Ripemd160::new();
    ripemd160.update(sha256_1.as_bytes());
    let ripe = format!(&quot;{:x}&quot;, ripemd160.finalize());
    let network_byte_key = format!(&quot;{:0&gt;42}&quot;, ripe.clone());
    let sha256_2 = digest(network_byte_key.as_bytes().clone());
    let sha256_3 = digest(sha256_2.as_bytes());
    let mut checksum = String::new();
    for i in 0..8
    {
        checksum.push(sha256_3.chars().nth(i).unwrap());
    }
    let checkKey = network_byte_key.clone() + &amp;checksum;
    let pubkeyint = big_hex::hex_to_int(checkKey.clone());
    let compressedKey = base58_encode(pubkeyint.clone());
    let pubkeyint_check: BigInt = base58_decode(compressedKey.clone());
    assert!(pubkeyint == pubkeyint_check, &quot;error in base58 encoding/decoding&quot;);
    return (ripe, compressedKey);
}
</code></pre>
<p>The function returns the hash160 address and the base58 compressed key. It seems to be doing everything that online resources say to do, but it seems as if it generates invalid checksums. I tried to check it against some online tools but they all seemed to generate invalid hashes or inconsistent results with each-other.</p>

CC BY-SA 4.0
2023-01-11T13:45:42.300Z
The checksum covers the network type byte + the public key hash bytes. Your code only hashes the network type byte.
Pieter Wuille
CC BY-SA 4.0
2023-01-11T20:46:00.053Z
Do you mean the line `let sha256_2 = digest(network_byte_key.as_bytes().clone());` ? The **network_byte_key** variable that is being hashed here does have the public key hash bytes concatenated on to it. It is done in the previous line: `let network_byte_key = format!("{:0>42}", ripe.clone());` This line takes the ripemedhash and pads the network byte to the front. Then that entire hexadecimal is hashed as bytes.
Poseidon
CC BY-SA 4.0
2023-01-11T20:47:10.087Z
Ah, apologies, I misunderstood. Maybe the issue is that you're copying 8 checksum *characters*, rather than 4 checksum *bytes*?
Pieter Wuille
CC BY-SA 4.0
2023-01-12T11:26:36.083Z
I'm having trouble understanding what value I would be targeting even if I were to interpret it as bytes. Example: first four elements in the byte array for sha256: *6ef39a7ee6c9a7436636e1acf97fd6680d4f2a4319ccb9605b4e6d821a4f5cfe* are `[54, 101, 102, 51]` These correspond to `6ef3` which is the first 4 chars of the hash. However if I use this 4 character hex to append to the hash I actually get a 31 character key which also has an invalid checksum. If I then grab the first 8 bytes in the array I am right back to where I started of getting the first 8 chars of the hash. What to do differently?
Poseidon
CC BY-SA 4.0
2023-01-12T13:52:58.667Z
I guess I'm asking (I don't know Rust): are you hashing hex character bytes, or are you hashing the hash output directly? SHA256 has a 32-byte output, which is often represented as 64 hex characters for human consumption. However, here you must use the bytes directly; a hex conversion (which the sha256 functionality may be doing for you) should never be involved.
Pieter Wuille
CC BY-SA 4.0
2023-01-12T20:02:19.447Z
I believe I was trying out both methods of hashing the hex string and hashing the byte output however it seems as if the problem was in the sha256 and ripemd hashes themselves. The openssl versions used in bitcoin apparently `will convert the hex string into binary and then output it in hexdump style (ascii)` [https://medium.com/coinmonks/how-to-generate-a-bitcoin-address-step-by-step-9d7fcbf1ad0b] (gen-p2pkh-medium) this method produces completely different results than hashing it as a string which was what made me confused and produce wrong checksums.
Poseidon
<python><address-generation><p2pkh><rust-bitcoin>
Having issues properly generating a p2pkh address in Rust code. (for research purposes not for real use)
Poseidon
# Having issues properly generating a p2pkh address in Rust code. (for research purposes not for real use)

(Asked by: username_0 on 2023-01-11)

I have followed the steps to generate a p2pkh compressed base58 address and am consistently checking it to find that I get an invalid checksum.

I am using this python code to check the address:
(Checked the solution against the python code and an online address checker, this current python code fails around the encode part for some reason so it cant print out the addr after, it will check the addr still)

```
import base58
adr = '1AMGLbW71pvdMuRWkDdmZWibmzRkxa6LVt'
adr160 = base58.b58decode_check(adr).encode('hex')[2:]
print(adr160)

```

Here is the Rust function to hash a public key:

```
fn hashPubKey(publicKeyStr: String) -> (String, String)
{
    let sha256_1 = digest(publicKeyStr.as_bytes());
    let mut ripemd160 = Ripemd160::new();
    ripemd160.update(sha256_1.as_bytes());
    let ripe = format!("{:x}", ripemd160.finalize());
    let network_byte_key = format!("{:0>42}", ripe.clone());
    let sha256_2 = digest(network_byte_key.as_bytes().clone());
    let sha256_3 = digest(sha256_2.as_bytes());
    let mut checksum = String::new();
    for i in 0..8
    {
        checksum.push(sha256_3.chars().nth(i).unwrap());
    }
    let checkKey = network_byte_key.clone() + &checksum;
    let pubkeyint = big_hex::hex_to_int(checkKey.clone());
    let compressedKey = base58_encode(pubkeyint.clone());
    let pubkeyint_check: BigInt = base58_decode(compressedKey.clone());
    assert!(pubkeyint == pubkeyint_check, "error in base58 encoding/decoding");
    return (ripe, compressedKey);
}

```

The function returns the hash160 address and the base58 compressed key. It seems to be doing everything that online resources say to do, but it seems as if it generates invalid checksums. I tried to check it against some online tools but they all seemed to generate invalid hashes or inconsistent results with each-other.

**Tags:** python, address-generation, p2pkh, rust-bitcoin

> **Comments:**
> **username_1**: The checksum covers the network type byte + the public key hash bytes. Your code only hashes the network type byte.  
> **username_0**: Do you mean the line `let sha256_2 = digest(network_byte_key.as_bytes().clone());` ? The **network_byte_key** variable that is being hashed here does have the public key hash bytes concatenated on to it. It is done in the previous line: `let network_byte_key = format!("{:0>42}", ripe.clone());` This line takes the ripemedhash and pads the network byte to the front. Then that entire hexadecimal is hashed as bytes.  
> **username_1**: Ah, apologies, I misunderstood. Maybe the issue is that you're copying 8 checksum *characters*, rather than 4 checksum *bytes*?  
> **username_0**: I'm having trouble understanding what value I would be targeting even if I were to interpret it as bytes. Example: first four elements in the byte array for sha256: *6ef39a7ee6c9a7436636e1acf97fd6680d4f2a4319ccb9605b4e6d821a4f5cfe* are `[54, 101, 102, 51]` These correspond to `6ef3` which is the first 4 chars of the hash. However if I use this 4 character hex to append to the hash I actually get a 31 character key which also has an invalid checksum. If I then grab the first 8 bytes in the array I am right back to where I started of getting the first 8 chars of the hash. What to do differently?  
> **username_1**: I guess I'm asking (I don't know Rust): are you hashing hex character bytes, or are you hashing the hash output directly? SHA256 has a 32-byte output, which is often represented as 64 hex characters for human consumption. However, here you must use the bytes directly; a hex conversion (which the sha256 functionality may be doing for you) should never be involved.  
> **username_0**: I believe I was trying out both methods of hashing the hex string and hashing the byte output however it seems as if the problem was in the sha256 and ripemd hashes themselves. The openssl versions used in bitcoin apparently `will convert the hex string into binary and then output it in hexdump style (ascii)` [https://medium.com/coinmonks/how-to-generate-a-bitcoin-address-step-by-step-9d7fcbf1ad0b] (gen-p2pkh-medium) this method produces completely different results than hashing it as a string which was what made me confused and produce wrong checksums.  

---

## 1 answers

### Answer by username_2 on 2023-01-12. Score: 3 | Accepted answer

I post Rust code below which does what you try to do. I am not sure what's wrong with your code, but I hope my code helps.

Please note that the example you use, `1AMGLbW71pvdMuRWkDdmZWibmzRkxa6LVt` is not a valid address. With valid checksum it would be `1AMGLbW71pvdMuRWkDdmZWibmzRkyByigA`.

In my example I use public key `032d1e1736727f0957a5137ab93bfbbf6e0c293dd0f65a6c8947b563c1f5827376`, which produces address bytes `007eefa5452e04cbc2d0f949ce96080aa2afc15c314e858c41` (prefix + pubkey hash + checksum), and address `1CaBE3CYxhqdjcxKCPqwNhsys1weUnZhHJ`.

I provide 3 solutions: one with 'manual' steps, one where the bas58 checksum is done by the base58 library, and the shortest where `bitcoin` crate is used.

```
use bitcoin::hashes::Hash;
use bitcoin::util::address::Address;
use bitcoin;

fn hex_to_bytes(hex: &str) -> Vec<u8> {
    hex::decode(hex).unwrap()
}

fn bytes_to_hex(bytes: &Vec<u8>) -> String {
    hex::encode(bytes)
}

fn pubkey_hash(pubkey: &Vec<u8>) -> Vec<u8> {
    let sha = bitcoin::hashes::sha256::Hash::hash(pubkey);
    bitcoin::hashes::ripemd160::Hash::hash(&sha).to_vec()
}

// All manual steps
fn pubkey_to_addr_detailed(pubkey_hex: &str) -> String {
    let pubkey = hex_to_bytes(pubkey_hex);
    let mut pubkey_hash = pubkey_hash(&pubkey);
    let mut address_bytes = Vec::new();
    let prefix: u8 = 0;
    address_bytes.push(prefix);
    address_bytes.append(&mut pubkey_hash);
    let checksum = bitcoin::hashes::sha256d::Hash::hash(&address_bytes).to_vec();
    let mut checksum_truncated = checksum[0..4].to_vec();
    let mut address_bytes_with_checksum = Vec::new();
    address_bytes_with_checksum.append(&mut address_bytes);
    address_bytes_with_checksum.append(&mut checksum_truncated);
    bitcoin::util::base58::encode_slice(&address_bytes_with_checksum)
}

// Base58 checksum done by base58
fn pubkey_to_addr_base58(pubkey_hex: &str) -> String {
    let pubkey = hex_to_bytes(pubkey_hex);
    let mut pubkey_hash = pubkey_hash(&pubkey);
    let mut address_bytes = Vec::new();
    let prefix: u8 = 0;
    address_bytes.push(prefix);
    address_bytes.append(&mut pubkey_hash);
    bitcoin::util::base58::check_encode_slice(&address_bytes)
}

// Using bitcoin::address
fn pubkey_to_addr_bitcoin(pubkey_hex: &str) -> String {
    let pubkey_data = hex_to_bytes(pubkey_hex);
    let pubkey = bitcoin::PublicKey::from_slice(&pubkey_data).unwrap();
    let addr = Address::p2pkh(&pubkey, bitcoin::Network::Bitcoin);
    addr.to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    const PUBKEY1: &str = "032d1e1736727f0957a5137ab93bfbbf6e0c293dd0f65a6c8947b563c1f5827376";
    const ADDRESS: &str = "1CaBE3CYxhqdjcxKCPqwNhsys1weUnZhHJ";

    #[test]
    fn test_pubkey_to_addr_detailed() {
        let addr = pubkey_to_addr_detailed(PUBKEY1);
        assert_eq!(addr, ADDRESS);
    }

    #[test]
    fn test_pubkey_to_addr_base58() {
        let addr = pubkey_to_addr_base58(PUBKEY1);
        assert_eq!(addr, ADDRESS);
    }
    
    #[test]
    fn test_pubkey_to_addr_bitcoin() {
        let addr = pubkey_to_addr_bitcoin(PUBKEY1);
        assert_eq!(addr, ADDRESS);
    }

    #[test]
    fn t1() {
        let pubkey = hex_to_bytes(PUBKEY1);
        let mut pubkey_hash = pubkey_hash(&pubkey);
        assert_eq!(bytes_to_hex(&pubkey_hash), "7eefa5452e04cbc2d0f949ce96080aa2afc15c31");
        let mut address_bytes = Vec::new();
        let prefix: u8 = 0;
        address_bytes.push(prefix);
        address_bytes.append(&mut pubkey_hash);
        assert_eq!(bytes_to_hex(&address_bytes), "007eefa5452e04cbc2d0f949ce96080aa2afc15c31");
        let checksum = bitcoin::hashes::sha256d::Hash::hash(&address_bytes).to_vec();
        assert_eq!(bytes_to_hex(&checksum), "4e858c4104e953987e6147a2a653a423fa63c4f518c89e9d58c3a3aa40f518e3");
        let mut checksum_truncated = checksum[0..4].to_vec();
        assert_eq!(bytes_to_hex(&checksum_truncated), "4e858c41");
        let mut address_bytes_with_checksum = Vec::new();
        address_bytes_with_checksum.append(&mut address_bytes);
        address_bytes_with_checksum.append(&mut checksum_truncated);
        assert_eq!(bytes_to_hex(&address_bytes_with_checksum), "007eefa5452e04cbc2d0f949ce96080aa2afc15c314e858c41");
        let b58 = bitcoin::util::base58::encode_slice(&address_bytes_with_checksum);
        assert_eq!(b58, ADDRESS);
    }
}

```

> **Comments:**
> **username_0**: Thank you sir, changing the libraries being used was a very smart idea. It looks like the SSL library used in bitcoin does not process hex strings the same way my sha256 digest function did. By extension ripemd160 also did not hash properly. That is likely what lead to my results being chaotically wrong. There is some kind of conversion where it takes the hex string and turns it into a binary hex dump. [https://medium.com/coinmonks/how-to-generate-a-bitcoin-address-step-by-step-9d7fcbf1ad0b](source) This article uses these SSL flags `xxd -p -r ` which I wouldn't know how to do in rust offhand.  
> **username_1**: @username_0 **no** SHA256 implementation handles hex input. Whenever you see hex, it's done outside of SHA256 for human interaction.  
> **username_0**: I meant was I was hashing the hex as a string, the rust sha256 digest function accepts strings and string.as_bytes(). Thank you though for clarifying this though.  

---

