主页 > imtoken华为 > 4.2 比特币地址

4.2 比特币地址

imtoken华为 2023-08-19 05:09:41

4.2 比特币地址

比特币地址是一串数字和字母,可以向任何向您转移比特币的人透露。 从公钥(也是一串数字和字母)生成的比特币地址以数字“1”开头。 以下是比特币地址的示例:

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

在交易中,比特币地址通常被用作资金的接收地址。 如果把比特币交易比作一张支票,比特币地址就是受益人,也就是“给谁”一栏写的。 支票的收款人可以是银行账户、公司、机构,甚至是现金支票。 由于支票不需要指定具体账户,而是使用一个抽象名称作为收款人,这使其成为一种相当灵活的支付工具。 同样,比特币交易使用类似的抽象:比特币地址,这使得比特币交易变得灵活。 一个比特币地址代表一对公钥和私钥的拥有者,也可以代表其他的东西,比如后面“P2SH(Pay-to-Script-Hash)”部分会讲到的支付脚本。 现在,让我们看一个简单的例子,其中比特币地址代表并从公钥生成。

比特币地址可以通过一种单向加密哈希算法从公钥中获得。 散列算法是一种单向函数,它接受任意长度的输入并产生指纹或散列。 加密哈希函数在比特币中被广泛使用:比特币地址、脚本地址和挖矿中的工作量证明算法。 用于从公钥生成比特币地址的算法是安全哈希算法 (SHA) 和 RACE Integrity 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-5从公钥生成比特币地址

图 4-5 从公钥生成比特币地址]

4.2.1 Base58和Base58Check编码

为了更简洁方便地表达一长串数字,使用更少的符号,许多计算机系统在表示大于十进制的数字时使用数字和字母的混合。 例如,传统的十进制记数系统使用十位数字0-9,而十六进制系统使用16,加上六个字母AF来表示多余的符号0-9。 对于相同的数字,其十六进制表示形式将比等效的十进制表示形式短。 更简洁地说,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-2 是完整的 Base58 字母表。

示例 4-2 比特币的 Base58 字母表

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

为了增加防止打印和转录错误的安全性,比特币通常使用 Base58Check,这是一种带有内置错误检查代码的 Base58 编码格式。 校验和是添加到被编码数据末尾的 4 个额外字节。 校验和源自编码数据的哈希值,因此可用于检测和避免转录和打字中的错误。 使用Base58check编码时,解码软件会计算数据的校验和,并与编码中包含的校验和进行比较。 如果两者不匹配,就会出错,Base58Check数据无效。 这可以防止不正确的比特币地址被钱包软件视为有效地址,从而导致资金损失。

为了将数据(数字)转换成Base58Check格式,首先我们需要给数据加上一个叫做“version byte”的前缀,用来标识编码数据的类型。 例如,比特币地址的前缀为 0(十六进制为 0x00),而编码私钥的前缀为 128(十六进制为 0x80)。 表 4-1 列出了一些常见的版本前缀。

接下来,我们计算一个“双哈希”校验和,这意味着对先前的结果(前缀和数据)运行 SHA256 哈希算法两次:

  1. checksum = SHA256(SHA256(prefix+data))

在生成的 32 字节散列(两次散列操作)中,我们只取前 4 个字节。 这 4 个字节用作检查错误的代码或校验和。 将校验和添加到末尾。

结果由三部分组成:前缀、数据和校验和。 此结果使用前面描述的 Base58 字母表进行编码。 下图描述了Base58Check编码的过程。

图4-6Base58Check编码的过程

图 4-6 Base58Check 编码:用于明确编码比特币数据的 Base58、版本控制和校验和格式

在比特币中,大部分需要展示给用户的数据都是使用Base58Check编码的,因为它紧凑、易读,并且具有错误检查功能。 Base58Check 编码中的版本前缀用于创建一种易于区分的格式,当 Base58 编码时,该格式在 base58check 编码有效负载的开头包含某些字符。 这些字符可以很容易地理解被编码的数据类型以及如何使用它。 例如,我们很容易看出Base58Check编码的比特币地址以1开头,Base58Check编码的私钥WIF以5开头。表4-1列出了一些版本前缀及其对应的Base58格式。

表 4-1 Base58Check 版本前缀及编码结果

TypeVersion prefix (hex)Base58 结果前缀

比特币地址

0x00

1个

支付脚本哈希地址

0x05

3个

比特币测试网地址

0x6F

中号或中号

私钥 WIF

0x80

5、K 或 L

BIP-38 加密私钥

0x0142

6P

BIP-32 扩展公钥

0x0488B21E

xpub

4.2.2 密钥格式

公钥和私钥都可以有多种格式。 虽然看起来可能不同,但编码的是相同的数字。 这些不同的编码格式主要是为了方便人类无误地阅读和转录。

