参考比特币源代码的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 |
---|---|---|---|
0 | 1 | Bitcoin pubkey hash | 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem |
5 | 3 | Bitcoin script hash | 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX |
111 | m or n | Bitcoin testnet pubkey hash | mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn |
128 | 5 | Bitcoin Private key (for uncompressed pubkey) | 5Hwgr3u458GLafKBgxtssHSPqJnYoGrSzgQsPwLFhLNYskDPyyA |
128 | K or L | Bitcoin Private key (for compressed pubkey) | L1aW4aubDFB7yfras2S1mN3bqg9nwySY8nkoLmJebSLD5BWv3ENZ |
196 | 2 | Testnet script hash | 2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc |
239 | 9 | Testnet Private key (for uncompressed pubkey) | 92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc |
239 | c | Testnet 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