精力有限,后期修订以github为主,建议大家移步github链接阅读更新版本,感谢理解:
https://github.com/tianmingyun/MasterBitcoin2CN
预警:本文5600字,请保存后再浏览。

4.2 比特币地址
比特币地址是一个由数字和字母组成的字符串,可以与任何想给你比特币的人分享。由公钥(一个同样由数字和字母组 成的字符串)生成的比特币地址以数字“1”开头。下面是一个比特币地址的例子:
1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
在交易中,比特币地址通常以收款方出现。如果把比特币交易比作一张支票,比特币地址就是收款人,也就是我们要写入收款人一栏的内容。一张支票的 收款人可能是某个银行账户,也可能是某个公司、机构,甚至是现金支票。支票不需要指定一个特定的账户,而是用一个普通的名字作为收款人,这使它成为一种相当灵活的支付工具。与此类似,比特币地址的使用也使比特币交易变得很灵活。比特币地址可以代表一对公钥和私钥的所有者,也可以代表其 它东西,比如会在后面的“P2SH (Pay-to-Script-Hash)”一节讲到的付款脚本。现在,让我们来看一个简单的例子,由公钥生成比特币地址。
比特币地址可由公钥经过单向的加密哈希算法得到。哈希算法是一种单向函数,接收任意长度的输入产生指纹摘要。加密哈希函数在比特币中被广泛使用 :比特币地址、脚本地址以及在挖矿中的工作量证明算法。由公钥生成比特币地址时使用的算法是Secure Hash Algorithm (SHA)和the RACE Integ rity Primitives Evaluation Message Digest (RIPEMD),特别是SHA256和RIPEMD160。
以公钥 K 为输入,计算其SHA256哈希值,并以此结果计算RIPEMD160 哈希值,得到一个长度为160位(20字节)的数字:
A = RIPEMD160(SHA256(K))
公式中,K是公钥,A是生成的比特币地址。
提示比特币地址与公钥不同。比特币地址是由公钥经过单向的哈希函数生成的。
通常用户见到的比特币地址是经过“Base58Check”编码的(参见“Base58和Base58Check编码”一节),这种编码 使用了58个字符(一种Base58数字系统)和校验码,提高了可读性、避免歧义并有效防止了在地址转录和输入中产生 的错误。Base58Check编码也被用于比特币的其它地方,例如比特币地址、私钥、加密的密钥和脚本哈希中,用来提 高可读性和录入的正确性。下一节中我们会详细解释Base58Check的编码机制,以及它产生的结果。
下图描述了如何从公钥生成比特币地址。

4.2.1 Base58和Base58Check编码
为了更简洁方便地表示长串的数字,许多计算机系统会使用一种以数字和字母组成的大于十进制的表示法。例如,传统 的十进制计数系统使用0-9十个数字,而十六进制系统使用了额外的 A-F 六个字母。一个同样的数字,它的十六进制表 示就会比十进制表示更短。更进一步,Base64使用了26个小写字母、26个大写字母、10个数字以及两个符号(例 如“+”和“/”),用于在电子邮件这样的基于文本的媒介中传输二进制数据。Base64通常用于编码邮件中的附件。Base58 是一种基于文本的二进制编码格式,用在比特币和其它的加密货币中。这种编码格式不仅实现了数据压缩,保持了易读 性,还具有错误诊断功能。Base58是Base64编码格式的子集,同样使用大小写字母和10个数字,但舍弃了一些容易错 读和在特定字体中容易混淆的字符。具体地,Base58不含Base64中的0(数字0)、O(大写字母o)、l(小写字母 L)、I(大写字母i),以及“+”和“/”两个字符。简而言之,Base58就是由不包括(0,O,l,I)的大小写字母和数字组 成。
例4-1 比特币的Base58字母表
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
Base58Check是一种常用在比特币中的Base58编码格式,增加了错误校验码来检查数据在转录中出现的错误。校验码长4个字节,添加到需要编码的数据 之后。校验码是从需要编码的数据的哈希值中得到的,所以可以用来检测并避免转录和输入中产生的错误。使用Base58check编码格式时,编码软件会计 算原始数据的校验码并和结果数据中自带的校验码进行对比。二者不匹配则表明有错误产生,那么这个Base58Check格式的数据就是无效的。例如,一个 错误比特币地址就不会被钱包认为是有效的地址,否则这种错误会造成资金的丢失。
为了使用Base58Check编码格式对数据(数字)进行编码,首先我们要对数据添加一个称作“版本字节”的前缀,这个前缀用来明确需要编码的数据的类 型。例如,比特币地址的前缀是0(十六进制是0x00),而对私钥编码时前缀是128(十六进制是0x80)。 表4-1会列出一些常见版本的前缀。
接下来,我们计算“双哈希”校验码,意味着要对之前的结果(前缀和数据)运行两次SHA256哈希算法:
checksum = SHA256(SHA256(prefix+data))
在产生的长32个字节的哈希值(两次哈希运算)中,我们只取前4个字节。这4个字节就作为校验码。校验码会添加到数 据之后。
结果由三部分组成:前缀、数据和校验码。这个结果采用之前描述的Base58字母表编码。下图描述了Base58Check编 码的过程。

