文档章节

比特币地址是怎样生成的

從此迷花粉
 從此迷花粉
发布于 2018/08/23 16:20
字数 930
阅读 34
收藏 0

首先得到 ECDSA private key, 再得到 ECDSA public key,,然后再计算出钱包地址。ECDSA是Elliptic Curve Digital Signature Algorithm的缩写, 即椭圆曲线数字签名算法。

1、ECDSA private key:执行openssl命令, bitcoin要用到secp256k1

openssl ecparam -name secp256k1 -genkey > priv.pem

# DER格式
openssl ec -in priv.pem -outform DER | tail -c +8 | head -c 32 | xxd -p -c 32

# 输出
read EC key
writing EC key
ccea9c5a20e2b78c2e0fbdd8ae2d2b67e6b1894ccb7a55fc1de08bd53994ea64

得到秘钥文件priv.pem, 输出DER格式, 长度是 64
ccea9c5a20e2b78c2e0fbdd8ae2d2b67e6b1894ccb7a55fc1de08bd53994ea64

 

2、ECDSA public key: priv.pem 生成 pub_key

openssl ec -in priv.pem -pubout -outform DER | tail -c 65 | xxd -p -c 65

# 输出
read EC key
writing EC key
04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

同样输出DER格式, 长度是130
pub_key = 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

3、第2步结果进行hash160运算:hash160运算就是先进行SHA256, 再进行RMD160

bytes = [pub_key].pack("H*") # 转为16进制
hash160_val = Digest::RMD160.hexdigest(Digest::SHA256.digest(bytes) )

hash160_val = 2b6f3b9e337cedbb7c40839523fb1100709c12f7

4、第3步结果加上前缀符

前缀符一般是00, 会生成普通的主网地址
bitcoin address 前缀符有好几种, 具体看https://en.bitcoin.it/wiki/List_of_address_prefixes

'00'+ '2b6f3b9e337cedbb7c40839523fb1100709c12f7'

result_04 = 002b6f3b9e337cedbb7c40839523fb1100709c12f7

bitcoin地址的前缀列表列出了几种钱包地址的类型

十进制 16进制 作用 首字母 例子
0 00 P2PKH address 1 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem
5 05 P2SH address 3 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
111 6F Testnet pub key m or n mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn

5、第4步结果, 执行2次SHA256, 取前8位作为校验和

hex_str = [result_04].pack("H*")
checksum = Digest::SHA256.hexdigest(Digest::SHA256.digest(hex_str) )[0...8]

checksum = 86b2e90c

6、第4步结果 跟 第5步结果合并

'002b6f3b9e337cedbb7c40839523fb1100709c12f7' + '86b2e90c'
# result_04 + checksum

result_06 = 002b6f3b9e337cedbb7c40839523fb1100709c12f786b2e90c

7、第6步结果进行base58编码

Base58是一种独特的编码方式, 是Base64的变形, 主要用于Bitcoin的钱包地址.
相比Base64, Base58去掉了数字0, 大写字母O, 大写字母I, 小写字母l+/, 避免引起视觉混淆。

result_06 = "002b6f3b9e337cedbb7c40839523fb1100709c12f786b2e90c"
leading_zero_bytes = (step_06.match(/^([0]+)/) ? $1 : '').size / 2
# leading_zero_bytes的作用是字母填充, 待研究下

address = ("1" * leading_zero_bytes) + encode_base58(step_06.to_i(16) )

得到 14xfJr1DArtYR156XBs28FoYk6sQqirT2s, 这就是了一个标准的bitcoin地址。

在bitcoin系统中,私钥能得公钥, 公钥能得到钱包地址,
私钥=>公钥=>钱包地址, 而反向是不可能的。

 

How to create Bitcoin Address

 

Java Base58算法


public class Base58 {
    public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
    private static final char ENCODED_ZERO;
    private static final int[] INDEXES;

    public Base58() {
    }

