43

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

 5 years ago
source link: https://studygolang.com/articles/13625?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

上文《比特币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是其中一种格式。

jUvyuy6.png!web

相同的密钥,不同的格式

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

地址

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

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

EVn6rmM.png!web

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK