Bitcoin Core是根正苗红的比特币全节点钱包软件,由创始人中本聪最早完成,编程语言是C++,对于一些现代程序员来说,理解起来有一定难度,所以有些开发者将这些代码移植为其它编程语言。

Bitcoin在.NET平台的一款实现就是今天要介绍的NBitcoin,项目站点:

https://github.com/MetacoSA/NBitcoin

我使用的软件集成开发平台是Visual Studio 2015,.Net Framework是4.5.2。

安装NBitcoin引用

我建立了一个控制台应用HelloBitcoin,需要添加NBitcoin类库的引用,最方便的办法是用NuGet,操作办法见下图,注意可能需要翻墙。

安装过程中,会对整个解决方案的依赖类库进行一系列修改, 并且让你接受软件许可授权,可能涉及到的类库:

Newtonsoft.Json 11.0.2

System.Buffers 4.5.0

System.Collections 4.0.11

System.Collections.Concurrent 4.0.12

System.Diagnostics.Debug 4.0.11

System.Globalization 4.0.11

System.Linq 4.1.0

System.Net.Http 4.3.3

System.Net.Requests 4.3.0

System.Reflection 4.1.0

System.Resources.ResourceManager 4.0.1

System.Runtime.Extensions 4.1.0

System.Runtime.InteropServices 4.1.0

Microsoft.Extensions.Logging.Abstractions 1.0.2

最后在调试输出窗口中出现这样一行:

已将“NBitcoin 4.1.1.71”成功安装到 HelloBitcoin

表示NBitcoin安装成功。

用私钥生成比特币地址

下面一段简单的代码用来将一串私钥生成为比特币地址,请与《我生成的比特币地址竟然与别人的重合了》这篇文章一起参考阅读。

程序开头不要忘记这一行:

using NBitcoin;

直接上代码:

简要解释说明一下:

privKeys是私钥,为256位二进制数,NBitcoin中与私钥对应的类是Key。

下面这行语句生成一个随机私钥。

Key k = new Key();

私钥可以生成公钥,再生成比特币地址,借用一下《精通比特币》中的这张原理图。

Network.Main表示使用比特币的真实主网络,直接用真金白银的BTC调试程序太奢侈,可以切换到测试网络Network.TestNet。

程序最终输出:

19t4GGYorFziM26CRMYvxqvDw6NPhuCRJS

对应于BitcoinExplorer工具的命令行是:

bx ec-to-public 3243……E6C8 | bx bitcoin160 | bx address-encode

可以看到,最后生成的比特币地址是一致的。

压缩公钥、非压缩公钥

一个私钥实际上可以产生出两种不同的比特币地址,这个问题有点迷惑人,但这是由于椭圆曲线的特性造成的,先看代码。

k1生成出来的是压缩公钥:

为了方便阅读,我人为插入了空格。

02 2e88d239fb78cee0c1c55943a96dcc8b70adf47e18b53f9ba110b6fb871e1f8b

k2对应于非压缩公钥:

请注意与上面的区别。

04 2e88d239fb78cee0c1c55943a96dcc8b70adf47e18b53f9ba110b6fb871e1f8b b119f9161df032167181d623a401dde4091c3e0be2001e4dea3e1f53f851aa3a

拆开说明这一长串数字的具体含义:

04 // 表示非压缩公钥

2e88d239fb78cee0c1c55943a96dcc8b70adf47e18b53f9ba110b6fb871e1f8b // 椭圆曲线上点的X坐标

b119f9161df032167181d623a401dde4091c3e0be2001e4dea3e1f53f851aa3a // 椭圆曲线上点的Y坐标

椭圆曲线有一个重要特性,它是以X轴为对称轴的,这样记录一个点的坐标时,只需要记录X坐标,省略Y坐标,只需要知道Y坐标的正负号,就可以根据X计算出Y。

在二进制的椭圆曲线运算中,没有正负号,但有奇偶性,只记录X坐标及奇偶性,就是压缩公钥的表示法,刚才的那个公钥就可以节省一半的存储空间。

02 // 表示压缩公钥,Y坐标为偶数

2e88d239fb78cee0c1c55943a96dcc8b70adf47e18b53f9ba110b6fb871e1f8b // 椭圆曲线上点的X坐标

两种公钥表现形式不一样,生成的比特币地址当然就不一样:

地址1:

19t4GGYorFziM26CRMYvxqvDw6NPhuCRJS

地址2:

17mKugcBDEJbu391Fq41AdwLeGHwJLPRDf

比特币的交易信息中经常存储公钥,压缩公钥节省了大量存储空间,意义重大,后来的钱包软件主要都使用压缩公钥,也就是常用上面的地址1

19t4GGYorFziM26CRMYvxqvDw6NPhuCRJS

WIF钱包导入格式

私钥是一长串数字,比如:3243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C8,输入费劲,还容易出错,中本聪就规定了一种WIF格式,称为Wallet Import Format,这种格式的私钥可以非常方便地导入到Bitcoin Core等钱包软件中。

用GetWif()函数可以非常容易地得到WIF格式的私钥。

以K开头的为压缩表示法:

KxuRLWqnfcgs8ru7YiMBfP6T71jK9twCedaeBtHgRgzb8adnoZzH

以5开头的是非压缩表示法,但本质上两个私钥是一模一样的:

5JCRYcJrKGLTK6R3PbHopfY9BRdmtrq5TCTesx7x9mQUDeYDfZj

P2SH地址、隔离见证地址

前面见到的比特币地址都是以1开头的:

19t4GGYorFziM26CRMYvxqvDw6NPhuCRJS

17mKugcBDEJbu391Fq41AdwLeGHwJLPRDf

很多人现在看到的是以3开头的地址,这种地址术语是P2SH地址,即Pay-to-Script Hash地址,这类地址是由交易脚本创建的,具体原理先不用了解,代码很方便。

pk1.GetScriptAddress(Network.Main)

结果是这样的:

33yjfa31iqdDRqHqQRuvySB7SVV7wd77aj

后来又出来了隔离见证地址,采用Bech32格式编码。

pk1.GetSegwitAddress(Network.Main)

样子是这样的: bc1qv950rspcgfqufasc29calr5qphh4ucl3ur7vnm

看到这样的地址别以为收到了假比特币。

以前在给多个人发币时,使用NBitcoin时踩了一些坑:

当时想参考NBitcoin的API文档:

https://metacosa.github.io/NBitcoin/api/index.html

可惜文档非常糟糕,还不如果直接看源代码,几乎是边google边试错完成了程序。

发表评论