    public static String encode(byte[] input) {
        if (input.length == 0) {
            return "";
        } else {
            int zeros;
            for(zeros = 0; zeros < input.length && input[zeros] == 0; ++zeros) {
                ;
            }

            input = Arrays.copyOf(input, input.length);
            char[] encoded = new char[input.length * 2];
            int outputStart = encoded.length;
            int inputStart = zeros;

            while(inputStart < input.length) {
                --outputStart;
                encoded[outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
                if (input[inputStart] == 0) {
                    ++inputStart;
                }
            }

            while(outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
                ++outputStart;
            }

            while(true) {
                --zeros;
                if (zeros < 0) {
                    return new String(encoded, outputStart, encoded.length - outputStart);
                }

                --outputStart;
                encoded[outputStart] = ENCODED_ZERO;
            }
        }
    }

    public static byte[] decode(String input) throws AddressFormatException {
        if (input.length() == 0) {
            return new byte[0];
        } else {
            byte[] input58 = new byte[input.length()];

            int zeros;
            int outputStart;
            for(zeros = 0; zeros < input.length(); ++zeros) {
                char c = input.charAt(zeros);
                outputStart = c < 128 ? INDEXES[c] : -1;
                if (outputStart < 0) {
                    throw new AddressFormatException("Illegal character " + c + " at position " + zeros);
                }

                input58[zeros] = (byte)outputStart;
            }

            for(zeros = 0; zeros < input58.length && input58[zeros] == 0; ++zeros) {
                ;
            }

            byte[] decoded = new byte[input.length()];
            outputStart = decoded.length;
            int inputStart = zeros;

            while(inputStart < input58.length) {
                --outputStart;
                decoded[outputStart] = divmod(input58, inputStart, 58, 256);
                if (input58[inputStart] == 0) {
                    ++inputStart;
                }
            }

            while(outputStart < decoded.length && decoded[outputStart] == 0) {
                ++outputStart;
            }

            return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
        }
    }

    public static BigInteger decodeToBigInteger(String input) throws AddressFormatException {
        return new BigInteger(1, decode(input));
    }

    public static byte[] decodeChecked(String input) throws AddressFormatException {
        byte[] decoded = decode(input);
        if (decoded.length < 4) {
            throw new AddressFormatException("Input too short");
        } else {
            byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
            byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
            byte[] actualChecksum = Arrays.copyOfRange(Sha256Hash.hashTwice(data), 0, 4);
            if (!Arrays.equals(checksum, actualChecksum)) {
                throw new AddressFormatException("Checksum does not validate");
            } else {
                return data;
            }
        }
    }

    private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
        int remainder = 0;

        for(int i = firstDigit; i < number.length; ++i) {
            int digit = number[i] & 255;
            int temp = remainder * base + digit;
            number[i] = (byte)(temp / divisor);
            remainder = temp % divisor;
        }

        return (byte)remainder;
    }

    static {
        ENCODED_ZERO = ALPHABET[0];
        INDEXES = new int[128];
        Arrays.fill(INDEXES, -1);

        for(int i = 0; i < ALPHABET.length; INDEXES[ALPHABET[i]] = i++) {
            ;
        }

    }
}

 

本文转载自:https://www.jianshu.com/p/954e143e97d2

共有 人打赏支持
從此迷花粉
粉丝 8
博文 174
码字总数 63344
作品 0
海淀
程序员
私信 提问
C#程序如何对接比特币钱包节点?

NBitcoin是.NET平台上最完整的比特币开发包,也是每一个C#开发人员必备的比特币开发包。NBitcoin几乎实现了所有相关的比特币改进提议,并且提供了对比特币协议底层元语的访问支持,因此你可以...

汇智网教程
02/28
0
0
区块链 100 讲:如果连电子钱包都不懂,还谈什么区块链开发 (有彩蛋)

区块链100讲,本期来讲讲“钱包”...“钱包谁没有?还用你讲?”隔壁桌二花拿出钱包晃了晃,“我要讲的是区块链里的钱包...”嗯,好吧,对着二花弹琴也很累心的.. 1 什么是钱包 The word "wa...

yanyan
2018/07/10
0
0
007-050-越写越快乐之浅谈Base58Check的使用

今天的越学越快乐系列文章给大家分享一下Base58Check的使用,也就是比特币地址的生成过程中使用的校验函数。 Base58是什么 我们都知道要想生成一个比特币地址,需要有以下几步: 私钥k生成公...

韬声依旧在路上
2018/09/07
0
0
Java如何离线生成比特币地址

如果你希望在自己的桌面Java应用或者手机安卓应用中集成对比特币 支付的支持,例如,离线生成比特币私钥和地址、接收比特币支付、多重签名转账、 查询钱包余额等,那么使用bitcoinj这个超高人...

编程狂魔
2018/10/08
0
0
比特币协议bitcoin raw protocol如何理解

媒体对比特币的关注让我开始了解比特币的真正运作方式,直至流经网络的字节数。普通人使用隐藏真实情况的软件,但我想亲自了解比特币协议。我的目标是直接使用比特币系统:手动创建比特币交易...

编程狂魔
01/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
今天
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0
最近研究如何加速UI界面开发,有点感觉了

最近在开发JFinal学院的JBolt开发平台,后端没啥说的,做各种极简使用的封装,开发者上手直接使用。 JBolt开发平台包含常用的用户、角色、权限、字典、全局配置、缓存、增删改查完整模块、电...

山东-小木
今天
3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字: 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》(李继宏译),第一疑问就是全书即没提到“月亮”,也没提到“六便士”。那这书名又与内容有什么...

原创小博客
昨天
2
0
微信网页授权获取用户信息(ThinkPHP5)+ 微信发送客服消息(一)

以thinkphp5为实例,创建控制器 class Kf extends Controller { /** * [protected description]微信公众号appid * @var [type] */ protected $appid = "xxxxxxxxxxxxxxx"; /** * [protected......

半缘修道半缘君丶
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部