eth_getProof
方法返回特定地址的账户和存储值,包括加密默克尔证明。这些证明可以验证数据是否正确地包含在状态字典树中,而无需信任数据提供者。
此方法检索指定存储键的账户证明和存储证明。
获取证明的账户地址
为其生成证明的存储键数组
区块号或区块标签
带有默克尔证明的账户信息
账户地址
从状态根到账户叶节点的 RLP 序列化默克尔树节点数组
账户余额(以 wei 为单位,十六进制)
账户代码的哈希(十六进制)
账户的 nonce(十六进制)
账户存储字典树根的哈希(十六进制)
请求键的存储证明数组
请求的存储键
存储值(十六进制)
从存储哈希到存储值的 RLP 序列化默克尔树节点数组
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"accountProof": [
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90131a0..."
],
"balance": "0x0",
"codeHash": "0x34eb8290afdbf2cea95f7afaefe0e2e8071e0b9a3ee4f106c8f3b6a45663e05d",
"nonce": "0x1",
"storageHash": "0x9588589599c2d368b2c109def4942d664838b3b681e80898fae7d2d140fe6a4c",
"storageProof": [
{
"key": "0x0000000000000000000000000000000000000000000000000000000000000002",
"value": "0x000000000000000000000000000000000000000001cc3c3beb2aa7ced800000",
"proof": ["0xf90211a0...", "0xf90211a0...", "0xf90211a0...", "0xf90211a0..."]
},
{
"key": "0x29ae811400000000000000000000000000000000000000000000000000000000",
"value": "0x0000000000000000000000000000000000000000000000000000000000000001",
"proof": ["0xf90211a0...", "0xf90211a0...", "0xf90211a0...", "0xf90211a0..."]
}
]
}
}
默克尔证明允许在不拥有整棵树的情况下验证一段数据是否属于默克尔树的一部分。在以太坊中:
// 示例:使用证明验证 DAI 代币的总供应量
const ethers = require('ethers');
const RLP = require('rlp');
const { keccak256 } = ethers.utils;
async function verifyTokenSupply(tokenAddress) {
// DAI 代币的 totalSupply 存储在存储槽位 2
const totalSupplySlot = '0x0000000000000000000000000000000000000000000000000000000000000002';
// 获取最新区块哈希作为参考
const blockData = await provider.send('eth_getBlockByNumber', ['latest', false]);
const blockHash = blockData.hash;
// 获取证明
const proof = await provider.send('eth_getProof', [tokenAddress, [totalSupplySlot], 'latest']);
console.log(`区块哈希: ${blockHash}`);
console.log(`状态根: ${blockData.stateRoot}`);
console.log(`存储哈希: ${proof.storageHash}`);
// 从证明中提取总供应量
const totalSupplyHex = proof.storageProof[0].value;
const totalSupply = BigInt(totalSupplyHex);
const totalSupplyDecimal = totalSupply / 10n ** 18n; // DAI 有 18 位小数
console.log(`已验证的总供应量: ${totalSupplyDecimal.toString()} DAI`);
// 要真正在加密层面验证证明,我们需要:
// 1. 验证从 stateRoot 到 storageHash 的账户证明
// 2. 验证从 storageHash 到 totalSupply 值的存储证明
// 这需要实现默克尔-帕特里夏字典树验证
return {
blockHash,
stateRoot: blockData.stateRoot,
storageHash: proof.storageHash,
totalSupply: totalSupplyDecimal.toString(),
};
}
// 使用方法
const daiProof = await verifyTokenSupply('0x6B175474E89094C44Da98b954EedeAC495271d0F');
对于 Solidity 合约,存储槽位可以按以下方式确定:
keccak256(abi.encode(key, slot))
,其中 slot
是映射声明的位置keccak256(slot) + index
keccak256(abi.encode(userAddress, 3))