【比特币】base58编码

参考比特币源代码的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

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

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

你可能感兴趣的:(base64,number,OpenSSL,divide,big,base58)