35
35
36
36
from p2p .exceptions import (
37
37
BadLESResponse ,
38
+ NoEligiblePeers ,
38
39
)
39
40
from p2p .cancel_token import CancelToken
40
41
from p2p import protocol
@@ -160,6 +161,15 @@ async def get_contract_code(self, block_hash: Hash32, address: Address) -> bytes
160
161
:return: bytecode of the contract, ``b''`` if no code is set
161
162
"""
162
163
peer = cast (LESPeer , self .peer_pool .highest_td_peer )
164
+
165
+ # get account for later verification, and
166
+ # to confirm that our highest total difficulty peer has the info
167
+ try :
168
+ account = await self .get_account (block_hash , address )
169
+ except HeaderNotFound as exc :
170
+ raise NoEligiblePeers ("Our best peer does not have header %s" % block_hash ) from exc
171
+
172
+ # request contract code
163
173
request_id = gen_request_id ()
164
174
peer .sub_proto .send_get_contract_code (block_hash , keccak (address ), request_id )
165
175
reply = await self ._wait_for_reply (request_id )
@@ -170,12 +180,15 @@ async def get_contract_code(self, block_hash: Hash32, address: Address) -> bytes
170
180
bytecode = reply ['codes' ][0 ]
171
181
172
182
# validate bytecode against a proven account
173
- account = await self .get_account (block_hash , address )
174
-
175
183
if account .code_hash == keccak (bytecode ):
176
184
return bytecode
185
+ elif bytecode == b'' :
186
+ # TODO disambiguate failure types here, and raise the appropriate exception
187
+ # An (incorrectly) empty bytecode might indicate a bad-acting peer, or it might not
188
+ raise NoEligiblePeers ("Our best peer incorrectly responded with an empty code value" )
177
189
else :
178
- # disconnect from this bad peer
190
+ # a bad-acting peer sent an invalid non-empty bytecode
191
+ # disconnect from the peer
179
192
await self .disconnect_peer (peer , DisconnectReason .subprotocol_error )
180
193
# try again with another peer
181
194
return await self .get_contract_code (block_hash , address )
0 commit comments