Alert Source Discuss
🚧 Stagnant Standards Track: Interface

EIP-1186: 用于获取 Merkle 证明的 RPC 方法 - eth_getProof

Authors Simon Jentzsch <simon.jentzsch@slock.it>, Christoph Jentzsch <christoph.jentzsch@slock.it>
Created 2018-06-24
Discussion Link https://github.com/ethereum/EIPs/issues/1186
Requires EIP-1474

简单概要

以太坊的一个伟大特性是,你可以验证状态的所有数据。但是为了允许在客户端外部验证账户,我们需要一个额外的函数来提供所需的证明。这些证明对于保护 Layer2 技术非常重要。

摘要

以太坊使用 Merkle Tree 来存储账户及其存储的状态。这允许通过简单地创建 Merkle 证明来验证每个值。但是目前,标准的 RPC 接口并没有让你访问这些证明。这个 EIP 建议增加一个 RPC 方法,为账户和存储值创建 Merkle 证明。

结合 stateRoot(来自区块头),它可以离线验证任何账户或存储值。这使得特别是 IOT 设备甚至是无法运行轻客户端的移动应用程序,只需给定一个受信任的 blockhash,就可以验证来自不受信任来源的响应。

动机

为了创建 MerkleProof,需要访问完整的状态数据库。当前的 RPC 方法允许应用程序访问单个值(eth_getBalanceeth_getTransactionCounteth_getStorageAteth_getCode),但是无法通过标准 RPC 接口读取 MerkleProof 所需的数据。(有一些实现使用 leveldb 并通过文件系统访问数据,但这不能用于生产系统,因为它首先需要停止客户端 - 参见 https://github.com/zmitton/eth-proof)

今天,MerkleProof 已经在内部使用。例如,轻客户端协议 支持一个创建 MerkleProof 的函数,该函数用于验证所请求的账户或存储数据。

通过 RPC 接口提供这些已经存在的功能,也将使应用程序能够存储这些证明并将其发送到未直接连接到 p2p 网络的设备,并且仍然能够验证数据。这可以用于验证移动应用程序或 IOT 设备中的数据,这些设备目前仅使用远程客户端。

规范

作为 eth-Module 的一部分,应该定义一个名为 eth_getProof 的附加方法,如下所示:

eth_getProof

返回指定账户的账户和存储值,包括 Merkle 证明。

参数
  1. DATA,20 字节 - 账户的地址。
  2. ARRAY,32 字节 - 应该被证明和包含的存储键的数组。 参见 eth_getStorageAt
  3. QUANTITY|TAG - 整数块号,或字符串 "latest""earliest",参见 默认块参数
返回值

Object - 一个账户对象:

  • balanceQUANTITY - 账户的余额。参见 eth_getBalance
  • codeHashDATA,32 字节 - 账户代码的哈希值。对于没有代码的简单账户,它将返回 "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
  • nonceQUANTITY - 账户的 nonce。参见 eth_getTransactionCount
  • storageHashDATA,32 字节 - StorageRoot 的 SHA3。所有存储都将提供一个以这个 rootHash 开始的 MerkleProof。
  • accountProofARRAY - rlp 序列化的 MerkleTree-Nodes 数组,从 stateRoot-Node 开始,沿着 SHA3 (地址) 作为键的路径。
  • storageProofARRAY - 根据请求的存储条目数组。每个条目都是一个具有以下属性的对象:

    • keyQUANTITY - 请求的存储键
    • valueQUANTITY - 存储值
    • proofARRAY - rlp 序列化的 MerkleTree-Nodes 数组,从 storageHash-Node 开始,沿着 SHA3 (键) 作为路径。
例子
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "eth_getProof",
  "params": [
    "0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842",
    [  "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ],
    "latest"
  ]
}

结果将如下所示:

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "accountProof": [
      "0xf90211a...0701bc80",
      "0xf90211a...0d832380",
      "0xf90211a...5fb20c80",
      "0xf90211a...0675b80",
      "0xf90151a0...ca08080"
    ],
    "balance": "0x0",
    "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
    "nonce": "0x0",
    "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "storageProof": [
      {
        "key": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
        "proof": [
          "0xf90211a...0701bc80",
          "0xf90211a...0d832380"
        ],
        "value": "0x1"
      }
    ]
  }
}

理由

这个方法实际上返回 3 个不同的重要数据点:

  1. 黄皮书中指定的账户对象的 4 个字段 [nonce, balance, storageHash, codeHash ],允许存储账户对象的哈希值,以便跟踪更改。
  2. 从指定块的 stateRoot 开始的账户的 MerkleProof。
  3. 每个请求的存储条目的 MerkleProof,从账户的 storageHash 开始。

将这些组合在一个方法中,可以使客户端非常高效地工作,因为所需的数据已经从数据库中获取。

不存在的值的证明

如果地址或存储值不存在,则证明需要提供足够的数据来验证这个事实。这意味着客户端需要沿着从根节点的路径传递数据,直到最后一个匹配的节点。如果最后一个匹配的节点是一个分支,则节点中的证明值必须是空的。如果是叶子类型,则它必须指向不同的相对路径,以证明所请求的路径不存在。

可能需要讨论的变更:

  • 不是提供块号,也许 blockhash 会更好,因为它允许证明叔块状态。
  • 为了减少数据,账户对象可能只提供 accountProofstorageProof。字段 balancenoncestorageHashcodeHash 可以通过反序列化从证明中的最后一个节点获取。

向后兼容性

由于这只是添加了一个新方法,因此不存在向后兼容性问题。

测试用例

待办事项:测试仍然需要实现,但是创建证明的核心功能已经存在于客户端内部,并且经过了充分的测试。

版权

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Simon Jentzsch <simon.jentzsch@slock.it>, Christoph Jentzsch <christoph.jentzsch@slock.it>, "EIP-1186: 用于获取 Merkle 证明的 RPC 方法 - eth_getProof [DRAFT]," Ethereum Improvement Proposals, no. 1186, June 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1186.