NO IMAGE

參考比特幣原始碼的base58.h程式碼用openssl的bignum庫實現的一個編碼函式, 作為驗證小工具使用:

生成:

g   -o base58encode -g base58encode.cc -lcrypto

原始碼:

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <openssl/bn.h>
#define DOMAIN_CHECK(c) ('0'<=(c)&&(c)<='9'||'a'<=(c)&&(c)<='f'||'A'<=(c)&&(c)<='F')
const char * BASE58TABLE="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
std::string base58encode(const std::string & hexstring)
{
std::string result = "";
BN_CTX * bnctx = BN_CTX_new();
BN_CTX_init(bnctx);
BIGNUM * bn = BN_new();
BIGNUM * bn0= BN_new();
BIGNUM * bn58=BN_new();
BIGNUM * dv = BN_new();
BIGNUM * rem= BN_new();
BN_init(bn); 
BN_init(bn0);
BN_init(bn58);
BN_init(dv);
BN_init(rem);
BN_hex2bn(&bn, hexstring.c_str());
//printf("bn:%s\n", BN_bn2dec(bn));
BN_hex2bn(&bn58, "3a");//58
BN_hex2bn(&bn0,"0");
while(BN_cmp(bn, bn0)>0){
BN_div(dv, rem, bn, bn58, bnctx);
BN_copy(bn, dv);
//printf("dv: %s\n", BN_bn2dec(dv));
//printf("rem:%s\n", BN_bn2dec(rem));
char base58char = BASE58TABLE[BN_get_word(rem)];
result  = base58char;
}
std::string::iterator pbegin = result.begin();
std::string::iterator pend   = result.end();
while(pbegin < pend) {
char c = *pbegin;
*(pbegin  ) = *(--pend);
*pend = c;
}
return result;
}
int main(int argc, char * argv [])
{
std::string hexstring = "";
FILE * fin = stdin;
while(!feof(fin))
{
char c = fgetc(fin);
if (DOMAIN_CHECK(c))
hexstring  =c;
}
fprintf(stdout, "%s", base58encode(hexstring).c_str());
return 0;
}

測試:

echo -n 

00010966776006953D5567439E5E39F86A0D273BEED61967F6

| ./base58encode

列印:6UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

因為前面有2個前導0,所以地址應該是 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

參考地址生成裡面的程式碼: 新增了前導0的處理,生成最終的比特幣地址

base58前導版本位元組的定義:

Decimal version Leading symbol Use Example
01Bitcoin pubkey hash17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem
53Bitcoin script hash3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
111m or nBitcoin testnet pubkey hashmipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn
1285Bitcoin Private key (for uncompressed pubkey)5Hwgr3u458GLafKBgxtssHSPqJnYoGrSzgQsPwLFhLNYskDPyyA
128K or LBitcoin Private key (for compressed pubkey)L1aW4aubDFB7yfras2S1mN3bqg9nwySY8nkoLmJebSLD5BWv3ENZ
1962Testnet script hash2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc
2399Testnet Private key (for uncompressed pubkey)92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc
239cTestnet Private key (for compressed pubkey)cNJFgo1driFnPcBdBX8BrJrpxchBWXwXCvNH5SoSkdcF6JXXwHMm

區別於公鑰地址的前導位元組的定義:

    // Compute the length of a pubkey with a given first byte.
unsigned int static GetLen(unsigned char chHeader) {
if (chHeader == 2 || chHeader == 3) // 壓縮方式
return 33;
if (chHeader == 4 || chHeader == 6 || chHeader == 7) // 非壓縮方式
return 65;
return 0;
}

討論:

1) 關於地址前導0的,也就是base58的1,這個可以這樣實現,hexstring中的每2個字元是一個byte,接寫成byte陣列,看看從0下標開始,有多少個byte是0x00,就前置多少個1放到前面,一般地址的版本號是0x00,所以,幾乎看到都是1開頭的比特幣地址。

參考:

https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses

https://en.bitcoin.it/wiki/List_of_address_prefixes

https://en.bitcoin.it/wiki/Base58Check_encoding