bitcoin地址是如何生成的

By @liushooter3/9/2018bitcoin

根据How to create Bitcoin Address的步骤,
我们要先得到 ECDSA private key, 再得到 ECDSA public key, 然后再计算出钱包地址.
ECDSA是Elliptic Curve Digital Signature Algorithm的缩写, 即椭圆曲线数字签名算法.

Step1. 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

Step2. 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

Step3. 第2步结果进行hash160运算

hash160运算就是先进行SHA256, 再进行RMD160

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

hash160_val = 2b6f3b9e337cedbb7c40839523fb1100709c12f7

Step4. 第3步结果加上前缀符

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

'00'+ '2b6f3b9e337cedbb7c40839523fb1100709c12f7'

step_04 = 002b6f3b9e337cedbb7c40839523fb1100709c12f7

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

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

checksum = 86b2e90c

Step6. 第4步结果 跟 第5步结果合并

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

step_06 = 002b6f3b9e337cedbb7c40839523fb1100709c12f786b2e90c

Step7. Base58编码

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

来个base58算法

def encode_base58(int_val, leading_zero_bytes=0)
  alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
  base58_val, base = '', alpha.size

  while int_val > 0
    int_val, remainder = int_val.divmod(base)
    base58_val = alpha[remainder] + base58_val
  end

  base58_val
end

Step8. 第6步结果进行base58编码

step_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 address能反向得到hash160_val么 ?!

终于大功告成

还是乖乖的用bitcoin-ruby

require 'bitcoin'

pri_key, pub_key = Bitcoin.generate_key # 私钥 公钥
# 通过ffi调用openssl得到
# 很多类库都这么做 怕出致命错误吧
address = Bitcoin::pubkey_to_address(pub_key)

在bitcoin系统中,私钥能得公钥, 公钥能得到钱包地址,
私钥=>公钥=>钱包地址, 而反向是不可能的.
牢记你的私钥, 而且私钥不能修改, 谁掌握了私钥谁就拥有了这些币!!!


参考:

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

comments