Bitcoin Forum
September 19, 2018, 11:37:00 PM *
News: ♦♦ Bitcoin Core users must update to 0.16.3 [Torrent]. More info.
 
   Home   Help Search Donate Login Register  
Pages: [1]
  Print  
Author Topic: Step by step guide to go from public key to a Bech32 encoded address  (Read 119 times)
Coding Enthusiast
Sr. Member
****
Offline Offline

Activity: 498
Merit: 317


Novice C♯ Coder


View Profile WWW
September 01, 2018, 03:05:04 PM
Merited by ETFbitcoin (2), BitCryptex (2), LeGaulois (1), RGBKey (1), butka (1)
 #1

Bitcoin wiki has a pretty good step-by-step explanation of how to go from a public key to a base58_encoded address which contains the values for each step of the way[1]. But unfortunately I could not find anything similar for Bech32_encoding. Additionally I found the reference implementations a bit confusing[2]! The information is out there[3] but I feel like having it step-by-step like "[1]" can make it a lot easier specially for developers. For example during unit testing I was getting a different address (bc1qp63uahgrxged4z5jswyt5dn5v3lzsem6c0qqhg8) for below public key and I wasn't sure where the bug was coming from, this visualization helped me [4] realize I was appending the version byte before converting the bits instead of after. So hopefully these steps can help someone like me looking for them.


How to create a Bech32 address from a public key:

1. Having a compressed[5] public key (0x02 or 0x03 followed by 32 byte X coordinate):
Code:
0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798

2. Perform SHA-256 hashing on the public key:
Code:
0f715baf5d4c2ed329785cef29e562f73488c8a2bb9dbc5700b361d54b9b0554

3. Perform RIPEMD-160 hashing on the result of SHA-256:
Code:
751e76e8199196d454941c45d1b3a323f1433bd6

4. The result of step 3 is an array of 8-bit unsigned integers (base 2^8=256) and Bech32 encoding converts this to an array of 5-bit unsigned integers (base 2^5=32) so we "squash" the bytes to get:
in hex:
Code:
0e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16
in numbers:
Code:
14 20 15 07 13 26 00 25 18 06 11 13 08 21 04 20 03 17 02 29 03 12 29 03 04 15 24 20 06 14 30 22
5 bits binary:
Code:
01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110

5. Add the witness version byte in front of the step 4 result (current version is 0):
Code:
000e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16

6. Compute the checksum by using the data from step 5 and the H.R.P (bc for MainNet and tb for TestNet)
Code:
0c0709110b15

7. Append the checksum to result of step 5 (we now have an array of 5-bit integers):
Code:
000e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e160c0709110b15

8. Map each value to its corresponding character in Bech32Chars (qpzry9x8gf2tvdw0s3jn54khce6mua7l) 00 -> q, 0e -> w,...
Code:
qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

9. A Bech32_encoded address consists of 3 parts: HRP + Separator + Data:
Code:
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

The final result from step 9 is the same as example in BIP173[6]

References:
[1] https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
[2] https://github.com/sipa/bech32
[3] https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
[4] https://en.bitcoin.it/w/images/en/4/48/Address_map.jpg
[5] Only compressed public keys are allowed: https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type
[6] https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#examples

Projects List+Suggestion box
Donation link using BIP21
Bech32 Donation link!
BitcoinTransactionTool (0.9.2):  Ann - Source Code
Watch Only Bitcoin Wallet (supporting SegWit) (3.1.0):  Ann - Source Code
SharpPusher (broadcast transactions) (0.10.0): Ann - Source Code

1537400220
Hero Member
*
Offline Offline

Posts: 1537400220

View Profile Personal Message (Offline)

Ignore
1537400220
Reply with quote  #2

1537400220
Report to moderator
1537400220
Hero Member
*
Offline Offline

Posts: 1537400220

View Profile Personal Message (Offline)

Ignore
1537400220
Reply with quote  #2

1537400220
Report to moderator
Make a difference with your Ether.
Donate Ether for the greater good.
SPRING.WETRUST.IO
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction. Advertise here.
1537400220
Hero Member
*
Offline Offline

Posts: 1537400220

View Profile Personal Message (Offline)

Ignore
1537400220
Reply with quote  #2

1537400220
Report to moderator
ETFbitcoin
Legendary
*
Offline Offline

Activity: 1442
Merit: 1093


Use SegWit and enjoy lower fees


View Profile
September 01, 2018, 03:26:19 PM
 #2

