【比特币】地址生成

来源:互联网 发布:淘宝网防晒衣女 编辑:程序博客网 时间:2024/05/15 23:45

1) 带校验以及前导0的base58编码方法源代码

#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')#define 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());    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);        char base58char = BASE58TABLE[BN_get_word(rem)];        result += base58char;    }    // compute leading-zeros and prepend '1' (base58table[0])    const char * phexstr = hexstring.c_str();    int hexlen = hexstring.length();    for (int i = 1; i < hexlen; i += 2)    {        if (phexstr[i-1] == '0' && phexstr[i] == '0')            result += BASE58TABLE[0];        else break;    }        // little endian -> big endian    // most significant byte (the dv) is calulated at the end,     // and appended at the end of string.     // so convert it to big endian.    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;    }        // check empty    if (hexstring.length() == 0) return -1;    // assert length %2 == 0    if (hexstring.length() % 2) hexstring.insert(hexstring.begin(), '0');        fprintf(stdout, "%s", base58encode(hexstring).c_str());    return 0;}

比特币地址是ECDSA算法的公钥


关于这个算法的三个特性:

1)私钥可以推导出公钥,但是公钥无法推导出私钥,

2)公钥有2个表示方式,压缩方式,前导为0x02,0x03;非压缩方式,前导为0x04

3)私钥对一个hash签名的结果是一个整数对(r,s),公钥可以验证是否有对应的私钥签发



1) 计算这个公钥对应的地址,

0400112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff

echo -n 0400112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff | h2b | sha256sum | sed -e 's/ -//g' | h2b | openssl dgst -ripemd160

(stdin)= 77eaeaabc6da57ece603d2b56e4d21c865d5a5cd


输出就是一个16进制字符串表示的20字节的大整数



再跳到下面的第2步,使用带hash-checking的base58编码


2) 如何从一个地址对象生成打印一个比特币地址出来。


关于CBitcoinAddress的定义:

保存vchVersion字段和vchData字段(20个字节长度,ripemd160的hash结果),分别表示《版本号》和《RipeMD160表示的ID》内容。

版本号,可以是 0x00和0x05两种,但是base58编码在比特币系统中的应用还有其他几种类型,参考wiki



主要有几个步骤:


2.1,序列化

【版本】+【Ripemd160数据部分】

插播:至于怎么从公钥得到RipmeMd160的结果可以参考wiki,先sha256sum再ripemd160就可以了。


举例:

假如从公钥hash出来的id是1122334455667788990011223344556677889900,

05+1122334455667788990011223344556677889900 => 051122334455667788990011223344556677889900


2.2,计算校验和

HASH(ResultStep1)即:Hash256(Hash256(051122334455667788990011223344556677889900)) => 68787bf6caca67526183fdfb06d9a3efc0f2264cdfe94af017e5428d83ddf873

这是一个LE表示的uint256整数,提取前面的4个字节

得到:68787bf6


2.3, 追加第二步的校验和到第一步的结果后面再进行base58编码

Base58(05112233445566778899001122334455667788990068787bf6) 也就是对一个(1+20+4)总共25个元素表示的大整数进行base58编码,这个整数肯定小于32字节的uint256。

得到: 33FcP5di8njrEcWNRvHhjx46fa2jrWPzYD 


备注:


1)这个过程是可以反向解码校验的。


2)Hash256计算的对象都是256字节的大整数(小端表示的字节数据,博客中和wiki中都是用16进制表示的,要么用bignum库反序列化得到大整数,或者使用十六进制字符串反序列化 得到字节数组以后再计算)


3)举例的输入是随意的,实际0x05版本的比特币地址(也就是3前导的)是脚本地址,而不是公钥地址。


4)一个脚本实现的从CPubKeyID生成比特币地址

hex2bvec.sh 转换16进制字符串到字节数组

echo -n -e $(echo -n $(cat) | sed -e 's/\([a-fA-F0-9]\{2\}\)/\\x\1/g' )

pubkey2addr.sh

echo -n 00$1$(echo -n 00$1 | hex2bvec.sh | sha256sum | sed -e 's/ -//g' | hex2bvec.sh | sha256sum | sed -e 's/\([0-9a-fA-F]\{8\}\).*/\1/g') | base58

把比特币发送到某个地址,使用的是keyid,未经base58编码的公钥id,40个字节十六进制表示的20字节整数。

Output Scripts

OP_DUP OP_HASH160 c83dc3af1091303168fd1ab44e528d7c941ab49c OP_EQUALVERIFY OP_CHECKSIG OKOP_DUP OP_HASH160 45d906fdc235c3ffb40acb4831a509e5bac4092c OP_EQUALVERIFY OP_CHECKSIGsh pubkey2addr.sh c83dc3af1091303168fd1ab44e528d7c941ab49c 得到地址1KFn8qCwsNwo5fSC3qMHs6d3FEPTsojhKy

sh pubkey2addr.sh 45d906fdc235c3ffb40acb4831a509e5bac4092c  得到地址17NKcZNXqAbxWsTwB1UJHjc9mQG3yjGALA


也可以修改一个script2addr.sh

echo -n 00$1$(echo -n 00$1 | hex2bvec.sh | sha256sum | sed -e 's/ -//g' | hex2bvec.sh | sha256sum | sed -e 's/\([0-9a-fA-F]\{8\}\).*/\1/g') | base58


参考网页



0 0
原创粉丝点击