Skip to content

Commit 11ef9df

Browse files
paouvrardcarver
authored andcommitted
EIP 1186 eth_getProof support (#1185)
* add getProof to the eth api * document getProof in docs.web3.eth API * add proof verification example to docs
1 parent ee7f3e8 commit 11ef9df

File tree

6 files changed

+133
-0
lines changed

6 files changed

+133
-0
lines changed

docs/web3.eth.rst

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,107 @@ The following methods are available on the ``web3.eth`` namespace.
201201
'0x00000000000000000000000000000000000000000000000000120a0b063499d4'
202202
203203
204+
.. py:method:: Eth.getProof(account, positions, block_identifier=eth.defaultBlock)
205+
206+
* Delegates to ``eth_getProof`` RPC Method
207+
208+
Returns the values from an array of storage positions for the given ``account`` at the
209+
block specified by ``block_identifier``.
210+
211+
``account`` may be a hex address or an ENS name
212+
213+
.. code-block:: python
214+
215+
>>> web3.eth.getProof('0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b', [0], 3391)
216+
AttributeDict({
217+
'address': '0x4CB06C43fcdABeA22541fcF1F856A6a296448B6c',
218+
'accountProof': ['0xf90211a03841a7ddd65c70c94b8efa79190d00f0ab134b26f18dcad508f60a7e74559d0ba0464b07429a05039e22931492d6c6251a860c018ea390045d596b1ac11b5c7aa7a011f4b89823a03c9c4b5a8ab079ee1bc0e2a83a508bb7a5dc7d7fb4f2e95d3186a0b5f7c51c3b2d51d97f171d2b38a4df1a7c0acc5eb0de46beeff4d07f5ed20e19a0b591a2ce02367eda31cf2d16eca7c27fd44dbf0864b64ea8259ad36696eb2a04a02b646a7552b8392ae94263757f699a27d6e9176b4c06b9fc0a722f893b964795a02df05d68bceb88eebf68aafde61d10ab942097afc1c58b8435ffd3895358a742a0c2f16143c4d1db03276c433696dddb3e9f3b113bcd854b127962262e98f43147a0828820316cc02bfefd899aba41340659fd06df1e0a0796287ec2a4110239f6d2a050496598670b04df7bbff3718887fa36437d6d8c7afb4eff86f76c5c7097dcc4a0c14e9060c6b3784e35b9e6ae2ad2984142a75910ccc89eb89dc1e2f44b6c58c2a009804db571d0ce07913e1cbacc4f1dc4fb8265c936f5c612e3a47e91c64d8e9fa063d96f38b3cb51b1665c6641e25ffe24803f2941e5df79942f6a53b7169647e4a0899f71abb18c6c956118bf567fac629b75f7e9526873e429d3d8abb6dbb58021a00fd717235298742623c0b3cafb3e4bd86c0b5ab1f71097b4dd19f3d6925d758da0096437146c16097f2ccc1d3e910d65a4132803baee2249e72c8bf0bcaaeb37e580',
219+
'0xf90151a097b17a89fd2c03ee98cb6459c08f51b269da5cee46650e84470f62bf83b43efe80a03b269d284a4c3cf8f8deacafb637c6d77f607eec8d75e8548d778e629612310480a01403217a7f1416830c870087c524dabade3985271f6f369a12b010883c71927aa0f592ac54c879817389663be677166f5022943e2fe1b52617a1d15c2f353f27dda0ac8d015a9e668f5877fcc391fae33981c00577096f0455b42df4f8e8089ece24a003ba34a13e2f2fb4bf7096540b42d4955c5269875b9cf0f7b87632585d44c9a580a0b179e3230b07db294473ae57f0170262798f8c551c755b5665ace1215cee10ca80a0552d24252639a6ae775aa1df700ffb92c2411daea7286f158d44081c8172d072a0772a87d08cf38c4c68bfde770968571abd16fd3835cb902486bd2e515d53c12d80a0413774f3d900d2d2be7a3ad999ffa859a471dc03a74fb9a6d8275455f5496a548080',
220+
'0xf869a020d13b52a61d3c1325ce3626a51418adebd6323d4840f1bdd93906359d11c933b846f8440180a01ab7c0b0a2a4bbb5a1495da8c142150891fc64e0c321e1feb70bd5f881951f7ea0551332d96d085185ab4019ad8bcf89c45321e136c261eb6271e574a2edf1461f'
221+
],
222+
'balance': 0,
223+
'codeHash': '0x551332d96d085185ab4019ad8bcf89c45321e136c261eb6271e574a2edf1461f',
224+
'nonce': 1,
225+
'storageHash': '0x1ab7c0b0a2a4bbb5a1495da8c142150891fc64e0c321e1feb70bd5f881951f7e',
226+
'storageProof': [
227+
AttributeDict({
228+
'key': '0x00',
229+
'value': '0x48656c6c6f00000000000000000000000000000000000000000000000000000a',
230+
'proof': ['0xf9019180a01ace80e7bed79fbadbe390876bd1a7d9770edf9462049ef8f4b555d05715d53ea049347a3c2eac6525a3fd7e3454dab19d73b4adeb9aa27d29493b9843f3f88814a085079b4abcd07fd4a5d6c52d35f4c4574aecc85830e90c478ca8c18fcbe590de80a02e3f8ad7ea29e784007f51852b9c3e470aef06b11bac32586a8b691134e4c27da064d2157a14bc31f195f73296ea4dcdbe7698edbf3ca81c44bf7730179d98d94ca09e7dc2597c9b7f72ddf84d7eebb0fe2a2fa2ab54fe668cd14fee44d9b40b1a53a0aa5d4acc7ac636d16bc9655556770bc325e1901fb62dc53770ef9110009e080380a0d5fde962bd2fb5326ddc7a9ca7fe0ee47c5bb3227f838b6d73d3299c22457596a08691410eff46b88f929ef649ea25025f62a5362ca8dc8876e5e1f4fc8e79256d80a0673e88d3a8a4616f676793096b5ae87cff931bd20fb8dd466f97809a1126aad8a08b774a45c2273553e2daf4bbc3a8d44fb542ea29b6f125098f79a4d211b3309ca02fed3139c1791269acb9365eddece93e743900eba6b42a6a8614747752ba268f80',
231+
'0xf891808080a0c7d094301e0c54da37b696d85f72de5520b224ab2cf4f045d8db1a3374caf0488080a0fc5581783bfe27fab9423602e1914d719fd71433e9d7dd63c95fe7e58d10c9c38080a0c64f346fc7a21f6679cba8abdf37ca2de8c4fcd8f8bcaedb261b5f77627c93908080808080a0ddef2936a67a3ac7d3d4ff15a935a45f2cc4976c8f0310aed85daf763780e2b480',
232+
'0xf843a0200decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a1a048656c6c6f00000000000000000000000000000000000000000000000000000a'
233+
]
234+
})
235+
]
236+
})
237+
238+
* Merkle proof verification using py-trie.
239+
240+
The following example verifies that the values returned in the AttributeDict are included in the state of given trie ``root``.
241+
242+
.. code-block:: python
243+
244+
from eth_utils import (
245+
keccak,
246+
)
247+
import rlp
248+
from rlp.sedes import (
249+
Binary,
250+
big_endian_int,
251+
)
252+
from trie import (
253+
HexaryTrie,
254+
)
255+
from web3._utils.encoding import (
256+
pad_bytes,
257+
)
258+
259+
def format_proof_nodes(proof):
260+
trie_proof = []
261+
for rlp_node in proof:
262+
trie_proof.append(rlp.decode(bytes(rlp_node)))
263+
return trie_proof
264+
265+
def verify_eth_getProof(proof, root):
266+
trie_root = Binary.fixed_length(32, allow_empty=True)
267+
hash32 = Binary.fixed_length(32)
268+
269+
class _Account(rlp.Serializable):
270+
fields = [
271+
('nonce', big_endian_int),
272+
('balance', big_endian_int),
273+
('storage', trie_root),
274+
('code_hash', hash32)
275+
]
276+
acc = _Account(
277+
proof.nonce, proof.balance, proof.storageHash, proof.codeHash
278+
)
279+
rlp_account = rlp.encode(acc)
280+
trie_key = keccak(bytes.fromhex(proof.address[2:]))
281+
282+
assert rlp_account == HexaryTrie.get_from_proof(
283+
root, trie_key, format_proof_nodes(proof.accountProof)
284+
), "Failed to verify account proof {}".format(proof.address)
285+
286+
for storage_proof in proof.storageProof:
287+
trie_key = keccak(pad_bytes(b'\x00', 32, storage_proof.key))
288+
root = proof.storageHash
289+
if storage_proof.value == b'\x00':
290+
rlp_value = b''
291+
else:
292+
rlp_value = rlp.encode(storage_proof.value)
293+
294+
assert rlp_value == HexaryTrie.get_from_proof(
295+
root, trie_key, format_proof_nodes(storage_proof.proof)
296+
), "Failed to verify storage proof {}".format(storage_proof.key)
297+
298+
return True
299+
300+
block = w3.eth.getBlock(3391)
301+
proof = w3.eth.getProof('0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b', [0, 1], 3391)
302+
assert verify_eth_getProof(proof, block.stateRoot)
303+
304+
204305
.. py:method:: Eth.getCode(account, block_identifier=eth.defaultBlock)
205306
206307
* Delegates to ``eth_getCode`` RPC Method

web3/_utils/rpc_abi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
'eth_getCode': ['address', None],
4343
'eth_getLogs': FILTER_PARAMS_ABIS,
4444
'eth_getStorageAt': ['address', 'uint', None],
45+
'eth_getProof': ['address', 'uint[]', None],
4546
'eth_getTransactionByBlockHashAndIndex': ['bytes32', 'uint'],
4647
'eth_getTransactionByHash': ['bytes32'],
4748
'eth_getTransactionCount': ['address', None],

web3/eth.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ def getStorageAt(self, account, position, block_identifier=None):
121121
[account, position, block_identifier]
122122
)
123123