4.2.2.1 私钥格式

私钥有多种格式,所有格式都对应同一个 256 位数字。 表 4-2 显示了三种常见的私钥格式。 不同的场景使用不同的格式。 十六进制和原始二进制格式由软件内部使用,很少向用户显示。 WIF 格式用于钱包之间密钥的输入和输出,也用于表示私钥的二维码(条形码)。

表 4-2 私钥格式(编码格式)

类型前缀说明

生的

没有任何

32字节

十六进制

没有任何

64位十六进制数字

世界互联网论坛

5个

Base58Check 编码:版本前缀为 128 位和 32 位校验和的 Base58

WIF压缩

大号或大号

如上,在编码前添加后缀 0x01

下面的表 4-3 显示了私钥的三种格式。

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

格式私钥

十六进制

1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

世界互联网论坛

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

WIF压缩

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

这些格式都代表相同的数字,相同的私钥。 尽管编码后的字符串看起来不同,但两种格式都可以轻松转换为另一种格式。 请注意,表 4-3 示例中未显示“原始二进制”,因为根据定义,此处显示的任何编码格式都不是原始二进制数据。

我们在 Bitcoin Explorer 中使用 wif-to-ec 命令(参见 [appdx_bx])来显示两个 WIF 密钥代表相同的私钥:

  1. $ bx wif-to-ec 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
  2. 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
  3. $ bx wif-to-ec KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
  4. 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

4.2.2.2 Base58Check解码

Bitcoin Explorer 命令(参见本书附录 [appdx_bx])可以轻松编写处理比特币密钥、地址和交易的 shell 脚本和命令行“管道”。 Bitcoin Explorer 命令行可以解码 Base58Check 格式。

我们使用 base58check-decode 命令解码未压缩的密钥:

  1. $ bx base58check-decode 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn
  2. wrapper
  3. {
  4. checksum 4286807748
  5. payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
  6. version 128
  7. }

结果包含密钥负载、WIF 版本前缀 128 和校验和。

注意压缩密钥的“payload”后缀为01,表示要压缩导出的公钥:

  1. $ bx base58check-decode KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ
  2. wrapper
  3. {
  4. checksum 2339607926
  5. payload 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01
  6. version 128
  7. }

4.2.2.3 十六进制转Base58Check编码

要转换为 Base58Check(与之前的命令相反),请使用 Bitcoin Explorer 的 base58check-encode 命令(参见本书附录 [appdx_bx]),需要十六进制私钥后跟 WIF 版本前缀 128:

  1. bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd --version 128
  2. 5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

4.2.2.4 将十六进制(压缩格式密钥)转换为Base58Check编码

将压缩格式的私钥(见“压缩格式私钥”一节)进行Base58Check编码,在16进制私钥末尾加上后缀01,方法同上:

  1. $ bx base58check-encode 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01 --version 128
  2. KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

生成的WIF压缩格式的私钥以字母“K”开头,表示编码后的私钥有后缀“01”,此私钥只能用于生成压缩格式的公钥(见“压缩公钥密钥”部分)。

4.2.2.5 公钥格式

公钥也可以用多种不同的格式表示,通常是未压缩或压缩的公钥形式。

我们从上一篇文章中知道,公钥是椭圆曲线上的一个点,由一对坐标(x,y)组成。 公钥通常表示为前缀 04 后跟两个 256 位数字。 其中一个 256 位数字是公钥的 x 坐标,另一个 256 位数字是 y 坐标。 前缀04为非压缩格式公钥,压缩格式公钥以02或03开头。

下面是上面私钥生成的公钥,其坐标x和y如下:

  1. x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
  2. y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

下面是用 520 位数字(130 位十六进制数字)表示的相同公钥。 这个 520 位数字以前缀 04 开头,后跟 x 和 y 坐标,格式为:04 xy:

  1. K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE 52DDFE2E505BDB

4.2.2.6 压缩格式公钥

比特币以压缩格式引入公钥以减少交易的大小,从而节省运行区块链数据库的节点上的磁盘空间。 大多数比特币交易都包含公钥,用于验证用户凭证和支付比特币。 每个公钥有 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的解来自a平方根,可以是正数也可以是负数。 更形象地说,y 坐标可以高于或低于 x 坐标。 从图4-2的椭圆曲线图中可以看出,曲线是对称的,就像x轴的镜像。 因此,如果我们省略 y 坐标,我们必须存储 y 的符号(正或负)。 换句话说,你需要知道它是在x轴上方还是下方,因为上方和下方代表椭圆曲线上的不同点,即不同的公钥。 当我们使用二进制算法计算 p 阶素数有限域上的椭圆曲线时,y 坐标可能是偶数或奇数,对应于前面描述的 y 值的正负号。 因此,为了区分y坐标的两种可能取值,在生成压缩格式公钥时,如果y为偶数,则使用02作为前缀; 如果 y 是奇数,则使用 03 作为前缀。 这样软件就可以根据x坐标正确推导出对应的y坐标,从而将公钥解压成椭圆曲线上点的完整坐标。 下图说明了公钥压缩:

