runner workarounds for hardhat

This commit is contained in:
Tim Olson
2024-01-10 16:50:49 -04:00
parent e4d70a28e8
commit bb440205f8
2 changed files with 41 additions and 22 deletions

View File

@@ -16,8 +16,9 @@ class Block(Base):
@property
def timestamp(self) -> int:
raw = self.data['timestamp']
# noinspection PyTypeChecker
return hexint(self.data['timestamp'])
return raw if type(raw) is int else hexint(raw)
def __str__(self):
return f'{self.height}_{self.hash.hex()[:5]}'

View File

@@ -150,15 +150,20 @@ class BlockStateRunner:
while self.running:
try:
new_blocks = await w3.eth.filter("latest")
for head in await new_blocks.get_new_entries():
if head != prev_blockhash:
prev_blockhash = head
await self.queue.put(head)
log.debug(f'polled new block {hexstr(head)}')
if not self.running:
break
await asyncio.sleep(config.polling)
# polling mode is used primarily because Hardhat fails to deliver newHeads events after about an hour
# unfortunately, hardhat also stops responding to eth_getBlockByHash. so instead, we use the standard (stupid)
# 'latest' polling for blocks, and we push the entire block to the queue since apparently this is the only
# rpc call Hardhat seems to consistently support. The worker must then detect the type of object pushed to the
# work queue and either use the block directly or query for the block if the queue object is a hashcode.
block = await w3.eth.get_block('latest')
head = block['hash']
if head != prev_blockhash:
prev_blockhash = head
await self.queue.put(block)
log.debug(f'polled new block {hexstr(head)}')
if not self.running:
break
await asyncio.sleep(config.polling)
except ConnectionClosedError:
pass
finally:
@@ -187,7 +192,7 @@ class BlockStateRunner:
except TimeoutError:
# 1 second has passed without a new head. Run the postprocess callbacks to check for activated time-based triggers
if prev_head is not None:
await self.handle_time_tick(head)
await self.handle_time_tick(prev_head)
else:
try:
await self.handle_head(chain, head, w3)
@@ -202,21 +207,26 @@ class BlockStateRunner:
async def handle_head(self, chain, blockhash, w3):
log.debug(f'processing block {hexstr(blockhash)}')
# check blockhash type and convert
try:
block_data = blockhash
blockhash = block_data['hash']
parent = block_data['parentHash']
height = block_data['number']
except TypeError:
response = await w3.provider.make_request('eth_getBlockByHash', [blockhash, False])
block_data:dict = response['result']
parent = bytes.fromhex(block_data['parentHash'][2:])
height = int(block_data['number'], 0)
log.debug(f'processing block {blockhash}')
chain_id = chain.chain_id
session = None
try:
blockhash = hexstr(blockhash)
if self.state is not None and blockhash in self.state.by_hash:
log.debug(f'block {blockhash} was already processed')
return
# block_data = await w3.eth.get_block(head['hash'], True)
response = await w3.provider.make_request('eth_getBlockByHash', [blockhash, False])
block_data = response['result']
if block_data is None:
log.warning(f'block data for {blockhash} was None')
return # todo get block when hardhat stops responding to getBlockByHash
block = Block(chain=chain_id, height=int(block_data['number'], 0),
hash=bytes.fromhex(block_data['hash'][2:]), parent=bytes.fromhex(block_data['parentHash'][2:]), data=block_data)
assert block_data is not None
block = Block(chain=chain_id, height=height, hash=blockhash, parent=parent, data=block_data)
latest_block.set(block)
current_clock.get().set(block.timestamp)
if self.state is None:
@@ -329,8 +339,16 @@ class BlockStateRunner:
async def handle_time_tick(self, blockhash):
if current_blockstate.get() is None:
return
try:
blockhash = blockhash['hash']
except TypeError:
pass
# similar to handle_head, but we only call the postprocess events, since there was only a time tick and no new block data
block = self.state.by_hash[blockhash]
try:
block = self.state.by_hash[blockhash]
except KeyError:
log.warning(f'No block data for {blockhash}. Aborting time tick.')
return
fork = self.state.fork(block)
current_block.set(block)
current_fork.set(fork)