密钥、地址

以太坊的基础技术之一是 密码学 cryptography,它是数学的一个分支,广泛用于计算机安全。密码学在希腊文中的意思是“秘密写作”,但密码学的科学不仅仅包含秘密协作,它被称为加密。加密也可以用来证明秘密的知识而不泄露该秘密(数字签名),或者证明数据的真实性(数字指纹)。这些类型的密码学证明是以太坊和大多数区块链系统的关键数学工具,广泛用于以太坊应用。讽刺的是,加密并不是以太坊的重要组成部分,因为它的通信和交易数据没有加密,也不需要加密以保护系统。在本章中,我们将以密钥和地址的形式介绍一些以太坊用来控制资金所有权的密码学。

简介

以太坊有两种不同类型的账户,可以拥有和控制ether:外部所有账户 (EOA)和 合同 。在本节中,我们将研究使用密码学来确定外部所有账户(即私人密钥)对ether的所有权。

EOAs中以太的所有权通过 数字密钥 digital keys以太坊地址和数字签名建立 。数字密钥实际上并不存储在区块链中或在以太坊网络上传输,而是由用户创建并存储在文件或称为 钱包 的简单数据库中。用户钱包中的数字密钥完全独立于以太坊协议,可以由用户的钱包软件生成和管理,无需参考区块链或访问互联网。数字密钥可实现以太坊的许多有趣特性,包括去中心化的信任和控制以及所有权证明。

以太坊交易需要将有效的数字签名包含在区块链中,该签名只能使用密钥生成;因此,任何拥有该密钥副本的人都可以控制ether。以太坊交易中的数字签名证明了资金的真正所有者。

数字密钥成对组成,密钥和公钥。将公钥视为类似于银行帐号,私钥类似于私密PIN,用于控制帐户。以太坊的用户很少看到这些数字密钥。在大多数情况下,它们存储在钱包文件内并由以太坊钱包软件管理。

以太坊交易的付款部分中,预期收款人由 以太坊地址 表示,该地址与支票上的收款人名称相同(即“付款给谁”)。在大多数情况下,以太坊地址是从公钥生成并对应的。但是,并非所有以太坊地址都代表公钥。他们也可以代表合同,我们将在 [contracts] 中看到。以太坊地址是用户常会看到的唯一密钥表示,因为这是他们需要与世界分享的部分。

首先,我们将介绍密码学并解释以太坊使用的数学。接下来,我们将看看密钥是如何生成,存储和管理的。最后,我们将回顾用于表示私钥和公钥以及地址的各种编码格式。

公钥密码技术和加密货币

公钥密码技术是现代信息安全的核心概念。首先由Martin Hellman,Whitfield Diffie和Ralph Merkle在20世纪70年代公开发明的,这是一个巨大的突破,它激起了公众对密码学领域的广泛兴趣。在70年代以前,强大的密码学知识在政府的控制下,很少有公开的研究,直到公钥密码技术研究的公开发表。

公钥密码系统使用唯一的密钥来保护信息。这些独特的密钥基于具有独特属性的数学函数:它们很容易在一个方向上计算,但很难在相反方向上计算。基于这些数学函数,密码学能够创建数字密钥和不可伪造的数字签名,这些签名由数学定律保证。

例如,计算两个大素数的乘积是微不足道的。但是给定两个大素数的乘积,很难找到这两个素数(称为 素因式分解 问题)。假设我提供数字6895601并告诉你它是两个素数的乘积。找到这两个素数要比让它们相乘生产6895601要困难得多。

如果你知道一些秘密信息,这些数学函数可以很容易地被反转。在我们上面的例子中,如果我告诉你一个主素数是1931,你可以简单地用一个简单的除法找到另一个:6895601/1931 = 3571。这样的函数被称为 trapdoor函数 因为给定一个秘密信息,你可以采取一个快捷方式,使得反转该函数很简单。

在密码学中有用的另一类数学函数基于椭圆曲线上的算术运算。在椭圆曲线算术中,乘以模数是简单的,但是除法是不可能的(一个被称为 离散对数 的问题)。椭圆曲线密码术在现代计算机系统中被广泛使用,并且是以太坊(和其他加密货币)数字密钥和数字签名的基础。

