btcd代码之私钥、公钥及地址

晓涛_Klaus · · 3016 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

上文《比特币btcd代码之初体验》提到比特币除了主网外,还有Testnet以及Regtest网络。
Testnet是公开的测试网,所有开发都可以访问这个网络,为了避免有人恶意囤积上面的Testnet bitcoin,这个testnet每隔一段时期就会清空并以新的创始块重新开始。这也应该就是代码里面有时候会看到testnet3的原因。
Regtest则是本地的测试网络,这个网络不会公开出去,仅作为本地开发测试使用。
除此之外,还有Segnet即隔离见证的测试网络。而btcd代码里还看到了simnet这个测试网络选项,目测和Regtest一样是本地测试网络,但是具体有什么不同暂时未深入研究。

这篇文章主要从btcd相关代码出发,研究比特币的私钥、公钥以及地址如何生成,详情参见精通比特币第二版(中文版),简单地说:
第一步,也是最重要的一步,从一个随机数生成私钥。比特币软件使用操作系统底层的随机数生成器来产生256位的熵,即私钥。
第二步,通过椭圆曲线乘法可以从私钥计算得到公钥。
第三步,从公钥计算出比特币地址。

以下代码是从《在比特币上表白-使用Golang将誓言存在比特币的区块链上》拷贝过来的代码

package main
 
import (
   "github.com/btcsuite/btcd/btcec"
   "github.com/btcsuite/btcutil"
   "github.com/btcsuite/btcd/chaincfg"
   "fmt"
)
 
func GenerateBTC() (string, string, error) {
   privKey, err := btcec.NewPrivateKey(btcec.S256())
   if err != nil {
      return "", "", err
   }
 
   privKeyWif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, false)
   if err != nil {
      return "", "", err
   }
   pubKeySerial := privKey.PubKey().SerializeUncompressed()
 
   pubKeyAddress, err := btcutil.NewAddressPubKey(pubKeySerial, &chaincfg.MainNetParams)
   if err != nil {
      return "", "", err
   }
 
   return privKeyWif.String(), pubKeyAddress.EncodeAddress(), nil
}
 
func GenerateBTCTest() (string, string, error) {
   privKey, err := btcec.NewPrivateKey(btcec.S256())
   if err != nil {
      return "", "", err
   }
 
   privKeyWif, err := btcutil.NewWIF(privKey, &chaincfg.TestNet3Params, false)
   if err != nil {
      return "", "", err
   }
   pubKeySerial := privKey.PubKey().SerializeUncompressed()
 
   pubKeyAddress, err := btcutil.NewAddressPubKey(pubKeySerial, &chaincfg.TestNet3Params)
   if err != nil {
      return "", "", err
   }
 
   return privKeyWif.String(), pubKeyAddress.EncodeAddress(), nil
}
 
func main()  {
   wifKey, address, _ := GenerateBTCTest() // 测试地址
   // wifKey, address, _ := GenerateBTC() // 正式地址
   fmt.Println(address, wifKey)
}

分析如下:

随机数与私钥

privKey, err := btcec.NewPrivateKey(btcec.S256())

首先,在之前clone的btcd代码里,其中有一个模块叫btcec,它实现了比特币所需的椭圆曲线密码算法。S256返回的是一个实现了secp256k1标准所定义的一种特殊的椭圆曲线。再查看NewPrivateKey实现细节,发现是把一个椭圆曲线以及随机数生成器传入了crypto包。
这段代码直接返回了私钥和公钥,即上述的第一步和第二步。需要注意这代码里key是PrivateKey类型的对象,其有变量存放了公钥。

// github.com/btcsuite/btcd/btcec/privkey.go
// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey
// instead of the normal ecdsa.PrivateKey.
func NewPrivateKey(curve elliptic.Curve) (*PrivateKey, error) {
    key, err := ecdsa.GenerateKey(curve, rand.Reader)
    if err != nil {
        return nil, err
    }
    return (*PrivateKey)(key), nil
}

这里刚好可以和比特币源码研读(1)--私钥 文章里做一对比,可以发现在C++版本里,其随机数生成的随机性应该更强,因为它同时采用了几个不同的随机源,而Golang版本则只用了软件提供的rand方法。这可能会有潜在的隐患,假如某一环境下软件的随机数分布不均衡,随机数算法中选取了某一段区域内的高概率随机数。那么原本理论上2^256个范围便会大大缩小,黑客从而提高了随机性碰撞的可能性。

WIF

privKeyWif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, false)

继续看代码,这句是把256位数字的私钥以钱包导入格式(WIF)来表示。同样的256位数字的私钥,通过不同编码可以有不同的表示,WIF是其中一种格式。


相同的密钥,不同的格式

需要留意这里用到了与btcd项目同属的btcutil代码,可以把它看成是通用类的包。

地址

pubKeyAddress, err := btcutil.NewAddressPubKey(pubKeySerial, &chaincfg.MainNetParams)

和私钥的WIF类似,公钥可以通过一些算法来计算出其地址,详情需要参考《精通比特币》的讲解。


如何从公钥生成比特币地址

就这样,一个符合规则的比特币地址以及私钥就这样生成出来了,可以在https://btc.com上检查地址格式是否正确。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:晓涛_Klaus

查看原文:btcd代码之私钥、公钥及地址

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3016 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传