bitcoin 地址是如何生成的

本贴最后更新于 1889 天前,其中的信息可能已经时移世易

来自简书

btc address: 1FmWXNJT3jVKaHBQs2gAs6PLGVWx1zPPHf

手动生成 Bitcoin 地址看似有点儿事倍功半,如果你了解了这个过程,就会明白眼花缭乱的 Bitcoin 分叉币地址只是私钥的不同显示方式,对了解 Eth EOS 地址也很有帮助,也可以更清楚的了解 Bitcoin 是如何花费的,一通则百通,妙哉。

生成 Bitcoin 地址,先由私钥生成公钥,再有公钥生成 hash160,最后 hash160 再进行 base58 运算得到地址,如下图所示:

生成私钥,再得到公钥,是由 ECDSA 实现的。ECDSA 是 Elliptic Curve Digital Signature Algorithm 的缩写, 即椭圆曲线数字签名算法

椭圆曲线其实不是椭圆,而是下面的模样:

比特币用到的椭圆曲线是 secp256k1: y^2=x^3+7
那我们言归正传,直接进入主题,我把这个过程分成了 8 个步骤。

Step1. 生成私钥

Bitcoin 要使用到 Secp256k1 这条特殊的椭圆曲线得到公私钥,
我们通过 OpenSSL 命令来生成私钥。

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

# 结果
ccea9c5a20e2b78c2e0fbdd8ae2d2b67e6b1894ccb7a55fc1de08bd53994ea64

这里的结果是个 16 进制数据
0xccea9c5a20e2b78c2e0fbdd8ae2d2b67e6b1894ccb7a55fc1de08bd53994ea64

Step2. 生成公钥

通过 priv.pem 生成 pub_key

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

# 输出
04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

输出 DER 格式, 字符长度是 130
pub_key = 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d
这是未压缩公钥,压缩公钥是 03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69

详细内容请看压缩公钥与非压缩公钥

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 地址前缀符有很多种, 具体看 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 地址能反向得到 hash160_val 么?

汇总代码: gen_addr.rb

可以使用 bitcoin-ruby 生成地址

require 'bitcoin'

pri_key, pub_key = Bitcoin.generate_key # 私钥 公钥
# 通过ffi调用OpenSSL得到,很多类库都是这么做的
address = Bitcoin::pubkey_to_address(pub_key)

生成测试网地址

require 'bitcoin'

Bitcoin::network = :testnet #使用测试网
pri_key, pub_key = Bitcoin.generate_key
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
https://github.com/liushooter/learn-blockchain/blob/master/gen_addr.rb

  • 比特币

    比特币(BitCoin)的概念最初由中本聪在 2009 年提出,根据中本聪的思路设计发布的开源软件以及建构其上的 P2P 网络。比特币是一种 P2P 形式的数字货币。点对点的传输意味着一个去中心化的支付系统。

    27 引用 • 169 回帖 • 79 关注
  • 钱包
    7 引用 • 35 回帖
  • 私钥
    3 引用 • 2 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...