在以太坊,我们使用公钥加密技术来创建一个密钥对,以控制对ether的访问,并允许我们对合同进行身份验证。密钥对由私钥和唯一公钥组成,并且被认为是“一对儿”,因为公钥是从私钥中派生出来的。公钥用于接收资金,私钥用于创建数字签名来签署交易以支付资金。数字签名也可用于验证合同的所有者或用户,我们将在 > 中看到。

公钥和私钥之间存在数学关系,允许私钥用于在消息上生成签名。该签名可以在不公开私钥的情况下使用公钥进行验证。

当使用ether时,当前所有者在交易中呈现她的公钥和签名(每次不同,但是使用相同的私钥创建)。通过公钥和签名,以太坊系统中的每个人都可以独立验证并接受交易的有效性,从而确认在转移ether的人拥有他们。

在大多数钱包实现中,为了方便起见,私钥和公钥一起存储为 key pair 。但是,公钥可以由私钥进行简单计算,因此只存储私钥也是可以的。

为什么使用不对称加密(公钥/私钥)?

为什么在以太坊使用非对称密码术?它不习惯“加密”(保密)交易。相反,非对称密码术的有用特性是产生数字签名的能力。私钥可应用产生交易的数字签名。这个签名只能由知道私钥的人制作。但是,任何有权访问公钥和交易签名的人都可以使用它们来验证。非对称加密技术的这一有用特性使任何人都可以验证每笔交易的每个签名,

私钥

私钥只是一个随机选取的数字。私有密钥的所有权和控制权是用户控制与相应以太坊地址相关联的所有资金的基础,也是对该地址的合同的访问权授权。通过证明交易中使用的资金的所有权,私钥用于创建花费ether所需的签名。私钥在任何时候都必须保密,因为向第三方透露密钥相当于让他们控制以太和由该密钥保证的合同。私钥还必须备份并防止意外丢失。如果它丢失了,无法恢复,它保护的资金也将永远丢失。

以太坊私钥只是一个数字。你可以使用硬币,铅笔和纸随机挑选你的私钥:投掷硬币256次,得到可以在以太坊钱包中使用的随机二进制数字作为私钥。然后可以从私钥生成公钥和地址。

从随机数生成私钥

生成密钥的第一步也是最重要的一步是找到一个安全的熵源或随机源。创建以太坊私钥基本上与“选择1到2^256^ 之间的数字”相同。只要不可预测和不可重复,用于选择该数字的确切方法并不重要。以太坊软件使用底层操作系统的随机数生成器生成256位熵(随机性)。通常,操作系统随机数生成器是由一个人为的随机源进行初始化的,这就是为什么可能会要求你将鼠标左右摇摆几秒钟,或者按下键盘上的随机键。

更确切地说,可能的私钥范围略小于2^256^ 。在以太坊中,私钥可以是+1+和+n-1+之间的任何数字,其中n是定义为使用的椭圆曲线的阶数的常数(n = 1.158*10^77^ ,略小于2^256^ )(参见椭圆曲线密码学解释)。为了创建这样的密钥,我们随机选择一个256位数字并检查它是否小于+n-1+。在编程方面,这通常是通过将从密码学安全的随机源收集的更大的随机比特串提供给256位哈希算法(如Keccak-256或SHA256)(参见 [cryptographic_hash_algorithm]),产生一个256位数字。如果结果小于+n-1+,我们有一个合适的私钥。否则,我们只需再次尝试使用另一个随机数。

不要编写自己的代码来创建随机数或使用你的编程语言提供的“简单”随机数发生器。使用密码学安全的伪随机数字发生器(CSPRNG)和来自足够熵源的种子。研究你选择的随机数生成器库的文档,以确保其是密码学安全的。正确实施CSPRNG对于密钥的安全至关重要。

以下是以十六进制格式显示的随机生成的私钥(k)(256位,显示为64个十六进制数字,每个4位):f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315

以太坊的私人密钥空间的大小(\( 2^{256} \) )是一个难以置信的大数目。十进制大约是 \( 10^{77} \) 。可见宇宙估计含有 \( 10^{80} \) 原子。

公钥