124+
def getProof(self, account, positions, block_identifier=None):
125+
if block_identifier is None:
126+
block_identifier = self.defaultBlock
127+
return self.web3.manager.request_blocking(
128+
"eth_getProof",
129+
[account, positions, block_identifier]
130+
)
131+
124132
def getCode(self, account, block_identifier=None):
125133
if block_identifier is None:
126134
block_identifier = self.defaultBlock

web3/middleware/exception_retry_request.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'eth_blockNumber',
2424
'eth_getBalance',
2525
'eth_getStorageAt',
26+
'eth_getProof',
2627
'eth_getCode',
2728
'eth_getBlockByNumber',
2829
'eth_getBlockByHash',

web3/middleware/pythonic.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,25 @@ def to_hexbytes(num_bytes, val, variable_length=False):
189189
block_formatter = apply_formatters_to_dict(BLOCK_FORMATTERS)
190190

191191

192+
STORAGE_PROOF_FORMATTERS = {
193+
'key': HexBytes,
194+
'value': HexBytes,
195+
'proof': apply_formatter_to_array(HexBytes),
196+
}
197+
198+
ACCOUNT_PROOF_FORMATTERS = {
199+
'address': to_checksum_address,
200+
'accountProof': apply_formatter_to_array(HexBytes),
201+
'balance': to_integer_if_hex,
202+
'codeHash': to_hexbytes(32),
203+
'nonce': to_integer_if_hex,
204+
'storageHash': to_hexbytes(32),
205+
'storageProof': apply_formatter_to_array(apply_formatters_to_dict(STORAGE_PROOF_FORMATTERS))
206+
}
207+
208+
proof_formatter = apply_formatters_to_dict(ACCOUNT_PROOF_FORMATTERS)
209+
210+
192211
SYNCING_FORMATTERS = {
193212
'startingBlock': to_integer_if_hex,
194213
'currentBlock': to_integer_if_hex,
@@ -271,6 +290,7 @@ def to_hexbytes(num_bytes, val, variable_length=False):
271290
),
272291
'eth_getCode': apply_formatter_at_index(block_number_formatter, 1),
273292
'eth_getStorageAt': apply_formatter_at_index(block_number_formatter, 2),
293+
'eth_getProof': apply_formatter_at_index(block_number_formatter, 2),
274294
'eth_getTransactionByBlockNumberAndIndex': compose(
275295
apply_formatter_at_index(block_number_formatter, 0),
276296
apply_formatter_at_index(integer_to_hex, 1),
@@ -327,6 +347,7 @@ def to_hexbytes(num_bytes, val, variable_length=False):
327347
'eth_getFilterLogs': filter_result_formatter,
328348
'eth_getLogs': filter_result_formatter,
329349
'eth_getStorageAt': HexBytes,
350+
'eth_getProof': apply_formatter_if(is_not_null, proof_formatter),
330351
'eth_getTransactionByBlockHashAndIndex': apply_formatter_if(
331352
is_not_null,
332353
transaction_formatter,

web3/providers/eth_tester/defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ def personal_send_transaction(eth_tester, params):
179179
),
180180
'getBalance': call_eth_tester('get_balance'),
181181
'getStorageAt': not_implemented,
182+
'getProof': not_implemented,
182183
'getTransactionCount': call_eth_tester('get_nonce'),
183184
'getBlockTransactionCountByHash': null_if_block_not_found(compose(
184185
len,

0 commit comments

Comments
 (0)