113 lines
3.4 KiB
Python
113 lines
3.4 KiB
Python
import asyncio
|
|
import logging
|
|
import traceback
|
|
from asyncio import CancelledError
|
|
from typing import Optional
|
|
|
|
from sqlalchemy import orm
|
|
from web3 import Web3
|
|
from web3.eth import AsyncEth
|
|
|
|
from mev_inspect.block import create_from_block_number
|
|
from mev_inspect.classifiers.trace import TraceClassifier
|
|
from mev_inspect.inspect_block import inspect_block, inspect_many_blocks
|
|
from mev_inspect.methods import get_block_receipts, trace_block
|
|
from mev_inspect.provider import get_base_provider
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# add missing parity methods
|
|
# this is a bit gross
|
|
AsyncEth.trace_block = trace_block
|
|
AsyncEth.get_block_receipts = get_block_receipts
|
|
|
|
|
|
class MEVInspector:
|
|
def __init__(
|
|
self,
|
|
rpc: str,
|
|
max_concurrency: int = 1,
|
|
request_timeout: int = 300,
|
|
):
|
|
base_provider = get_base_provider(rpc, request_timeout=request_timeout)
|
|
self.w3 = Web3(base_provider, modules={"eth": (AsyncEth,)}, middlewares=[])
|
|
|
|
self.trace_classifier = TraceClassifier()
|
|
self.max_concurrency = asyncio.Semaphore(max_concurrency)
|
|
|
|
async def create_from_block(
|
|
self,
|
|
trace_db_session: Optional[orm.Session],
|
|
block_number: int,
|
|
):
|
|
return await create_from_block_number(
|
|
w3=self.w3,
|
|
block_number=block_number,
|
|
trace_db_session=trace_db_session,
|
|
)
|
|
|
|
async def inspect_single_block(
|
|
self,
|
|
inspect_db_session: orm.Session,
|
|
block: int,
|
|
trace_db_session: Optional[orm.Session],
|
|
):
|
|
return await inspect_block(
|
|
inspect_db_session,
|
|
self.w3,
|
|
self.trace_classifier,
|
|
block,
|
|
trace_db_session=trace_db_session,
|
|
)
|
|
|
|
async def inspect_many_blocks(
|
|
self,
|
|
inspect_db_session: orm.Session,
|
|
trace_db_session: Optional[orm.Session],
|
|
after_block: int,
|
|
before_block: int,
|
|
block_batch_size: int = 10,
|
|
):
|
|
tasks = []
|
|
for block_number in range(after_block, before_block, block_batch_size):
|
|
batch_after_block = block_number
|
|
batch_before_block = min(block_number + block_batch_size, before_block)
|
|
|
|
tasks.append(
|
|
asyncio.ensure_future(
|
|
self.safe_inspect_many_blocks(
|
|
inspect_db_session,
|
|
trace_db_session,
|
|
after_block_number=batch_after_block,
|
|
before_block_number=batch_before_block,
|
|
)
|
|
)
|
|
)
|
|
logger.info(f"Gathered {before_block-after_block} blocks to inspect")
|
|
try:
|
|
await asyncio.gather(*tasks)
|
|
except CancelledError:
|
|
logger.info("Requested to exit, cleaning up...")
|
|
except Exception as e:
|
|
logger.error(f"Exited due to {type(e)}")
|
|
traceback.print_exc()
|
|
raise
|
|
|
|
async def safe_inspect_many_blocks(
|
|
self,
|
|
inspect_db_session: orm.Session,
|
|
trace_db_session: Optional[orm.Session],
|
|
after_block_number: int,
|
|
before_block_number: int,
|
|
):
|
|
async with self.max_concurrency:
|
|
return await inspect_many_blocks(
|
|
inspect_db_session,
|
|
self.w3,
|
|
self.trace_classifier,
|
|
after_block_number,
|
|
before_block_number,
|
|
trace_db_session=trace_db_session,
|
|
)
|