以太坊公钥是一个椭圆曲线上的 point ,意思是它是一组满足椭圆曲线方程的X和Y坐标。

简单来说,以太坊公钥是两个数字,并联在一起。这些数字是通过一次单向的计算从私钥生成的。这意味着,如果你拥有私钥,则计算公钥是微不足道的。但是你不能从公钥中计算私钥。

MATH即将发生!不要惊慌。如果你发现难以阅读前一段,则可以跳过接下来的几节。有很多工具和库会为你做数学。

公钥使用椭圆曲线乘法和私钥计算,这是不可逆的:K = k * G ,其中 k 是私钥,G 是一个称为 generator point 的常数点, K 是结果公钥。如果你知道 K ,那么称为“寻找离散对数”的逆运算就像尝试所有可能的 k 值一样困难,也就是蛮力搜索。

简单地说:椭圆曲线上的算术不同于“常规”整数算术。点(G)可以乘以整数(k)以产生另一点(K)。但是没有 除法 这样的东西,所以不可能简单地用公共密钥K除以点G来计算私钥k。这是公钥密码技术和加密货币中描述的单向数学函数。

椭圆曲线乘法是密码学家称之为“单向”函数的一种函数:在一个方向(乘法)很容易完成,而在相反方向(除法)不可能完成。私钥的所有者可以很容易地创建公钥,然后与世界共享,因为知道没有人能够反转该函数并从公钥计算私钥。这种数学技巧成为证明以太坊资金所有权和合同控制权的不可伪造和安全数字签名的基础。

在我们演示如何从私钥生成公钥之前,我们先来看一下椭圆曲线加密。

椭圆曲线密码学解释

椭圆曲线密码术是一种基于离散对数问题的非对称或公钥密码体系,如椭圆曲线上的加法和乘法运算。

A visualization of an elliptic curve 是椭圆曲线的一个例子,类似于以太坊使用的曲线。

以太坊使用与比特币完全相同的椭圆曲线,称为 secp256k1 。这使得重新使用比特币的许多椭圆曲线库和工具成为可能。

Figure 1. A visualization of an elliptic curveFigure 1. A visualization of an elliptic curve

以太坊使用特定的椭圆曲线和一组数学常数,由国家标准与技术研究院(NIST)制定的名为 secp256k1 的标准中所定义的。secp256k1 曲线由以下函数定义,该函数产生一个椭圆曲线:\( y^2 = (x^3 + 7) over(F_p) \) 或 \( y^2 mod p = (x^3 + 7) mod p \)

mod p (模素数p) 表示该曲线在素数阶 p 的有限域上,也写作\( (( {F}_p )) \), 其中 \( p = 2^{256} – 2^{32} – 2^9 – 2^8 – 2^7 – 2^6 – 2^4 – 1 \), 一个非常大的素数。

因为这条曲线是在有限的素数阶上而不是在实数上定义的,所以它看起来像是一个散布在二维中的点的模式,使得难以可视化。然而,数学与实数上的椭圆曲线的数学是相同的。作为一个例子,Elliptic curve cryptography: visualizing an elliptic curve over F(p), with p=17 在一个更小的素数阶17的有限域上显示了相同的椭圆曲线,显示了一个网格上的点的图案。secp256k1 以太坊椭圆曲线可以被认为是一个更复杂的模式,在一个不可思议的大网格上的点。

Figure 2. Elliptic curve cryptography: visualizing an elliptic curve over F(p), with p=17Figure 2. Elliptic curve cryptography: visualizing an elliptic curve over F(p), with p=17

例如,以下是坐标为(x,y)的点Q,它是 secp256k1 曲线上的一个点:

Q = (49790390825249384486033144355916864607616083520101638681403973749255924539515, 59574132161899900045862086493921015780032175291755807399284007721050341297360)

Using Python to confirm that this point is on the elliptic curve 显示了如何使用Python检查它。变量x和y是上述点Q的坐标。变量p是椭圆曲线的主要阶数(用于所有模运算的素数)。Python的最后一行是椭圆曲线方程(Python中的%运算符是模运算符)。如果x和y确实是椭圆曲线上的点,那么它们满足方程,结果为零(0L+是零值的长整数)。通过在命令行上键入+python 并复制下面的每行(不包括提示符 >>>),亲自尝试一下:Example 1. Using Python to confirm that this point is on the elliptic curve

