以太坊

在以太坊中,账户、地址、私钥(Private Key)和公钥(Public Key)是非常重要的概念。账户扮演着以太坊的中心角色,地址是我们与以太坊系统进行交互的标识,它是以太坊账户与外界进行交互的名字,而私钥与公钥是保护我们账户安全的重要屏障。

什么是账户(Accounts)?

账户在以太坊中扮演者十分重要的角色,它是以太坊的中心概念。在以太坊中,有两种类型的账户^1:一种是外部账户(EOAs,Externally Owned Accounts),另一种是合约账户(Contracts Accounts)。当我们提到账户这个术语的时候,我们通常指的是外部账户(EOA),当提到合约账户的时候我们通常称其为“合约”。

不论是外部账户还是合约账户,它们在以太坊中所维护的都是一系列叫做状态对象(state objects)的实体。这些实体中都拥有状态信息:外部账户存储的是账户的余额(balance),合约账户存储的是余额和合约中的内容。它们存储的这些状态会通过以太坊网络进行更新以及保证数据的一致性。账户是用户在以太坊区块链上创建交易必不可少的一部分。

账户标识了以太坊网络中每一个参与者的身份,每一笔交易都需要通过账户使用公钥加密进行签名才能够正常进行,这样的话,EVM(以太坊虚拟机)才能够对交易发送者进行验证来确保交易的真实可靠。

什么是以太坊地址(Address)?

一个以太坊地址就代表着一个以太坊账户,地址是账户的标识。对于外部账户来说,地址表示的是该账户公钥的后20字节(通常会以0x开头,例如,0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826,该地址使用的是16进制表示法^2)。上述示例中的地址中的字母全部是小写。在EIP55^3中引入了一种大小写混用的地址表示方法,通过这种表示方法进行表示的地址隐含了一个校验和(checksum)能够验证该地址的有效性。

什么是私钥和公钥?

每个账户都由一对钥匙定义,一个私钥(Private Key)和一个公钥(Public Key)。 账户以地址为索引,地址由公钥衍生而来,取公钥的最后20个字节。每对私钥/地址都编码在一个钥匙文件里。该文件是JSON格式的,下面我们将会查看一个私钥文件示例(Keystore)。

以太坊的私钥是一串64位16进制字符(32字节)。它是账户安全最重要的部分,需要妥善保管,如果丢失了私钥也就意味着你的账户丢失了。

查看一个Keystore文件

Keystore文件通常保存在以太坊数据目录的keystore文件夹下,它是JSON格式的:

通过keystore文件中的内容,我们可以看到其中包括了私钥加密的相关信息:

  • address:该账户的地址

  • cipher:加密方法使用的是AES-128-CTR算法^4

  • ciphertext:加密后的密文

  • cipherparams:AES-128-CTR算法加密所需的相关参数

  • kdf:秘钥生成函数,用于使用密码对keystore文件进行加密

  • kdfparams:kdf算法所需的参数

  • mac:用于验证密码的编码

私钥、公钥和地址是如何生成的?

大体来说,地址的生成的流程是:私钥 -> 公钥 -> 地址。因此地址的生成需要三步:

  1. 生成一个随机的私钥(32字节)

  2. 通过私钥生成公钥(64字节)

  3. 通过公钥得到地址(20字节)

私钥的生成

私钥是一组64位的16进制字符,通过私钥我们能够访问一个账户。以太坊的私钥生成是通过secp256k1^5曲线生成的,secp256k1是一个椭圆曲线算法,比特币使用的也是相同的曲线算法。

通过OpenSSL^6我们可以生成一个椭圆曲线私钥:

公钥的生成

其实,通过OpenSSL我们可以同时得到私钥和公钥:

地址的生成

地址是通过对上述的公钥做Keccak-256哈希^7,然后取最后的40位16进制字符得到的。我们对上述的公钥做哈希后并取后40位的结果是:0x24602722816b6cad0e143ce9fabf31f6026ec622。得到的该结果就是一个有效的以太坊地址。

如何验证地址的有效性

Geth Web3进行验证:

通过以太坊客户端Geth的Web3接口可以对以太坊地址进行有效性验证:

> web3.utils.isAddress('0xc1912fee45d61c87cc5ea59dae31190fffff232d');
> true

通过第三方JS库进行验证:

wallet-address-validator^8是一个JavaScript库能够对多种加密货币的地址进行验证。

var WAValidator = require('wallet-address-validator');
var valid1 = WAValidator.validate('0x24602722816b6cad0e143ce9fabf31f6026ec622', 'ETH');
if(valid1)
console.log('This is a valid address');
else
console.log('Address INVALID');