比特币地址是一串由数字和字母构成的字符串,可以分享给任何想给你转钱的人。地址是从公钥转换而来的,包含数字和字母,其第一个字符是1(数字)。下面是一个比特币地址的例子。
比特币地址在交易中通常以资金“接收者”的形式出现。如果我们将比特币的交易与纸质支票做个类比,那么比特币地址就相当于支票上的受益人,也就是我们写在支票“收款人”后的内容。在纸质支票上,受益人可以是一个银行账户持有者的姓名,也可以是公司、机构,甚至现金。由于支票不需要指定账号,只是使用一个抽象的名字作为资金接收人,因此作为支付工具,支票非常灵活。比特币的交易采用了类似的抽象方法,即比特币地址,使其同样极具灵活性。比特币地址可以指代一个密钥对的拥有者,也可以指代其他一些东西,比如支付脚本。我们将在本章“支付到脚本哈希(P2SH)与多重签名地址”中讲到。现在,我们通过一个简单例子,了解代表公钥的比特币地址是如何由公钥产生的。
比特币地址是通过一种单向的加密哈希算法从公钥推导出来的。“哈希算法”是一种单向函数,它可以对任意长度的输入进行计算,生成输入信息的指纹或者“哈希”。加密哈希函数在比特币中应用广泛,包括比特币地址、脚本地址,以及挖矿中的工作量证明算法等。用于从公钥创建比特币地址的算法是安全哈希算法(SHA)和RACE完整性原语求值信息摘要算法(RIPEMD),应用的是其中两种特定算法,SHA256和RIPEMD160。
我们从公钥K开始,计算它的SHA256哈希值,然后从其结果中计算RIPEMD160的哈希值,这样我们就创建了一个160比特(20字节)长度的数字:
A=RIPEMD160(SHA256(K))
其中,K是公钥;A是计算结果,即比特币地址。
比特币地址与公钥不一样,比特币地址是利用单向哈希函数从公钥计算得来的。
比特币地址基本上是以Base58Check编码形式(参见本章中“Base58和Base58Check编码”)展现给用户的,它使用58个字符(一个Base58数字系统)和一个校验码,能够达到方便阅读、避免歧义、防止地址转录及输入时犯错的效果。Base58Check在比特币中还有很多其他应用,比如比特币地址、用户私钥、加密的密钥、脚本哈希等。在下一节,我们将看到Base58Check的编码和解码机制,以及它们的结果展示。图4.5描述了从公钥到比特币地址的转换过程。
图4.5 从公钥到比特币地址:将公钥转换为比特币地址
为了以更少的符号、更紧凑的方式表示一个很大的数,很多计算机系统采用超过十的进制(底数),并混合使用字母和数字的表示法。举例来说,传统十进制系统使用10个从0到9的数字字符,十六进制系统使用16个字符,包括10个数字字符和A到F的6个字母。数字采用十六进制表示的话,就会比用十进制表示来得短。Base-64编码采用26个小写字母、26个大写字母、10个数字字符,以及两个额外的字符,比如“+”“/”,对数据进行编码,以实现在基于文本的媒介——比如电子邮件上传输二进制数据。Base-64主要用于电子邮件中二进制附件的编码。Base58也是一个基于文本的二进制编码格式,用于比特币及很多其他密码货币系统中。它在紧凑性、可读性、错误检测与预防方面提供了一种平衡。Base58是Base64的一个子集,使用大小写字母和数字,但是省略了一些容易混淆的字符。具体来说,Base58是Base64编码中去掉0(数字零)、O(字母o的大写)、l(小写的L)、I(大写的i)、字符“+”和字符“/”。或者更简单地说,它是去掉四个字符(0,O,l,I)之后的大小写字母与数字的集合。
为了增加额外安全性,防止打字和转录出现错误,常用于比特币的Base58Check编码格式在Base58的基础上增加了内置的错误校验码。校验码为4个字节长,添加到需要编码的数据后面。校验码从待编码数据的哈希值得出,从而可以检测和避免转录和输入错误。取得一个Base58Check编码的数据后,解码软件可以计算原始数据的校验码,并与数据中的校验码进行比对。如果两者不一致,就说明原始数据有误,Base58Check数据无效。在比特币的实践中,这样可以避免钱包应用中将输错的比特币地址当作一个有效目标,防止资金丢失。
为了将数据(一个数字)转换为Base58Check编码格式,我们首先要添加一个前缀到数据前,称之为“版本字节”,这可以让我们更容易判断数据的类型。例如,比特币地址的前缀是0(十六进制表示为0x00),而私钥的前缀为128(十六进制表示为0x80)。常用版本前缀请参看表4.1。
接下来,我们计算“双重SHA”校验码,即对前面得到的数据(前缀+数据)进行两次SHA256哈希计算:
从结果的32字节哈希值(哈希的哈希),我们只取前面4个字节。这4个字节作为错误检查码,或者校验码附加到数据的最后。
这样,就形成了由3部分(前缀、原始数据、校验码)组成的数据。现在可以利用前面介绍的Base58字符表,对结果数据进行编码。图4.6描述了Base58Check的编码过程。
图4.6 Base58Check编码:一种基于Base58的、版本化的、可校验的比特币数据无歧义编码格式
在比特币中,大多数需要向用户展示的数据均以Base58Check格式进行编码,这使数据紧凑、易读、便于检查错误。Base58Check编码的版本前缀用于创建易于辨别的格式,这意味着,当以Base58编码时,使用Base58Check编码后的数据头部包含了特定的字符。这个字符使用户很容易地判断出数据类型,以及如何去使用它。比如,Base58Check编码的比特币地址以1开头,而Base58Check编码的WIF格式私钥以5开头。一些版本前缀的例子,及其编码后的Base58字符见表4.1。
表4.1 Base58Check版本前缀及编码后结果示例
我们来看一下完整的比特币地址生成过程,从私钥到公钥(椭圆曲线上的一个点),到双重哈希的地址,到最后Base58Check编码。例4-2中的C++代码逐步展示了从私钥一直到Base58Check编码的比特币地址的完整过程。代码使用了libbitcoin库中的一些函数(第3章“替代客户端、库、工具集”中介绍过)。
代码使用预定义好的私钥,使每次运行都可以得到相同的比特币地址,就像例4-3所展示的一样。
不管私钥还是公钥,都可以表示为一系列不同的格式。尽管它们看起来并不一样,这些表现形式均是对同样数字的编码。这些格式的主要作用在于方便用户阅读及密钥转录,避免出现错误。
私钥可以表示为几种不同的格式,所有格式均代表与之对应的相同的256比特长的数字。表4.2显示了三种用于表示私钥的常用格式。
表4.2 私钥表示形式(编码格式)
表4.3通过这三种格式展示了同一个私钥。
表4.3 例子:相同密钥、不同格式
以上这些是用不同格式对相同密钥编码后的结果。虽然看起来不同,但是一种编码格式可以很容易地转换为另一种格式。
使用sx工具包(参看第3章“libbitcoin和sx工具集”),我们可以很容易地写出脚本或者命令行“管道”来操控比特币密钥、地址和交易。你可以使用sx工具包通过命令行来解码Base58Check格式。
我们使用base58check-decode命令解码未压缩密钥:
结果是一个十六进制格式的密钥,跟着一个钱包导入格式(WIF)的版本前缀128。
将密钥编码为Base58Check格式(与前述命令刚好相反),我们提供十六进制的私钥,跟着一个钱包导入格式(WIF)的版本前缀128。
为了将“压缩”格式的私钥(参见本章中“压缩私钥”)编码成Base58Check格式,我们在十六进制密钥的最后加上后缀01,然后进行编码:
生成的WIF-压缩格式结果,以字母“K”开头。这表示私钥带有“01”后缀,并且只可用于生成压缩格式的公钥(参见本章中“压缩公钥”)。
公钥也同样可以采用不同的格式表示,其中最重要的是压缩和非压缩公钥格式。
就像我们前面看到的,公钥是一个在椭圆曲线上的点,包含一对坐标(x,y)。它通常的表现形式为:04前缀开头,紧跟两个256位长度的数字,一个代表x坐标,另一个代表y坐标。04前缀用于区别非压缩公钥和压缩公钥,压缩公钥是以02或03开头的。
以下是之前我们通过私钥生成的公钥,以x,y坐标表示。
这里是以520比特(130个十六进制数字)的数字显示的相同密钥,04开头,紧跟着x坐标和y坐标,即04 x y。
比特币采用压缩公钥的目的是降低交易文件的大小,使存储比特币区块链数据库的节点尽可能地节省磁盘空间。大多数交易均包含公钥,用以验证所有者的身份并花费比特币。每个公钥的长度为520比特(前缀\\+x\\+y),而每个区块由几百个交易组成,每天都有成千上万个交易加入区块,这给区块链的存储带来了一定负担。
我们在本章“公钥”中看到,公钥其实是椭圆曲线上的一个点。因为曲线代表了一个数学方程,曲线上的一个点就代表了方程的一个解,那么,如果我们知道x坐标,y坐标就能通过求解方程y2mod p=(x3+7)mod p得到。因此我们也可以只存储公钥中的x坐标,而把y坐标省略掉,这样就将所需空间减少了256比特,几乎少了一半。长期来看,交易过程中节省的空间将是相当可观的。
未压缩公钥带04前缀,而压缩公钥始于02或者03。我们看看为什么会有两个不同的前缀:方程的左边是y2,这意味着y的解是个平方根,可以是正值也可以是负值。或者说,结果中y坐标可能位于x轴之上,也可能位于x轴之下。就如我们在图4.2看到的曲线图像,它是相对x轴对称的。所以,当我们省略y坐标时,我们必须保留y的符号(正还是负),换言之,我们必须记住这个点是在x轴之上还是x轴之下,上下两个点代表了两个不同的公钥。当我们在素数幂p的有限域内以二进制算术计算椭圆曲线的时候,y坐标可能是偶数也可能是奇数,分别对应y坐标的正负符号。这样,为了区分两个可能的y值,我们在压缩公钥中用前缀02代表偶数,03代表奇数,从而保证软件可以从x坐标准确推断出y坐标,并将公钥解压成完整坐标的点。公钥压缩过程参见图4.7。
图4.7 公钥压缩
这是前面生成的、用压缩格式存储的264比特(66个十六进制字符)公钥,以03为前缀,代表y坐标是奇数:
虽然它看起来与非压缩公钥并不一样,但对应的都是相同的私钥,也就是从相同的私钥生成的。更重要的是,如果我们将一个压缩公钥使用双重哈希函数(RIPEMD160(SHA256(K)))转换为一个比特币地址时,将生成一个不同的地址。这就很容易引起混淆,因为这意味着一个私钥可以生成两种不同表达方式的公钥(压缩、非压缩),进一步还将生成两个不同的比特币地址。但是对应这两个不同比特币地址的私钥却是相同的。
压缩公钥已逐渐成为比特币客户端的默认配置,这对降低交易文件大小,进而降低区块链大小有一定的积极作用。但是,并不是所有的客户端都支持压缩公钥格式。支持压缩公钥的新版客户端必须能兼容不支持压缩公钥的老版客户端发来的交易。这对于从其他钱包应用中导入私钥尤为重要,因为新钱包需要扫描区块链以查找与这些私钥相关的所有交易。比特币钱包到底该扫描哪种类型的地址呢?非压缩公钥生成的地址还是压缩公钥生成的地址?这两种地址都是有效的比特币地址,它们都可以被私钥签名,但它们的确是两个不同的地址!
为了解决这个麻烦,当私钥从钱包中导出时,用于表示私钥的钱包导入格式(WIF)在新比特币钱包中采用了不同的实现方式,它可以指示私钥已经被用于创建压缩公钥,并且也生成了压缩比特币地址。由此导入钱包可以辨别私钥是从旧的钱包来的,还是从新的钱包来的,从而根据非压缩还是压缩的比特币地址从区块链上搜索交易。我们将在下一节来看这个过程的细节。
有意思的是,名词“压缩私钥”有点误导的意味,实际上私钥以WIF-压缩格式导出时比那些“未压缩”的私钥还长了1个字节。因为它加了一个01的后缀,这个后缀表明它是从一个新钱包来的,只能用于生成压缩公钥。私钥既没有被压缩,也不可能被压缩。“压缩私钥”的真正含义是“只能用于生成压缩公钥的私钥”;同样,“非压缩私钥”就是“只能生成非压缩公钥的私钥”。为避免带来更多混淆,你最好将导出格式称为“WIF-压缩格式”或者“WIF”,而不是将私钥称为“压缩的”或者“非压缩的”。
记住,这些格式不是可交换的。在实现了压缩公钥的新钱包中,私钥只能被转换为WIF-压缩格式公钥(带K或L前缀)。如果钱包是一个较老版本的实现,还不能支持压缩公钥,那么,私钥只能导出为WIF格式(带5前缀)。这样做的目的是通知导入这些私钥的钱包,究竟是以压缩公钥以及它们相应的比特币地址进行搜索,还是以非压缩公钥及其地址进行搜索。
如果比特币钱包支持压缩公钥,它将在所有交易中使用压缩公钥。钱包中的私钥用于在曲线中生成公钥点,这个公钥点将进行压缩。压缩后的公钥用于生成比特币地址,并应用在交易当中。从一个支持压缩公钥的钱包导出私钥时,钱包导入格式将被修改,添加一个01后缀到私钥中。经过Base58Check编码后的私钥被称为“压缩WIF”,始于字母K或L,而不是像使用WIF编码(未压缩)的老钱包应用一样始于数字“5”。同一个密钥,以WIF和WIF-压缩格式两种不同格式编码如表4.4所示。
表4.4 同一个密钥,以WIF和WIF-压缩格式两种不同格式编码
“压缩私钥”是一个误会!它们并没有被压缩;相反地,WIF-压缩格式只是表明它们只能用于生成压缩公钥以及相应的比特币地址。有意思的是,一个“WIF-压缩格式”编码的私钥比“不压缩”的格式还长一个字节,因为它被加上了一个“01”的后缀,用于与“非加密”进行区分。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。