Great guide and mostly it's easy to understand. But i don't understand the 8th steps, is it encode/decode result from Bech32 characters?

▄▄▄▄▄▄▄▄
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
██▀▀▀▀██
▀▀▀▀▀▀▀▀
          ▄▄▄▄       
     ▄▄█▀▀▀▄▄▀▀▀█▄▄   
   ▄█▀▄▄████████▄▄▀█▄
 ▄█▀▄██████████████▄▀█▄
▐█ ██████████████████ █▌
█▌▐██████████████████▌▐█
█▌▐██████████████████▌▐█
▐█ ██████████████████ █▌
 ▀█▄▀██████████████▀▄█▀
   ▀█▄▀▀████████▀▀▄█▀   
     ▀▀█▄▄▄▀▀▄▄▄█▀▀     
          ▀▀▀▀         
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
███▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀███
██▌                 ▐██
██▌                 ▐██
██▌                 ▐██
██▌                 ▐██
██▌                 ▐██
▐██                 ██▌
 ███▄             ▄███
  ▀███▄         ▄███▀ 
    ▀▀███▄▄▄▄▄███▀▀   
        ▀▀▀▀▀▀▀       
 
██ ████  ██████  ██████ ███ ████ ██████████████████████

..WHITEPAPER..





             ▄████▄▄   ▄
█▄          ██████████▀▄
███        ███████████▀
▐████▄     ██████████▌
▄▄██████▄▄▄▄█████████▌
▀████████████████████
  ▀█████████████████
  ▄▄███████████████
   ▀█████████████▀
    ▄▄█████████▀
▀▀██████████▀
    ▀▀▀▀▀





▄█████████████████████████▄
███████████████████████████
███████████████▀       ████
██████████████      ▄▄▄████
██████████████    ▐████████
██████████████    ▐████████
██████████            ▐████
██████████            █████
██████████████    ▐████████
██████████████    ▐████████
██████████████    ▐████████
▀█████████████    ▐███████▀





                   ▄▄████
              ▄▄████████▌
         ▄▄█████████▀███
    ▄▄██████████▀▀ ▄███▌
▄████████████▀▀  ▄█████
▀▀▀███████▀   ▄███████▌
      ██    ▄█████████
       █  ▄██████████▌
       █  ███████████
       █ ██▀ ▀██████▌
       ██▀     ▀████
                 ▀█▌
Coding Enthusiast
Sr. Member
****
Offline Offline

Activity: 498
Merit: 317


Novice C♯ Coder


View Profile WWW
September 01, 2018, 03:38:49 PM
Merited by BitCryptex (1)
 #3

Great guide and mostly it's easy to understand. But i don't understand the 8th steps, is it encode/decode result from Bech32 characters?

Yes. Note that step7 is the hexadecimal representation of an array of 5-bit integers {0, 14, 20, 15, 7, ..., 11, 21} so 0 is item at index 0 of B32Chars or the letter q and 14 is the character at index 14 or w, 20 is 5 and so on.
 In C♯
Code:
string B32Chars = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
StringBuilder result = new StringBuilder();
foreach (byte item in step7Array)
{
   result.Append(B32Chars[item]);
}

Basically this:
.join in python implementation (https://github.com/sipa/bech32/blob/master/ref/python/segwit_addr.py#L59)
or the for loop in JavaScript implementation (https://github.com/sipa/bech32/blob/master/ref/javascript/bech32.js#L74-L76)

Projects List+Suggestion box
Donation link using BIP21
Bech32 Donation link!
BitcoinTransactionTool (0.9.2):  Ann - Source Code
Watch Only Bitcoin Wallet (supporting SegWit) (3.1.0):  Ann - Source Code
SharpPusher (broadcast transactions) (0.10.0): Ann - Source Code

odolvlobo
Legendary
*
Offline Offline

Activity: 2240
Merit: 1135



View Profile
September 02, 2018, 08:06:11 AM
 #4

4. The result of step 3 is an array of 8-bit unsigned integers (base 2^8=256) and Bech32 encoding converts this to an array of 5-bit unsigned integers (base 2^5=32) so we "squash" the bytes to get:

I feel that your description is confusing. You write "array of 5-bit integers", but displaying the results as a hex string implies that it is a string of 8-bit values. I recommend inserting spaces between each value to emphasize that each element is distinct, and perhaps using decimal values to avoid implying that the values could lie outside of the range 0 - 31.

For example:

Quote
4. The result of step 3 is an array of 8-bit unsigned integers (base 2^8=256) and Bech32 encoding converts this to an array of 5-bit unsigned integers (base 2^5=32) so we "squash" the bytes to get:
Code:
0e 14 0f 07 0d 1a 00 19 12 06 0b 0d 08 15 04 14 03 11 02 1d 03 0c 1d 03 04 0f 18 14 06 0e 1e 16

or even better:

Quote
4. The result of step 3 is an array of 8-bit unsigned integers (base 2^8=256) and Bech32 encoding converts this to an array of 5-bit unsigned integers (base 2^5=32) so we "squash" the bytes to get:
Code:
14 20 15 7 13 26 0 25 18 6 11 13 8 21 4 20 3 17 2 29 3 12 29 3 4 15 24 20 6 14 30 22


Also,

5. Add the witness version byte in front of the step 4 result (current version is 0):
Code:
000e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16

I recommend removing "byte" since the witness version is not a byte. Note that bip-173 also calls it a "byte" when it isn't.

Quote
5. Add the witness version in front of the step 4 result (current version is 0):
Code:
0 14 20 15 7 13 26 0 25 18 6 11 13 8 21 4 20 3 17 2 29 3 12 29 3 4 15 24 20 6 14 30 22

Buy bitcoins with cash from somebody near you: LocalBitcoins
Buy stuff on Amazon at a discount with bitcoins: Purse.io
Join an anti-signature campaign: DannyHamilton's ignore list
Coding Enthusiast
Sr. Member
****
Offline Offline

Activity: 498
Merit: 317


Novice C♯ Coder


View Profile WWW
September 02, 2018, 02:47:35 PM
 #5

I feel that your description is confusing. You write "array of 5-bit integers", but displaying the results as a hex string implies that it is a string of 8-bit values.
I get what you are talking about but base-16 is just another representation of an array of "numbers", it doesn't really make a difference if I write 14 12 15 with spaces or 0e 14 0f with or without spaces, they are both representing the same set of numbers in base-256. The only possible way to clarify things is if I start typing them in binary like this but that's just impossible to read:
Code:
01110 10100 01111 ...


Additionally hex or base-16 is a very easy and convenient way to transfer arrays of "numbers". For instance you can not input each of those "numbers" (14, 20, 15...) one by one in an array when coding, it would take a long time and it is easy to make a mistake. But you can very easily give your code the hexadecimal string representation of it and decode it into the array of "numbers" then treat those "numbers" however you like.

I am going to add both numbers and binary, maybe that helps visualizing it better.

I recommend removing "byte" since the witness version is not a byte. Note that bip-173 also calls it a "byte" when it isn't.
Well, "version byte" is the name of the "0" we are appending to it, I can't just change that name:
https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program
https://github.com/bitcoin/bips/blob/master/bip-0142.mediawiki#rationale

Projects List+Suggestion box
Donation link using BIP21
Bech32 Donation link!
BitcoinTransactionTool (0.9.2):  Ann - Source Code
Watch Only Bitcoin Wallet (supporting SegWit) (3.1.0):  Ann - Source Code
SharpPusher (broadcast transactions) (0.10.0): Ann - Source Code

TheArchaeologist
Member
**
Offline Offline

Activity: 70
Merit: 59

Learn from the Past!


View Profile WWW
September 03, 2018, 07:13:05 AM
 #6

If you want to play around with this using Python you can check: https://github.com/mcdallas/cryptotools

Example:
Code:
>>> from ECDSA.secp256k1 import CURVE, PrivateKey

>>> private = PrivateKey.random()
>>> private.int()
8034465994996476238286561766373949549982328752707977290709076444881813294372

>>> public = private.to_public()
>>> public
PublicKey(102868560361119050321154887315228169307787313299675114268359376451780341556078, 83001804479408277471207716276761041184203185393579361784723900699449806360826)

>>> public.point in CURVE
True

>>> public.to_address('P2WPKH')
'bc1qh2egksgfejqpktc3kkdtuqqrukrpzzp9lr0phn'
danda
Full Member
***
Offline Offline

Activity: 183
Merit: 126


View Profile WWW
September 03, 2018, 09:35:48 AM
 #7

nice.  why not add to bitcoin wiki?

mybitprices.info - wallet auditing tools
Pages: [1]
  Print  
 
Jump to:  

Sponsored by , a Bitcoin-accepting VPN.
Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!