Python 3.4.0 (default, Mar 30 2014, 19:23:13)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
>>> x = 49790390825249384486033144355916864607616083520101638681403973749255924539515
>>> y = 59574132161899900045862086493921015780032175291755807399284007721050341297360
>>> (x ** 3 + 7 - y**2) % p
0L

椭圆曲线算术运算

很多椭圆曲线数学看起来很像我们在学校学到的整数算术。具体而言,我们可以定义一个加法运算符,而不是添加数字就是在曲线上添加点。一旦我们有了加法运算符,我们也可以定义一个点和一个整数的乘法,等于重复加法。

A lot of elliptic curve math looks and works very much like the integer arithmetic we learned at school. Specifically, we can define an addition operator, which instead of adding numbers is adding points on the curve. Once we have the addition operator, we can also define multiplication of a point and a whole number, such that it is equivalent to repeated addition.

加法定义为给定椭圆曲线上的两个点 \( P_1 and P_2 \) , 第三个点 \( P_3 = P_1 + P_2 \) , 也在椭圆曲线上。

在几何上,这个第三点 \( P_3 \) 是通过在 \( P_1 \) 和 \( P_2 \) 之间画一条直线来计算的。这条线将在另外一个地方与椭圆曲线相交。称此点为 \( P_3 = (x, y) \)。然后在x轴上反射得到 \( P_3 = (x, –y) \)。

在椭圆曲线数学中,有一个叫做“无穷点”的点,它大致对应于零点的作用。在计算机上,它有时用 x = y = 0表示(它不满足椭圆曲线方程,但它是一个容易区分的情况,可以检查)。有几个特殊情况解释了“无穷点”的需要。

如果 \( P_1 \) 和 \( P_2 \) 是同一点,\( P_1 \) and \( P_2 \) 之间的直线应该延伸到曲线上 \( P_1 \) 的切线。 该切线恰好与曲线在一个新点相交。你可以使用微积分技术来确定切线的斜率。我们将我们的兴趣局限在具有两个整数坐标的曲线上,这些技巧令人好奇地工作!

在某些情况下(即,如果 \(P_1 \) 和 \( P_2 \) 具有相同的x值但不同的y值),切线将精确地垂直,在这种情况下P3 =“无穷点”。

如果 \( P_1 \) 是“无穷点”,那么 \(P_1 + P_2 = P_2 \) 。 类似地, 如果 \( P_2 \) 是“无穷点”,\( P_1 + P_2 = P_1 \) 。这显示了无穷点如何扮演零在“正常”算术中扮演的角色。

  • 是可结合的, (A + B) + C = A + (B + C). 这表示 A + B + C 不加括号也没有歧义。

现在我们已经定义了加法,我们可以用扩展加法的标准方式来定义乘法。对于椭圆曲线上的点P,如果k是整数,则 k * P = P + P + P + … + P (k 次)。请注意,在这种情况下,k有时会被混淆地称为“指数”。

生成一个公钥

以一个随机生成的数字 k 的私钥开始,我们通过将它乘以称为 generator point G 的曲线上的预定点,在曲线上的其他位置产生另一个点,这是相应的公钥 K 。生成点被指定为+secp256k1+标准的一部分,对于+secp256k1+的所有实现始终相同,并且从该曲线派生的所有密钥都使用相同的点 G :\( K = k * G \)

其中 k 是私钥,G 是生成点, K是生成的公钥,即曲线上的一个点。因为所有以太坊用户的生成点始终相同,所以 G 乘以 G 的私钥总是会导致相同的公钥 K 。k 和 K 之间的关系是固定的,但只能从 kK 的一个方向进行计算。这就是为什么以太坊地址(来自 K )可以与任何人共享,并且不会泄露用户的私钥(k)。

正如我们在 椭圆曲线算术运算中所描述的那样,k * G的乘法相当于重复加,G + G + G + … + G ,重复k次。总而言之,为了从私钥 k 生成公钥 K ,我们将生成点 G 添加到自己 k 次。

私钥可以转换为公钥,但公钥不能转换回私钥,因为数学只能单向工作。