在比特币中,大多数需要向用户展示的数据都使用Base58Check编码,可以实现数据压缩,易读而且有错误检验。 Base58Check编码中的版本前缀是数据的格式易于辨别,编码之后的数据头包含了明确的属性。这些属性使用户可以 轻松明确被编码的数据的类型以及如何使用它们。例如我们可以看到他们的不同,Base58Check编码的比特币地址是以1开头的,而Base58Check编码的私钥WIF是以5开头的。表4-1展示了一些版本前缀和他们对应的Base58格式。
表4-1 Base58Check版本前缀和编码后的结果

我们回顾比特币地址产生的完整过程,从私钥、到公钥(椭圆曲线上某个点)、再到两次哈希的地址,最终产生 Base58Check格式的比特币地址。例4-3的C++代码完整详细的展示了从私钥到Base58Check编码后的比特币地址的 步骤。代码中使用“3.3 其他客户端、资料库、工具包 ”一节中介绍的libbitcoin library来实现某些辅助功能。
例4-3.从私钥中创建Base58Check编码的比特币地址
link:code/addr.cpp[]
代码使用预定义的私钥在每次运行时产生相同的比特币地址,如下例所示
例4-3.编译并运行addr代码
Compile the addr.cpp code
$ g++ -o addr addr.cpp $(pkg-config --cflags --libs libbitcoin)
Run the addr executable
$ ./addr
Public key: 0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa
Address: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK
4.2.2 密钥的格式
公钥和私钥的都可以有多种编码格式。一个密钥被不同的格式编码后,虽然结果看起来可能不同,但是密钥所编码数字 并没有改变。这些不同的编码格式主要是用来方便人们无误地使用和识别密钥。
4.2.2.1私钥的格式
私钥可以以许多不同的格式表示,所有这些都对应于相同的256位的数字。表4-2展示了私钥的三种常见格式。

表4-3 示例:同样的私钥,不同的格式