图4-7公钥压缩

图 4-7 公钥压缩

下面是上一节生成的公钥,采用264位(66位十六进制数字)压缩格式公钥格式,其中前缀03表示y坐标为奇数:

  1. K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

这个压缩后的公钥对应同一个私钥,意味着它是由同一个私钥生成的。 但是压缩和未压缩的公钥看起来不同。 更重要的是,如果我们使用双重哈希函数(RIPEMD160(SHA256(K)))将压缩格式公钥转换为比特币地址,得到的地址将与未压缩格式公钥生成的地址不同。 这个结果可能会令人困惑,一个私钥生成两种不同格式的公钥——压缩格式和未压缩格式,而这两种格式的公钥生成两个不同的比特币地址。 然而,这两个不同比特币地址的私钥是相同的。

公钥的压缩格式逐渐成为各种比特币客户端的默认格式,可以大大减少交易所需的字节数,也可以减少存储区块链所需的磁盘空间。 然而,并非所有客户端都支持压缩公钥,因此支持压缩公钥的新客户端必须考虑如何处理来自不支持压缩公钥的旧客户端的事务。 当钱包应用程序导入另一个钱包应用程序的私钥时,这一点变得尤为重要,因为新钱包需要扫描区块链并找到与这些导入密钥相关的所有交易。 比特币钱包应该扫描哪个比特币地址? 是压缩公钥生成的比特币地址,还是未压缩公钥生成的地址? 两者都是有效的比特币地址,都可以用私钥签名,但它们是不同的比特币地址。

为了解决这个问题,当私钥从钱包中导出时,WIF意味着私钥在较新的比特币钱包中被区别对待,表明私钥已被用于生成压缩的公钥和压缩的比特币地址。 这允许导入钱包区分私钥是来自旧钱包还是新钱包,并分别使用与压缩或未压缩公钥对应的比特币地址在区块链中搜索相应交易。 我们将在下一节详细解释这种机制是如何工作的。

4.2.2.7 压缩格式的私钥

其实“压缩格式私钥”是一个误导性的名字,因为当私钥以WIF压缩格式导出时,不仅没有压缩,反而比“未压缩格式”私钥长了一个字节。 多出的字节是私钥后缀为01,表示私钥来自较新的钱包,只能用来生成压缩公钥。 私钥没有被压缩,也不能被压缩。 “压缩私钥”实际上是指“只能生成压缩公钥的私钥”,而“未压缩私钥”则用来表示“只能生成非压缩公钥的私钥”。 为了避免更多的误解,应该只说导出格式是“WIF压缩格式”或“WIF”,而不是说私钥是“压缩”的。

表 4-4 显示了相同私钥的 WIF 和 WIF 压缩格式编码。表 4 示例:相同的密钥,不同的格式

格式私钥

十六进制

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD

世界互联网论坛

5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

十六进制压缩

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD01

WIF压缩

xFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

请注意,十六进制压缩的私钥在末尾有一个额外的字节(十六进制的 01)。 虽然WIF和WIF压缩格式的Base58编码版本前缀是一样的(0x80),但是在数字末尾加一个字节会导致Base58编码的第一个字符从5变成K或者L,也就是有点类似于十进制数 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”。 这个后缀是用来区分“未压缩格式”和“压缩格式”的。

4.3 在C++中实现键和地址

我们回顾一下比特币地址生成的完整过程,从私钥,到公钥(椭圆曲线上的一个点),到双哈希地址,到最后的Base58Check编码。 示例 4-3 中的 C++ 代码显示了从私钥到 Base58Check 编码的比特币地址的完整详细步骤。 该代码使用了“3.3 其他客户端、库和工具包”部分介绍的 libbitcoin 库中的辅助函数。

示例 4-3。 从私钥创建 Base58Check 编码的比特币地址

链接:代码/addr.cpp[]

上面的代码每次使用预定义的私钥运行时都会生成相同的比特币地址,如下例所示

例 4-4 编译运行 addr 代码

  1. Compile the addr.cpp code
  2. $ g++ -o addr addr.cpp $(pkg-config --cflags --libs libbitcoin)
  3. Run the addr executable
  4. $ ./addr
  5. Public key: 0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa
  6. Address: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CK

提示 示例 4-4 中的代码从压缩公钥(参见上面的“压缩公钥”部分)生成比特币地址 (1PRTT...)。 如果使用未压缩的公钥,将生成另一个地址(14K1y ...)。