让我们应用这个计算来找到我们在 私钥 中给出的特定私钥的公钥:Example private key to public key calculation K = f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315 * G

密码库可以帮助我们使用椭圆曲线乘法计算K值。得到的公钥 K 被定义为一个点 K = (x,y) :Example public key calculated from the example private key

K = (x, y)
where,
x = 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b
y = 83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0

在以太坊中,你可以看到公钥以66个十六进制字符(33字节)的十六进制序列表示。这是从行业联盟标准高效密码组(SECG)提出的标准序列化格式采用的,在[http://www.secg.org/sec1-v2.pdfStandards for Efficient Cryptography(SEC1)]中有记载。该标准定义了四个可用于识别椭圆曲线上点的可能前缀:

Prefix Meaning Length (bytes counting prefix)
0x00 Point at Infinity 1
0x04 Uncompressed Point 65
0x02 Compressed Point with even Y 33
0x03 Compressed Point with odd Y 33

以太坊只使用未压缩的公钥,因此唯一相关的前缀是(十六进制)04。顺序连接公钥的X和Y坐标:04 + X-coordinate (32 bytes/64 hex) + Y coordinate (32 bytes/64 hex)

因此,我们在 Example public key calculated from the example private key 中计算的公钥被序列化为:046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0

椭圆曲线库

加密货币相关项目中使用了secp256k1椭圆曲线的几个实现:

  • OpenSSLOpenSSL库提供了一套全面的加密原语,包括secp256k1的完整实现。例如,要派生公钥,可以使用函数+EC_POINT_mul()+。https://www.openssl.org/
  • libsecp256k1Bitcoin Core的libsecp256k1是secp256k1椭圆曲线和其他密码原语的C语言实现。椭圆曲线密码学的libsecp256是从头开始编写的,代替了Bitcoin Core软件中的OpenSSL,在性能和安全性方面被认为是优越的。https://github.com/bitcoin-core/secp256k1

加密哈希函数

加密哈希函数在整个以太坊使用。事实上,哈希函数几乎在所有密码系统中都有广泛应用,这是密码学家布鲁斯•施奈尔(Bruce Schneier)所说的一个事实,他说:“单向哈希函数远不止于加密算法,而是现代密码学的主要工具。

在本节中,我们将讨论哈希函数,了解它们的基本属性以及这些属性如何使它们在现代密码学的很多领域如此有用。我们在这里讨论哈希函数,因为它们是将以太坊公钥转换成地址的一部分。

简而言之,“哈希函数是可用于将任意大小的数据映射到固定大小的数据的函数。” Source:Wikipedia。哈希函数的输入称为 原象 pre-image消息 message 。输出被称为 哈希 hash摘要 digest 。哈希函数的一个特殊子类别是 加密哈希函数 ,它具有对密码学有用的特定属性。

加密哈希函数是一种 单向 哈希函数,它将任意大小的数据映射到固定大小的位串,如果知道输出,计算上不可能重新创建输入。确定输入的唯一方法是对所有可能的输入进行蛮力搜索,检查匹配输出。

加密哈希函数有五个主要属性 (Source: Wikipedia/Cryptographic Hash Function):

  • 确定性: 任何输入消息总是产生相同的哈希摘要。
  • 可验证性: 计算消息的哈希是有效的(线性性能)。
  • 不相关: 对消息的小改动(例如,一位改变)会大幅改变哈希输出,以致它不能与原始消息的哈希相关联。
  • 不可逆性: 从哈希计算消息是不可行的,相当于通过可能的消息进行蛮力搜索。
  • 碰撞保护: 计算两个不同的消息产生相同的哈希输出应该是不可行的。

碰撞保护对于防止以太坊中的数字签名伪造至关重要。这些属性的组合使加密哈希函数可用于广泛的安全应用程序,包括:

  • 数据指纹识别
  • 消息完整性(错误检测)
  • 工作证明
  • 认证(密码哈希和密钥扩展)
  • 伪随机数发生器
  • 原象承诺
  • 唯一标识符

通过研究系统的各个层面,我们会在以太坊找到它的很多应用。

以太坊的加密哈希函数 - Keccak-256

以太坊在许多地方使用 Keccak-256 加密哈希函数。Keccak-256被设计为于2007年举行的SHA-3密码哈希函数竞赛的候选者。Keccak是获胜的算法,在2015年被标准化为 FIPS(联邦信息处理标准)202。

然而,在以太坊开发期间,NIST标准化工作正在完成。在标准过程完成后,NIST调整了Keccak的一些参数,据称可以提高效率。这与英雄告密者爱德华斯诺登透露的文件暗示NIST可能受到国家安全局的不当影响同时发生,故意削弱Dual_EC_DRBG随机数生成器标准,有效地在标准随机数生成器中放置一个后门。这场争论的结果是对所提议修改的反对以及SHA-3标准化的严重拖延。当时,以太坊基金会决定实施最初的Keccak算法。

虽然你可能在Ethereum文档和代码中看到“SHA3”,但很多(如果不是全部)这些实例实际上是指Keccak-256,而不是最终确定的FIPS-202 SHA-3标准。实现差异很小,与填充参数有关,但它们的重要性在于Keccak-256在给定相同输入的情况下产生与FIPS-202 SHA-3不同的哈希输出。

由于Ethereum中使用的哈希函数(Keccak-256)与最终标准(FIP-202 SHA-3)之间的差异造成了混淆,因此正在努力将代码中所有的 sha3 的所有实例,操作码和库重新命名为 keccak256。详情请参阅https://github.com/ethereum/EIPs/issues/59[ERC-59]。

我正在使用哪个哈希函数?

如何判断你使用的软件库是FIPS-202 SHA-3还是Keccak-256(如果两者都可能被称为“SHA3”)?

一个简单的方法是使用 test vector ,一个给定输入的预期输出。最常用于哈希函数的测试是 empty input 。如果你使用空字符串作为输入运行哈希函数,你应该看到以下结果:

  • Testing whether the SHA3 library you are using is Keccak-256 of FIP-202 SHA-3
    Keccak256("") =
    c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
    SHA3("") =
    a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a
    

因此,无论调用什么函数,都可以通过运行上面的简单测试来测试它是否是原始的Keccak-256或最终的NIST标准FIPS-202 SHA-3。请记住,以太坊使用Keccak-256,尽管它在代码中通常被称为SHA-3。

接下来,让我们来看一下Ethereum中Keccak-256的第一个应用,即从公钥生成以太坊地址。

以太坊地址

以太坊地址是 唯一标识符 unique identifiers ,它们是使用单向哈希函数(Keccak-256)从公钥或合约派生的。

在我们之前的例子中,我们从一个私钥开始,并使用椭圆曲线乘法来派生一个公钥:

  • Private Key k
    k = f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315
    
  • Public Key K (X and Y coordinates concatenated and shown as hex):
    K = 6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0
    

值得注意的是,在计算地址时,公钥没有用前缀(十六进制)04格式化。

  • 我们使用Keccak-256来计算这个公钥的 hashKeccak256(K) = 2a5bc342ed616b5ba5732269001d3f1ef827552ae1114027bd3ecf1f086ba0f9
  • 然后我们只保留最后的20个字节(大端序中的最低有效字节),这是我们的以太坊地址:001d3f1ef827552ae1114027bd3ecf1f086ba0f9
  • 大多数情况下,你会看到带有前缀“0x”的以太坊地址,表明它是十六进制编码,如下所示:0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9

以太坊地址格式

以太坊地址是十六进制数字,从公钥的Keccak-256哈希的最后20个字节导出的标识符。

与在所有客户端的用户界面中编码的比特币地址不同,它们包含内置校验和来防止输入错误的地址,以太坊地址以原始十六进制形式呈现,没有任何校验和。

该决定背后的基本原理是,以太坊地址最终会隐藏在系统高层的抽象(如名称服务)之后,并且必要时应在较高层添加校验和。

回想起来,这种设计选择导致了一些问题,包括由于输入错误地址和输入验证错误而导致的资金损失。以太坊名称服务的开发速度低于最初的预期,诸如ICAP之类的替代编码被钱包开发商采用得非常缓慢。

互换客户端地址协议 Inter Exchange Client Address Protocol (ICAP)

互换客户端地址协议(ICAP) 是一种部分与国际银行帐号(IBAN)编码兼容的以太坊地址编码,为以太坊地址提供多功能,校验和互操作编码。ICAP地址可以编码以太坊地址或通过以太坊名称注册表注册的常用名称。

阅读以太坊Wiki上的ICAP:https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol

IBAN是识别银行账号的国际标准,主要用于电汇。它在欧洲单一欧元支付区(SEPA)及其以后被广泛采用。IBAN是一项集中和严格监管的服务。ICAP是以太坊地址的分散但兼容的实现。

一个IBAN由含国家代码,校验和和银行账户标识符(特定国家)的34个字母数字字符(不区分大小写)组成。

ICAP使用相同的结构,通过引入代表“Ethereum”的非标准国家代码“XE”,后面跟着两个字符的校验和以及3个可能的账户标识符变体:

Direct最多30个字母数字字符big-endian base-36整数,表示以太坊地址的最低有效位。由于此编码适合小于155位,因此它仅适用于以一个或多个零字节开头的以太坊地址。就字段长度和校验和而言,它的优点是它与IBAN兼容。示例:XE60HAMICDXSV5QXVJA7TJW47Q9CHWKJD(33个字符长)

Baasic与“Direct”编码相同,只是长度为31个字符。这使它可以编码任何以太坊地址,但使其与IBAN字段验证不兼容。示例:XE18CHDJBPLTBCJ03FE9O2NS0BPOJVQCU2P(35个字符长)

Indrect编码通过名称注册表提供程序解析为以太坊地址的标识符。使用由 asset identifier (例如ETH),名称服务(例如XREG)和9个字符的名称(例如KITTYCATS)组成的16个字母数字字符,这是一个人类可读的名称。示例:XE## ETHXREGKITTYCATS(20个字符长),其中“##”应由两个计算校验和字符替换。

我们可以使用 helpeth 命令行工具来创建ICAP地址。让我们尝试使用我们的示例私钥(前缀为0x并作为参数传递给helpeth):

$ helpeth keyDetails -p 0xf8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315
Address: 0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
ICAP: XE60 HAMI CDXS V5QX VJA7 TJW4 7Q9C HWKJ D
Public key: 0x6e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0

helpeth 命令为我们构建了一个十六进制以太坊地址以及一个ICAP地址。我们示例密钥的ICAP地址是:

XE60HAMICDXSV5QXVJA7TJW47Q9CHWKJD

由于我们的示例以太坊地址恰好以零字节开始,因此可以使用IBAN格式中有效的“Direct”ICAP编码方法进行编码。因为它是33个字符长。

如果我们的地址不是从零开始,那么它将被编码为“Basic”编码,这将是35个字符长并且作为IBAN格式无效。

以零字节开始的任何以太坊地址的概率是1/256。为了生成这样一个类型,在我们找到一个作为IBAN兼容的“Direct”编码之前,它将平均用256个不同的随机私钥进行256次尝试ICAP地址。

不幸的是,现在,只有几个钱包支持ICAP。

使用大写校验和的十六进制编码 (EIP-55)

由于ICAP或名称服务部署缓慢,因此提出了一个新的标准,以太坊改进建议55(EIP-55)。你可以阅读详细信息:https://github.com/Ethereum/EIPs/blob/master/EIPS/eip-55.md

通过修改十六进制地址的大小写,EIP-55为以太坊地址提供了向后兼容的校验和。这个想法是,以太坊地址不区分大小写,所有钱包都应该接受以大写字母或小写字母表示的以太坊地址,在解释上没有任何区别。

通过修改地址中字母字符的大小写,我们可以传达一个校验和,可以用来保护地址完整性,防止输入或读取错误。不支持EIP-55校验和的钱包简单地忽略地址包含混合大写的事实。但那些支持它的人可以验证它并以99.986%的准确度检测错误。

  • 混合大小写编码很微妙,最初你可能不会注意到它。我们的示例地址是:0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9
  • 使用 EIP-55 混合大小写校验和,它变为:0x001d3F1ef827552Ae1114027BD3ECF1f086bA0F9

你能看出区别吗?一些来自十六进制编码字母表的字母(AF)字符现在是大写字母,而另一些则是小写字母。除非你仔细观察,否则你甚至可能没有注意到其中的差异。

EIP-55实施起来相当简单。我们采用小写十六进制地址的Keccak-256哈希。这个哈希作为地址的数字指纹,给我们一个方便的校验和。输入(地址)中的任何小改动都会导致哈希结果(校验和)发生很大变化,从而使我们能够有效地检测错误。然后我们的地址的哈希被编码为地址本身的大写字母。让我们一步步分解它:

  1. 计算小写地址的哈希,不带 0x 前缀::
    Keccak256("001d3f1ef827552ae1114027bd3ecf1f086ba0f9")
    23a69c1653e4ebbb619b0b2cb8a9bad49892a8b9695d9a19d8f673ca991deae1
    
  2. 如果哈希的相应十六进制数字大于或等于 0x8,则将每个字母地址字符大写。如果我们排列地址和哈希,这将更容易显示:
    Address: 001d3f1ef827552ae1114027bd3ecf1f086ba0f9
    Hash   : 23a69c1653e4ebbb619b0b2cb8a9bad49892a8b9...
    

我们的地址在第四个位置包含一个字母 d。哈希的第四个字符是 6,小于+8+。所以,我们保持 d 小写。我们地址中的下一个字母字符是 f,位于第六位。十六进制哈希的第六个字符是 c,它大于+8 。因此,我们在地址中大写 +F,等等。正如你所看到的,我们只使用哈希的前20个字节(40个十六进制字符)作为校验和,因为我们只有20个字节(40个十六进制字符)能正确地大写。

  • 检查自己产生的混合大写地址,看看你是否可以知道在地址哈希中哪些字符被大写和它们对应的字符:
    Address: 001d3F1ef827552Ae1114027BD3ECF1f086bA0F9
    Hash   : 23a69c1653e4ebbb619b0b2cb8a9bad49892a8b9...
    

在EIP-55编码地址中检测错误

  • 现在,我们来看看EIP-55地址如何帮助我们发现错误。假设我们已经打印出ETHER-E编码的以太坊地址:0x001d3F1ef827552Ae1114027BD3ECF1f086bA0F9
  • 现在,让我们在阅读该地址时犯一个基本错误。最后一个字符之前的字符是大写字母“F”。对于这个例子,我们假设我们误解为大写“E”。我们在钱包中输入(不正确的地址):0x001d3F1ef827552Ae1114027BD3ECF1f086bA0E9
  • 幸运的是,我们的钱包符合EIP-55标准!它注意到混合大写字母并试图验证地址。它将其转换为小写,并计算校验和哈希值:Keccak256("001d3f1ef827552ae1114027bd3ecf1f086ba0e9") 5429b5d9460122fb4b11af9cb88b7bb76d8928862e0a57d46dd18dd8e08a6927

如你所见,即使地址只改变了一个字符(事实上,“e”和“f”只相隔1位),地址的哈希值已经根本改变了。这是哈希函数的特性,使它们对校验和非常有用!

  • 现在,让我们排列这两个并检查大小写:
    001d3F1ef827552Ae1114027BD3ECF1f086bA0E9
    5429b5d9460122fb4b11af9cb88b7bb76d892886...
    

这都是错的!几个字母字符不正确地大写。请记住,大写是 正确的 校验和的编码。

我们输入的地址的大小写与刚刚计算的校验和不匹配,这意味着地址中的内容发生了变化,并且引入了错误。

下一节:在以太坊中,“钱包”一词有几个不同的含义。

在较高层次上,钱包是作为主要用户界面的应用程序。钱包控制对用户资金的访问,管理密钥和地址,追踪余额以及创建和签署交易。另外,一些以太坊钱包还可以与合约(如代币)进行交互。

狭义上讲,从程序员的角度来看,“钱包”一词是指用于存储和管理用户密钥的系统。每个“钱包”都有一个密钥管理组件。对于一些钱包来说,这就是全部。其他一些钱包是更广泛类别的一部分,即“浏览器”,它是以太坊去中心化应用或“DApps”的接口。在“钱包”这个术语下混合的各种类别之间没有明确的区别。

在本节中,我们将把钱包看作私钥的容器,并将其视为用于管理密钥的系统。