fixes
This commit is contained in:
@@ -17,13 +17,15 @@ class Blockchain:
|
|||||||
def get(name_or_id):
|
def get(name_or_id):
|
||||||
return Blockchain.for_name(name_or_id) if type(name_or_id) is str else Blockchain.for_id(name_or_id)
|
return Blockchain.for_name(name_or_id) if type(name_or_id) is str else Blockchain.for_id(name_or_id)
|
||||||
|
|
||||||
def __init__(self, chain_id, name, confirms=10):
|
def __init__(self, chain_id, name, confirms=10, batch_size=100):
|
||||||
"""
|
"""
|
||||||
confirms is the number of blocks until a block can be considered finalized and unforkable
|
confirms is the number of blocks until a block can be considered finalized and unforkable
|
||||||
|
batch_size is the number of blocks to fetch per logs query
|
||||||
"""
|
"""
|
||||||
self.chain_id = chain_id
|
self.chain_id = chain_id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.confirms = confirms
|
self.confirms = confirms
|
||||||
|
self.batch_size = batch_size
|
||||||
Blockchain._instances_by_id[chain_id] = self
|
Blockchain._instances_by_id[chain_id] = self
|
||||||
Blockchain._instances_by_name[name] = self
|
Blockchain._instances_by_name[name] = self
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ class Fork:
|
|||||||
|
|
||||||
def for_height(self, height):
|
def for_height(self, height):
|
||||||
""" returns a new Fork object for an older block along this fork. used for root promotion. """
|
""" returns a new Fork object for an older block along this fork. used for root promotion. """
|
||||||
assert( self.height - len(self.ancestry) < height <= self.height)
|
assert( height <= self.height )
|
||||||
|
if height <= self.height - len(self.ancestry):
|
||||||
|
return None
|
||||||
return Fork(self.ancestry[self.height-height:], height=height)
|
return Fork(self.ancestry[self.height-height:], height=height)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from dexorder.blockstate.blockdata import BlockData
|
|||||||
from dexorder.blockstate.db_state import DbState
|
from dexorder.blockstate.db_state import DbState
|
||||||
from dexorder.configuration import parse_args
|
from dexorder.configuration import parse_args
|
||||||
from dexorder.runner import BlockStateRunner
|
from dexorder.runner import BlockStateRunner
|
||||||
from dexorder.data import pool_prices, vault_tokens, vault_addresses, underfunded_vaults, active_orders
|
|
||||||
|
|
||||||
log = logging.getLogger('dexorder')
|
log = logging.getLogger('dexorder')
|
||||||
|
|
||||||
@@ -24,6 +23,7 @@ if __name__ == '__main__':
|
|||||||
db_state = DbState(BlockData.by_tag['db'])
|
db_state = DbState(BlockData.by_tag['db'])
|
||||||
with db.session:
|
with db.session:
|
||||||
state = db_state.load()
|
state = db_state.load()
|
||||||
|
log.info(f'loaded state from db for root block {state.root_block}')
|
||||||
runner = BlockStateRunner(state)
|
runner = BlockStateRunner(state)
|
||||||
if db:
|
if db:
|
||||||
# noinspection PyUnboundLocalVariable
|
# noinspection PyUnboundLocalVariable
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class DbState:
|
|||||||
elif t == BlockData.Type.DICT:
|
elif t == BlockData.Type.DICT:
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
var: BlockDict = BlockData.registry[series]
|
var: BlockDict = BlockData.registry[series]
|
||||||
for row in db.session.query(SeriesDict).where(SeriesSet.series==keystr(series)):
|
for row in db.session.query(SeriesDict).where(SeriesDict.series==keystr(series)):
|
||||||
var[strkey(row.key)] = row.value
|
var[strkey(row.key)] = row.value
|
||||||
completed_block.set(root_block)
|
completed_block.set(root_block)
|
||||||
return state
|
return state
|
||||||
|
|||||||
@@ -182,7 +182,6 @@ class BlockState:
|
|||||||
if not difflist or len(difflist) == 1 and difflist[0].value == DELETE and difflist[0].height <= new_root_fork.height:
|
if not difflist or len(difflist) == 1 and difflist[0].value == DELETE and difflist[0].height <= new_root_fork.height:
|
||||||
del self.diffs_by_series[s][k]
|
del self.diffs_by_series[s][k]
|
||||||
|
|
||||||
del self.by_hash[self.root_block.hash] # old root block
|
|
||||||
self.root_block = block
|
self.root_block = block
|
||||||
log.debug(f'promoted root {self.root_block}')
|
log.debug(f'promoted root {self.root_block}')
|
||||||
return diffs
|
return diffs
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Callable, Union
|
|||||||
|
|
||||||
from web3 import AsyncWeb3
|
from web3 import AsyncWeb3
|
||||||
from web3.contract.contract import ContractEvents
|
from web3.contract.contract import ContractEvents
|
||||||
from web3.exceptions import LogTopicError
|
from web3.exceptions import LogTopicError, MismatchedABI
|
||||||
from web3.types import EventData
|
from web3.types import EventData
|
||||||
|
|
||||||
from dexorder import Blockchain, db, blockchain, NARG, dec
|
from dexorder import Blockchain, db, blockchain, NARG, dec
|
||||||
@@ -64,12 +64,14 @@ class BlockStateRunner:
|
|||||||
|
|
||||||
w3 = blockchain.connect()
|
w3 = blockchain.connect()
|
||||||
w3ws = create_w3_ws()
|
w3ws = create_w3_ws()
|
||||||
|
self.setup_triggers(w3)
|
||||||
|
|
||||||
chain_id = await w3ws.eth.chain_id
|
chain_id = await w3ws.eth.chain_id
|
||||||
chain = Blockchain.for_id(chain_id)
|
chain = Blockchain.for_id(chain_id)
|
||||||
current_chain.set(chain)
|
current_chain.set(chain)
|
||||||
|
|
||||||
state = self.state
|
state = self.state
|
||||||
|
|
||||||
async with w3ws as w3ws:
|
async with w3ws as w3ws:
|
||||||
await w3ws.eth.subscribe('newHeads')
|
await w3ws.eth.subscribe('newHeads')
|
||||||
while True:
|
while True:
|
||||||
@@ -88,25 +90,33 @@ class BlockStateRunner:
|
|||||||
# initialize
|
# initialize
|
||||||
state = BlockState(block)
|
state = BlockState(block)
|
||||||
current_blockstate.set(state)
|
current_blockstate.set(state)
|
||||||
self.setup_triggers(w3)
|
|
||||||
log.info('Created new empty root state')
|
log.info('Created new empty root state')
|
||||||
else:
|
else:
|
||||||
fork = state.add_block(block)
|
fork = state.add_block(block)
|
||||||
if fork is None:
|
if fork is None:
|
||||||
log.debug(f'discarded late-arriving head {block}')
|
log.debug(f'discarded late-arriving head {block}')
|
||||||
else:
|
else:
|
||||||
futures = []
|
batches = []
|
||||||
if fork.disjoint:
|
if fork.disjoint:
|
||||||
# todo backfill batches
|
# backfill batches
|
||||||
from_height = state.root_block.height + 1
|
for callback, event, log_filter in self.events:
|
||||||
log.error(f'backfill unimplemented for range {from_height} to {block}')
|
from_height = state.root_block.height + 1
|
||||||
exit(1)
|
end_height = block.height
|
||||||
|
while from_height <= end_height:
|
||||||
|
to_height = min(end_height, from_height + chain.batch_size - 1)
|
||||||
|
lf = dict(log_filter)
|
||||||
|
lf['fromBlock'] = from_height
|
||||||
|
lf['toBlock'] = to_height
|
||||||
|
log.debug(f'batch backfill {from_height} - {to_height}')
|
||||||
|
batches.append((w3.eth.get_logs(log_filter), callback, event, lf))
|
||||||
|
from_height += chain.batch_size
|
||||||
else:
|
else:
|
||||||
# event callbacks are triggered in the order in which they're registered. the events passed to
|
# event callbacks are triggered in the order in which they're registered. the events passed to
|
||||||
# each callback are in block transaction order
|
# each callback are in block transaction order
|
||||||
for callback, event, log_filter in self.events:
|
for callback, event, log_filter in self.events:
|
||||||
log_filter['blockhash'] = w3.to_hex(block.hash)
|
lf = dict(log_filter)
|
||||||
futures.append(w3.eth.get_logs(log_filter))
|
lf['blockhash'] = w3.to_hex(block.hash)
|
||||||
|
batches.append((w3.eth.get_logs(log_filter), callback, event, log_filter))
|
||||||
|
|
||||||
# set up for callbacks
|
# set up for callbacks
|
||||||
current_block.set(block)
|
current_block.set(block)
|
||||||
@@ -115,11 +125,12 @@ class BlockStateRunner:
|
|||||||
session.begin()
|
session.begin()
|
||||||
session.add(block)
|
session.add(block)
|
||||||
# callbacks
|
# callbacks
|
||||||
for future, (callback,event,filter_args) in zip(futures,self.events):
|
for future,callback,event,filter_args in batches:
|
||||||
for log_event in await future:
|
log_events = await future
|
||||||
|
for log_event in log_events:
|
||||||
try:
|
try:
|
||||||
parsed = event.process_log(log_event)
|
parsed = event.process_log(log_event)
|
||||||
except LogTopicError:
|
except (LogTopicError, MismatchedABI):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# todo try/except for known retryable errors
|
# todo try/except for known retryable errors
|
||||||
@@ -132,8 +143,8 @@ class BlockStateRunner:
|
|||||||
|
|
||||||
# check for root promotion
|
# check for root promotion
|
||||||
promotion_height = fork.height - chain.confirms
|
promotion_height = fork.height - chain.confirms
|
||||||
if not fork.disjoint and promotion_height > state.root_block.height:
|
if not fork.disjoint and promotion_height > state.root_block.height and (new_root_fork := fork.for_height(promotion_height)):
|
||||||
diff_items = state.promote_root(fork.for_height(promotion_height))
|
diff_items = state.promote_root(new_root_fork)
|
||||||
for callback in self.on_promotion:
|
for callback in self.on_promotion:
|
||||||
# todo try/except for known retryable errors
|
# todo try/except for known retryable errors
|
||||||
callback(state.root_block, diff_items)
|
callback(state.root_block, diff_items)
|
||||||
@@ -146,12 +157,13 @@ class BlockStateRunner:
|
|||||||
else:
|
else:
|
||||||
if session is not None:
|
if session is not None:
|
||||||
session.commit()
|
session.commit()
|
||||||
|
log.info(f'completed block {block}')
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_transfer(transfer: EventData):
|
def handle_transfer(transfer: EventData):
|
||||||
to_address = transfer['args']['to']
|
to_address = transfer['args']['to']
|
||||||
print('transfer', to_address)
|
log.debug(f'transfer {to_address}')
|
||||||
if to_address in vault_addresses:
|
if to_address in vault_addresses:
|
||||||
token_address = transfer['address']
|
token_address = transfer['address']
|
||||||
vault_tokens.add(token_address)
|
vault_tokens.add(token_address)
|
||||||
@@ -169,7 +181,7 @@ class BlockStateRunner:
|
|||||||
addr = swap['address']
|
addr = swap['address']
|
||||||
d = dec(sqrt_price)
|
d = dec(sqrt_price)
|
||||||
price = d*d / dec(2**(96*2))
|
price = d*d / dec(2**(96*2))
|
||||||
print(f'pool {addr} {price}')
|
log.debug(f'pool {addr} {price}')
|
||||||
pool_prices[addr] = price
|
pool_prices[addr] = price
|
||||||
|
|
||||||
|
|
||||||
@@ -179,6 +191,8 @@ class BlockStateRunner:
|
|||||||
self.events.append((callback, event, log_filter))
|
self.events.append((callback, event, log_filter))
|
||||||
|
|
||||||
def setup_triggers(self, w3: AsyncWeb3):
|
def setup_triggers(self, w3: AsyncWeb3):
|
||||||
|
self.events.clear()
|
||||||
|
|
||||||
transfer = w3.eth.contract(abi=get_contract_data('ERC20')['abi']).events.Transfer()
|
transfer = w3.eth.contract(abi=get_contract_data('ERC20')['abi']).events.Transfer()
|
||||||
self.add_event_trigger(self.handle_transfer, transfer)
|
self.add_event_trigger(self.handle_transfer, transfer)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user