这些表示法都是用来表示相同的数字、相同的私钥的不同方法。虽然编码后的字符串看起来不同,但不同的格式彼此之 间可以很容易地相互转换。请注意,“raw binary”未显示在表4-3 示例中,根据定义此处显示的任何编码的格式,不是raw binary数据。
我们使用Bitcoin Explorer中的wif-to-ec命令(请参阅[appdx_bx])来显示两个WIF键代表相同的私钥:
$ bx wif-to-ec 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
$ bx wif-to-ec KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
4.2.3从Base58Check解码
Bitcoin Explorer命令(参见[appdx_bx])使得编写shell脚本和命令行“管道”变得容易,这些方式可以处理比特币密钥,地址和交易。 您可以使用Bitcoin Explorer在命令行上解码Base58Check格式。
我们使用base58check-decode命令解码未压缩的密钥:
$ bx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
wrapper
{
checksum 4286807748
payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
version 128
}
结果包含密钥作为有效载荷,WIF版本前缀128和校验和。
请注意,压缩密钥的“有效负载”附加了后缀01,表示导出的公钥要压缩:
$ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
wrapper
{
checksum 2339607926
payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01
version 128
}
将十六进制转换为Base58Check编码
要转换成Base58Check(与上一个命令相反),我们使用Bitcoin Explorer的base58check-encode命令(请参阅[appdx_bx]),并提供十六进制私钥,其次是WIF版本前缀128:
bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd --version 128
5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
将十六进制(压缩格式密钥)转换为Base58Check编码
要将压缩格式的私钥编码为Base58Check(参见“压缩格式私钥”一节),我们需在十六进制私钥的后面添加后缀01,然 后使用跟上面一样的方法:
$ bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 --version 128
KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
生成的WIF压缩格式的私钥以字母“K”开头,用以表明被编码的私钥有一个后缀“01”,且该私钥只能被用于生成压缩格式 的公钥(参见“压缩格式公钥”一节)。
4.2.3.1公钥的格式
公钥也可以用多种不同格式来表示,最重要的是它们分为非压缩格式或压缩格式公钥这两种形式。
我们从前文可知,公钥是在椭圆曲线上的一个点,由一对坐标(x,y)组成。公钥通常表示为前缀04紧接着两个256比 特的数字。其中一个256比特数字是公钥的x坐标,另一个256比特数字是y坐标。前缀04是用来区分非压缩格式公钥, 压缩格式公钥是以02或者03开头。
下面是由前文中的私钥所生成的公钥,其坐标x和y如下:
x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
下面是同样的公钥以520比特的数字(130个十六进制数字)来表达。这个520比特的数字以前缀04开头,紧接着是x及y 坐标,组成格式为04 x y:
K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE 52DDFE2E505BDB
4.2.3.2压缩格式公钥
引入压缩格式公钥是为了减少比特币交易的字节数,从而可以节省那些运行区块链数据库的节点磁盘空间。大部分比特 币交易包含了公钥,用于验证用户的凭据和支付比特币。每个公钥有520比特(包括前缀,x坐标,y坐标)。如果每个 区块有数百个交易,每天有成千上万的交易发生,区块链里就会被写入大量的数据。
正如我们在“4.1.4 公钥”一节所见,一个公钥是一个椭圆曲线上的点(x, y)。而椭圆曲线实际是一个数学方程,曲线上的点 实际是该方程的一个解。因此,如果我们知道了公钥的x坐标,就可以通过解方程y2 mod p = (x3 + 7) mod p得到y坐 标。这种方案可以让我们只存储公钥的x坐标,略去y坐标,从而将公钥的大小和存储空间减少了256比特。每个交易所 需要的字节数减少了近一半,随着时间推移,就大大节省了很多数据传输和存储。
未压缩格式公钥使用04作为前缀,而压缩格式公钥是以02或03作为前缀。需要这两种不同前缀的原因是:因为椭圆曲 线加密的公式的左边是y2 ,也就是说y的解是来自于一个平方根,可能是正值也可能是负值。更形象地说,y坐标可能在 x坐标轴的上面或者下面。从图4-2的椭圆曲线图中可以看出,曲线是对称的,从x轴看就像对称的镜子两面。因此,如果 我们略去y坐标,就必须储存y的符号(正值或者负值)。换句话说,对于给定的x值,我们需要知道y值在x轴的上面还是 下面,因为它们代表椭圆曲线上不同的点,即不同的公钥。当我们在素数p阶的有限域上使用二进制算术计算椭圆曲线的时候,y坐标可能是奇数或者偶数,分别对应前面所讲的y值的正负符号。因此,为了区分y坐标的两种可能值,我们在 生成压缩格式公钥时,如果y是偶数,则使用02作为前缀;如果y是奇数,则使用03作为前缀。这样就可以根据公钥中给 定的x值,正确推导出对应的y坐标,从而将公钥解压缩为在椭圆曲线上的完整的点坐标。下图阐释了公钥压缩:

