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_getBalance
、eth_getTransactionCount
、eth_getStorageAt
、eth_getCode
),但是无法通过标准 RPC 接口读取 MerkleProof 所需的数据。(有一些实现使用 leveldb 并通过文件系统访问数据,但这不能用于生产系统,因为它首先需要停止客户端 - 参见 https://github.com/zmitton/eth-proof)
今天,MerkleProof 已经在内部使用。例如,轻客户端协议 支持一个创建 MerkleProof 的函数,该函数用于验证所请求的账户或存储数据。
通过 RPC 接口提供这些已经存在的功能,也将使应用程序能够存储这些证明并将其发送到未直接连接到 p2p 网络的设备,并且仍然能够验证数据。这可以用于验证移动应用程序或 IOT 设备中的数据,这些设备目前仅使用远程客户端。
规范
作为 eth-Module 的一部分,应该定义一个名为 eth_getProof
的附加方法,如下所示:
eth_getProof
返回指定账户的账户和存储值,包括 Merkle 证明。
参数
DATA
,20 字节 - 账户的地址。ARRAY
,32 字节 - 应该被证明和包含的存储键的数组。 参见eth_getStorageAt
QUANTITY|TAG
- 整数块号,或字符串"latest"
或"earliest"
,参见 默认块参数
返回值
Object
- 一个账户对象:
balance
:QUANTITY
- 账户的余额。参见eth_getBalance
codeHash
:DATA
,32 字节 - 账户代码的哈希值。对于没有代码的简单账户,它将返回"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
nonce
:QUANTITY
- 账户的 nonce。参见eth_getTransactionCount
storageHash
:DATA
,32 字节 - StorageRoot 的 SHA3。所有存储都将提供一个以这个 rootHash 开始的 MerkleProof。accountProof
:ARRAY
- rlp 序列化的 MerkleTree-Nodes 数组,从 stateRoot-Node 开始,沿着 SHA3 (地址) 作为键的路径。-
storageProof
:ARRAY
- 根据请求的存储条目数组。每个条目都是一个具有以下属性的对象:key
:QUANTITY
- 请求的存储键value
:QUANTITY
- 存储值proof
:ARRAY
- 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 个不同的重要数据点:
- 黄皮书中指定的账户对象的 4 个字段
[nonce, balance, storageHash, codeHash ]
,允许存储账户对象的哈希值,以便跟踪更改。 - 从指定块的 stateRoot 开始的账户的 MerkleProof。
- 每个请求的存储条目的 MerkleProof,从账户的 storageHash 开始。
将这些组合在一个方法中,可以使客户端非常高效地工作,因为所需的数据已经从数据库中获取。
不存在的值的证明
如果地址或存储值不存在,则证明需要提供足够的数据来验证这个事实。这意味着客户端需要沿着从根节点的路径传递数据,直到最后一个匹配的节点。如果最后一个匹配的节点是一个分支,则节点中的证明值必须是空的。如果是叶子类型,则它必须指向不同的相对路径,以证明所请求的路径不存在。
可能需要讨论的变更:
- 不是提供块号,也许 blockhash 会更好,因为它允许证明叔块状态。
- 为了减少数据,账户对象可能只提供
accountProof
和storageProof
。字段balance
、nonce
、storageHash
和codeHash
可以通过反序列化从证明中的最后一个节点获取。
向后兼容性
由于这只是添加了一个新方法,因此不存在向后兼容性问题。
测试用例
待办事项:测试仍然需要实现,但是创建证明的核心功能已经存在于客户端内部,并且经过了充分的测试。
版权
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.