Author

Topic: Address generation (Read 1138 times)

sr. member
Activity: 252
Merit: 250
April 23, 2013, 04:47:32 AM
#17
Uhmmm... are you sure!?

I think leading '\x00's reflect in shorter addresses; otherwise, you can't see addresses beginning with "1R...", "1S...", "1T..." and so on; second symbol further than "Q" is only possible in hashes with leading 'x00's

It is leading zero bytes, keep that in mind.  They couldn't affect the leading result character, if it were a straight conversion, right? Somewhat like writing 007345349 in decimal, it doesn't matter how many zeroes there are in front.  But yes, I am pretty sure:


Yes, you are right. I confused leading hex 'x0' with leading byte 'x00'.
sr. member
Activity: 266
Merit: 250
April 23, 2013, 03:21:20 AM
#16
For Address Generation, I recommend reading this https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses.

Here is a function in C for hex to binary taken from cgminer.

Code:
bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
{
bool ret = false;

while (*hexstr && len) {
char hex_byte[4];
unsigned int v;

if (!hexstr[1]) {
return ret;
}

memset(hex_byte, 0, 4);
hex_byte[0] = hexstr[0];
hex_byte[1] = hexstr[1];

if (sscanf(hex_byte, "%x", &v) != 1) {
return ret;
}

*p = (unsigned char) v;

p++;
hexstr += 2;
len--;
}

if (likely(len == 0 && *hexstr == 0))
ret = true;
return ret;
}

I have that page pretty much memorized at this point. XD

Thanks for the code, I'll digest it once I've had some rest.
legendary
Activity: 1862
Merit: 1011
Reverse engineer from time to time
April 23, 2013, 01:05:48 AM
#15
For Address Generation, I recommend reading this https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses.

Here is a function in C for hex to binary taken from cgminer.

Code:
bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
{
bool ret = false;

while (*hexstr && len) {
char hex_byte[4];
unsigned int v;

if (!hexstr[1]) {
return ret;
}

memset(hex_byte, 0, 4);
hex_byte[0] = hexstr[0];
hex_byte[1] = hexstr[1];

if (sscanf(hex_byte, "%x", &v) != 1) {
return ret;
}

*p = (unsigned char) v;

p++;
hexstr += 2;
len--;
}

if (likely(len == 0 && *hexstr == 0))
ret = true;
return ret;
}
sr. member
Activity: 266
Merit: 250
April 23, 2013, 12:55:18 AM
#14
Now I'm just completely stuck at converting the final hash into the base58 address.

Once you have the RIPEMD160 hash and the 4-bytes checksum:

Code:
010966776006953D5567439E5E39F86A0D273BEED61967F6

then you must consider it a number in little-endian format, like an usual base-10 number you see everyday, but in base-16. I show the number in decimal format:

Code:
25420294593250030202636073700053352635053786165627414518

Then you obtain the last base58 symbol doing:

Code:
25420294593250030202636073700053352635053786165627414518 mod 58 = 20

And 20 correspond to symbol "M" in the base58 alfabet used in bitcoin. Then

Code:
(25420294593250030202636073700053352635053786165627414518 - 20)/58 =
438280941262931555217863339656092286811272175269438181

And the process starts again for the next symbol. At the end you have:

Code:
6UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM


You must add "1" at the beginning:

Code:
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

And you are done!

How do I convert from hex to decimal like you have it?
kjj
legendary
Activity: 1302
Merit: 1026
April 22, 2013, 11:00:20 PM
#13
1 in base58 means zero.  It is a special case at the beginning of a string to mean 8 bits of zero.  This allows the base58 encoding to preserve arbitrary bit strings, rather than just integers.
sr. member
Activity: 448
Merit: 254
April 22, 2013, 05:12:19 PM
#12
Uhmmm... are you sure!?

I think leading '\x00's reflect in shorter addresses; otherwise, you can't see addresses beginning with "1R...", "1S...", "1T..." and so on; second symbol further than "Q" is only possible in hashes with leading 'x00's

It is leading zero bytes, keep that in mind.  They couldn't affect the leading result character, if it were a straight conversion, right? Somewhat like writing 007345349 in decimal, it doesn't matter how many zeroes there are in front.  But yes, I am pretty sure:

The leading character '1', which has a value of zero in base58, is reserved for representing an entire leading zero byte, as when it is in a leading position, has no value as a base-58 symbol. There can be one or more leading '1's when necessary to represent one or more leading zero bytes.

Also in the source (the string is built in reverse), and see 111kzsNZ1w27kSGXwyov1ZvUGVLJMvLmJ with three leading 1's and see how its hash160 starts with two zero bytes.
sr. member
Activity: 252
Merit: 250
April 22, 2013, 04:55:06 PM
#11
One minor clarification, each leading zero byte should be replaced with "1".  So if the hash happened to start with \x00 the address would start with 11.

Uhmmm... are you sure!?

I think leading '\x00's reflect in shorter addresses; otherwise, you can't see addresses beginning with "1R...", "1S...", "1T..." and so on; second symbol further than "Q" is only possible in hashes with leading 'x00's
sr. member
Activity: 448
Merit: 254
April 22, 2013, 04:11:35 PM
#10
Shevek is right, but it is easier said than done. Wink  One minor clarification, each leading zero byte should be replaced with "1".  So if the hash happened to start with \x00 the address would start with 11.