下面是前述章节所生成的公钥,使用了264比特(66个十六进制数字)的压缩格式公钥格式,其中前缀03表示y坐标是 一个奇数:
K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
这个压缩格式公钥对应着同样的一个私钥,这意味它是由同样的私钥所生成。但是压缩格式公钥和非压缩格式公钥差别 很大。更重要的是,如果我们使用双哈希函数(RIPEMD160(SHA256(K)))将压缩格式公钥转化成比特币地址,得到的地 址将会不同于由非压缩格式公钥产生的地址。这种结果会让人迷惑,因为一个私钥可以生成两种不同格式的公钥——压 缩格式和非压缩格式,而这两种格式的公钥可以生成两个不同的比特币地址。但是,这两个不同的比特币地址的私钥是 一样的。
压缩格式公钥渐渐成为了各种不同的比特币客户端的默认格式,它可以大大减少交易所需的字节数,同时也让存储区块 链所需的磁盘空间变小。然而,并非所有的客户端都支持压缩格式公钥,于是那些较新的支持压缩格式公钥的客户端就 不得不考虑如何处理那些来自较老的不支持压缩格式公钥的客户端的交易。这在钱包应用导入另一个钱包应用的私钥的 时候就会变得尤其重要,因为新钱包需要扫描区块链并找到所有与这些被导入私钥相关的交易。比特币钱包应该扫描哪 个比特币地址呢?新客户端不知道应该使用哪个公钥:因为不论是通过压缩的公钥产生的比特币地址,还是通过非压缩 的公钥产生的地址,两个都是合法的比特币地址,都可以被私钥正确签名,但是他们是完全不同的比特币地址。
为了解决这个问题,当私钥从钱包中被导出时,较新的比特币客户端将使用一种不同的钱包导入格式(Wallet Import Format)。这种新的钱包导入格式可以用来表明该私钥已经被用来生成压缩的公钥,同时生成的比特币地址也是基于该 压缩的公钥。这个方案可以解决导入私钥来自于老钱包还是新钱包的问题,同时也解决了通过公钥生成的比特币地址是 来自于压缩格式公钥还是非压缩格式公钥的问题。最后新钱包在扫描区块链时,就可以使用对应的比特币地址去查找该 比特币地址在区块链里所发生的交易。我们将在下一节详细解释这种机制是如何工作的。
4.2.3.3压缩格式私钥
实际上“压缩格式私钥”是一种名称上的误导,因为当一个私钥被使用WIF压缩格式导出时,不但没有压缩,而且比“非压 缩格式”私钥长出一个字节。这个多出来的一个字节是私钥被加了后缀01,用以表明该私钥是来自于一个较新的钱包, 只能被用来生成压缩的公钥。私钥是非压缩的,也不能被压缩。“压缩的私钥”实际上只是表示“用于生成压缩格式公钥的 私钥”,而“非压缩格式私钥”用来表明“用于生成非压缩格式公钥的私钥”。为避免更多误解,应该只可以说导出格式 是“WIF压缩格式”或者“WIF”,而不能说这个私钥是“压缩”的。
表4示例:相同的键,不同的格式

请注意,十六进制压缩私钥格式在末尾有一个额外的字节(十六进制为01)。 虽然Base58编码版本前缀对于WIF和WIF压缩格式都是相同的(0x80),但在数字末尾添加一个字节会导致Base58编码的第一个字符从5变为K或 L,考虑到对于Base58这是十进制编码100号和99号之间的差别。对于100是一个数字长于99的数字,它有一个前缀1,而不是前缀9。当长度变化 ,它会影响前缀。 在Base58中,前缀5改变为K或L,因为数字的长度增加一个字节。
要注意的是,这些格式并不是可互换使用的。在较新的实现了压缩格式公钥的钱包中,私钥只能且永远被导出为WIF压 缩格式(以K或L为前缀)。对于较老的没有实现压缩格式公钥的钱包,私钥将只能被导出为WIF格式(以5为前缀)导 出。这样做的目的就是为了给导入这些私钥的钱包一个信号:到底是使用压缩格式公钥和比特币地址去扫描区块链,还 是使用非压缩格式公钥和比特币地址。
如果一个比特币钱包实现了压缩格式公钥,那么它将会在所有交易中使用该压格式缩公钥。钱包中的私钥将会被用来生 成压缩格式公钥,压缩格式公钥然后被用来生成交易中的比特币地址。当从一个实现了压缩格式公钥的比特币钱包导出 私钥时,钱包导入格式(WIF)将会被修改为WIF压缩格式,该格式将会在私钥的后面附加一个字节大小的后缀01。最 终的Base58Check编码格式的私钥被称作WIF(“压缩”)私钥,以字母“K”或“L”开头。而以“5”开头的是从较老的钱包中 以WIF(非压缩)格式导出的私钥。
提示 “压缩格式私钥”是一个不当用词!私钥不是压缩的。WIF压缩格式的私钥只是用来表明他们只能被生成压缩的公钥 和对应的比特币地址。相反地,“WIF压缩”编码的私钥还多出一个字节,因为这种私钥多了后缀“01”。该后缀是用 来区分“非压缩格式”私钥和“压缩格式”私钥。
本章未完,待续。
参考内容:
1、本文部分内容摘自《精通比特币》第一版中译本,特此说明并致谢。
本文翻译过程中得到许多朋友(包括区块链研习社higer社长等)的支持和鼓励,在此表示感谢,另外精力有限难免纰漏,也非常希望大家提出宝贵意见和建议,欢迎大家关注下面的二维码加入小密圈:认知学习区块链(免费),进行交流。

网友评论