You will probably want a pre-existing biginteger/bignumber library to do that math for you.  It might even include a routine that already does base conversions and just needs the base and the alphabet.  The Bitcoin base58 alphabet is "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".
legendary
Activity: 2058
Merit: 1452
April 22, 2013, 04:02:48 PM
#9
Now I just need to figure out how to actually store the hex in c++.
I'd say vector
You can also use strings I believe... But pay attention to '\x00', it may end strings unexpectedly. I am no specialist with string + binary + c++ though, so wait until someone confirms and always try and learn before trusting your code

or just allocate 64 bytes of unsigned char. (or whatever size it needs to be)
sr. member
Activity: 252
Merit: 250
April 22, 2013, 03:59:24 PM
#8
Now I'm just completely stuck at converting the final hash into the base58 address.

Once you have the RIPEMD160 hash and the 4-bytes checksum:

Code:
010966776006953D5567439E5E39F86A0D273BEED61967F6

then you must consider it a number in little-endian format, like an usual base-10 number you see everyday, but in base-16. I show the number in decimal format:

Code:
25420294593250030202636073700053352635053786165627414518

Then you obtain the last base58 symbol doing:

Code:
25420294593250030202636073700053352635053786165627414518 mod 58 = 20

And 20 correspond to symbol "M" in the base58 alfabet used in bitcoin. Then

Code:
(25420294593250030202636073700053352635053786165627414518 - 20)/58 =
438280941262931555217863339656092286811272175269438181

And the process starts again for the next symbol. At the end you have:

Code:
6UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM


You must add "1" at the beginning:

Code:
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

And you are done!
sr. member
Activity: 266
Merit: 250
April 22, 2013, 03:19:49 PM
#7
Now I'm just completely stuck at converting the final hash into the base58 address.
legendary
Activity: 1862
Merit: 1011
Reverse engineer from time to time
April 22, 2013, 11:31:09 AM
#6
I also made this mistake once. Hashing the hexadecimal representation rather than the binary, very very wrong.
sr. member
Activity: 266
Merit: 250
April 22, 2013, 11:26:11 AM
#5
I got this code to work properly.

Code:
uint8_t* sha2(uint8_t *in, uint8_t *out)
{
    SHA256(in, 65, out);
    return out;
}
uint8_t* hex_decode(const char *in, size_t len,uint8_t *out)
{
    unsigned int i, t, hn, ln;

    for (t = 0,i = 0; i < len; i+=2,++t)
    {

        hn = in[i] > '9' ? in[i] - 'A' + 10 : in[i] - '0';
        ln = in[i+1] > '9' ? in[i+1] - 'A' + 10 : in[i+1] - '0';

        out[t] = (hn << 4 ) | ln;
    }

    return out;
}
int main()
{
    char pub_key[] = "0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6";
    uint8_t res_sha[32];
    uint8_t res_tmp[65];
    hex_decode(pub_key,131,res_tmp);
    for(int j =0; j < 65; j++)
        cout << setw(2) << setfill('0') << hex << (int)res_tmp[j];
    cout << endl << endl;
    sha2(res_tmp,res_sha);
    for(int i =0; i < 32; i++)
        cout << setw(2) << setfill('0') << hex << (int)res_sha[i];

    return 0;
}
legendary
Activity: 1176
Merit: 1280
May Bitcoin be touched by his Noodly Appendage
April 21, 2013, 05:24:54 PM
#4
Now I just need to figure out how to actually store the hex in c++.
I'd say vector
You can also use strings I believe... But pay attention to '\x00', it may end strings unexpectedly. I am no specialist with string + binary + c++ though, so wait until someone confirms and always try and learn before trusting your code
sr. member
Activity: 266
Merit: 250
April 21, 2013, 05:17:56 PM
#3
Alright, that helps a lot, and I understand what I was doing wrong. Now I just need to figure out how to actually store the hex in c++.
legendary
Activity: 1176
Merit: 1280
May Bitcoin be touched by his Noodly Appendage
April 21, 2013, 04:39:26 PM
#2
The public key, and further more all the hexadecimal data must not be used as strings, they are binary data
For example the string "0123afz" is "3031323361667a" in hexadecimal (see ascii), aka "\x30\x31\x32\x33\x61\x66\x7a"

Doing it wrong
Doing it right

ps: look at "Original bytes" too and notice how "045086..." as a string corresponds to "303435303836..." in binary
sr. member
Activity: 266
Merit: 250
April 21, 2013, 04:33:36 PM
#1
I've been trying to understand the generation of bitcoin addresses, and to further that end, I've been trying to write my own code to generate an address.

I understand the process, I just can't get it to work properly.

The example generation on the wiki has a public key of "0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A 299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6", which it then says is hashed via SHA256 to return "600FFE422B4E00731A59557A5CCA46CC183944191006324A447BDB2D98D4B408"
however, I'm consistently getting "32511e82d56dcea68eb774094e25bab0f8bdd9bc1eca1ceeda38c7a43aceddce".

What am I doing wrong?
Jump to: