order completion debug; refactor UNLOAD as a magic attr

This commit is contained in:
Tim Olson
2023-11-09 23:22:30 -04:00
parent aad4dfdd99
commit 6c9e28b00d
8 changed files with 52 additions and 30 deletions

View File

@@ -7,11 +7,18 @@ from web3 import AsyncWeb3
dec = Decimal dec = Decimal
# NARG is used in argument defaults to mean "not specified" rather than "specified as None" # NARG is used in argument defaults to mean "not specified" rather than "specified as None"
class _NARG: class _Token:
def __init__(self, name):
self.__token_name = name
def __repr__(self): return self.__token_name
def __str__(self): return self.__token_name
class _FalseToken (_Token):
def __bool__(self): return False def __bool__(self): return False
NARG = _NARG()
DELETE = object() # used as a value token to indicate removal of the key
UNLOAD = object() # used as a value token to indicate the key is no longer needed in memory NARG = _FalseToken('NARG')
DELETE = _FalseToken('DELETE') # used as a value token to indicate removal of the key
ADDRESS_0 = '0x0000000000000000000000000000000000000000' ADDRESS_0 = '0x0000000000000000000000000000000000000000'
WEI = 1 WEI = 1
GWEI = 1_000_000_000 GWEI = 1_000_000_000

View File

@@ -1,9 +1,10 @@
import copy
import json import json
import logging import logging
from enum import Enum from enum import Enum
from typing import TypeVar, Generic, Iterable, Union, Any, Iterator, Callable from typing import TypeVar, Generic, Iterable, Union, Any, Iterator, Callable
from dexorder import NARG, DELETE, UNLOAD from dexorder import NARG, DELETE
from dexorder.base.fork import current_fork from dexorder.base.fork import current_fork
from .state import current_blockstate from .state import current_blockstate
from dexorder.util import key2str as util_key2str, str2key as util_str2key from dexorder.util import key2str as util_key2str, str2key as util_str2key
@@ -77,7 +78,14 @@ class BlockData:
used when lazy_getitem is set used when lazy_getitem is set
""" """
assert self.lazy_getitem is not None assert self.lazy_getitem is not None
self.setitem(item, UNLOAD) try:
value = self.getitem(item)
except KeyError:
pass # no key exists to unload
else:
value = copy.deepcopy(value)
value.__dexorder_unload = True # mark value with magic attr
self.setitem(item, value)
def contains(self, item): def contains(self, item):
try: try:

View File

@@ -4,7 +4,7 @@ from typing import Iterable, Optional, Union, Any
from . import BlockSet, BlockDict, BlockState, current_blockstate, DataType from . import BlockSet, BlockDict, BlockState, current_blockstate, DataType
from .blockdata import BlockData, SeriesCollection from .blockdata import BlockData, SeriesCollection
from .diff import DiffItem, DiffEntryItem from .diff import DiffItem, DiffEntryItem
from .. import db, UNLOAD, DELETE from .. import db, DELETE
from ..base.chain import current_chain from ..base.chain import current_chain
from ..base.fork import current_fork, Fork from ..base.fork import current_fork, Fork
from ..database.model import SeriesSet, SeriesDict, Block from ..database.model import SeriesSet, SeriesDict, Block
@@ -52,8 +52,6 @@ class DbState(SeriesCollection):
if diff.value is DELETE: if diff.value is DELETE:
Entity = SeriesSet if t == DataType.SET else SeriesDict if t == DataType.DICT else None Entity = SeriesSet if t == DataType.SET else SeriesDict if t == DataType.DICT else None
db.session.query(Entity).filter(Entity.chain==chain_id, Entity.series==diffseries, Entity.key==diffkey).delete() db.session.query(Entity).filter(Entity.chain==chain_id, Entity.series==diffseries, Entity.key==diffkey).delete()
elif diff.value is UNLOAD:
pass
else: else:
# upsert # upsert
if t == DataType.SET: if t == DataType.SET:

View File

@@ -1,12 +1,12 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Union, Any from typing import Union, Any
from dexorder import DELETE, UNLOAD from dexorder import DELETE
@dataclass @dataclass
class DiffEntry: class DiffEntry:
value: Union[Any, DELETE, UNLOAD] value: Union[Any, DELETE]
height: int height: int
hash: bytes hash: bytes
@@ -18,7 +18,7 @@ class DiffItem:
value: Any value: Any
def __str__(self): def __str__(self):
return f'{self.series}.{self.key}={"[DEL]" if self.value is DELETE else "[UNL]" if self.value is UNLOAD else self.value}' return f'{self.series}.{self.key}={"[DEL]" if self.value is DELETE else self.value}'
@dataclass @dataclass
class DiffEntryItem: class DiffEntryItem:
@@ -32,4 +32,4 @@ class DiffEntryItem:
def __str__(self): def __str__(self):
return (f'{self.entry.hash.hex()} {self.series}.{self.key}=' return (f'{self.entry.hash.hex()} {self.series}.{self.key}='
f'{"[DEL]" if self.entry.value is DELETE else "[UNL]" if self.value is UNLOAD else self.entry.value}') f'{"[DEL]" if self.entry.value is DELETE else self.entry.value}')

View File

@@ -6,7 +6,7 @@ from typing import Any, Optional, Union, Reversible
from sortedcontainers import SortedList from sortedcontainers import SortedList
from dexorder import NARG, UNLOAD from dexorder import NARG
from dexorder.base.fork import Fork, DisjointFork from dexorder.base.fork import Fork, DisjointFork
from dexorder.database.model import Block from dexorder.database.model import Block
from dexorder.util import hexstr from dexorder.util import hexstr
@@ -125,7 +125,7 @@ class BlockState:
def _get_from_diffs(self, fork, diffs): def _get_from_diffs(self, fork, diffs):
for diff in reversed(diffs): for diff in reversed(diffs):
if diff.height <= self.root_block.height or fork is not None and diff in fork and diff.value is not UNLOAD: if diff.height <= self.root_block.height or fork is not None and diff in fork and not hasattr(diff.value, '__dexorder_unload'):
if diff.value is DELETE: if diff.value is DELETE:
break break
else: else:
@@ -191,7 +191,7 @@ class BlockState:
for d in block_diffs: for d in block_diffs:
if d.key == BlockState._DELETE_SERIES_KEY and dead.hash in new_root_fork: if d.key == BlockState._DELETE_SERIES_KEY and dead.hash in new_root_fork:
series_deletions.append(d.series) series_deletions.append(d.series)
elif d.value is UNLOAD and dead in new_root_fork: elif hasattr(d.value, '__dexorder_unload') and dead in new_root_fork:
key_unloads.append((d.series, d.key)) key_unloads.append((d.series, d.key))
else: else:
updated_keys.add((d.series, d.key)) updated_keys.add((d.series, d.key))

View File

@@ -6,7 +6,7 @@ from web3.types import EventData
from dexorder import current_pub, db, dec from dexorder import current_pub, db, dec
from dexorder.base.chain import current_chain from dexorder.base.chain import current_chain
from dexorder.base.order import TrancheExecutionRequest, TrancheKey, ExecutionRequest, new_tranche_execution_request from dexorder.base.order import TrancheExecutionRequest, TrancheKey, ExecutionRequest, new_tranche_execution_request, OrderKey
from dexorder.transaction import create_transactions, submit_transaction_request, handle_transaction_receipts, send_transactions from dexorder.transaction import create_transactions, submit_transaction_request, handle_transaction_receipts, send_transactions
from dexorder.uniswap import UniswapV3Pool, uniswap_price from dexorder.uniswap import UniswapV3Pool, uniswap_price
from dexorder.contract.dexorder import get_factory_contract, vault_address, VaultContract, get_dexorder_contract from dexorder.contract.dexorder import get_factory_contract, vault_address, VaultContract, get_dexorder_contract
@@ -128,11 +128,11 @@ def handle_swap_filled(event: EventData):
except KeyError: except KeyError:
log.warning(f'DexorderSwapFilled IGNORED due to missing order {vault} {order_index}') log.warning(f'DexorderSwapFilled IGNORED due to missing order {vault} {order_index}')
return return
try:
triggers = OrderTriggers.instances[order.key] triggers = OrderTriggers.instances[order.key]
triggers.fill(tranche_index, amount_in, amount_out) triggers.fill(tranche_index, amount_in, amount_out)
# check for fill except KeyError:
if order.remaining <= 0: log.warning(f'No order triggers for fill of {TrancheKey(*order.key,tranche_index)}')
close_order_and_disable_triggers(order, SwapOrderState.Filled)
async def handle_order_completed(event: EventData): async def handle_order_completed(event: EventData):
# event DexorderCompleted (uint64 orderIndex); // todo remove? # event DexorderCompleted (uint64 orderIndex); // todo remove?
@@ -294,7 +294,11 @@ def handle_dexorderexecutions(event: EventData):
def finish_execution_request(req: TrancheExecutionRequest, error: str): def finish_execution_request(req: TrancheExecutionRequest, error: str):
try:
order: Order = Order.of(req.vault, req.order_index) order: Order = Order.of(req.vault, req.order_index)
except KeyError:
log.error(f'Could not get order {OrderKey(req.vault, req.order_index)}')
return
tk = TrancheKey(req.vault, req.order_index, req.tranche_index) tk = TrancheKey(req.vault, req.order_index, req.tranche_index)
if error != '': if error != '':
log.debug(f'execution request for tranche {tk} had error "{error}"') log.debug(f'execution request for tranche {tk} had error "{error}"')

View File

@@ -62,12 +62,12 @@ class RedisState (SeriesCollection):
continue continue
t = d.type t = d.type
series = f'{chain_id}|{d.series2str(diff.series)}' series = f'{chain_id}|{d.series2str(diff.series)}'
key = d.key2str(diff.key)
value = d.value2str(diff.value)
# pub/sub socketio/redis # pub/sub socketio/redis
pub_era = d.opts.get('pub') # event, room, args pub_era = d.opts.get('pub') # event, room, args
if pub_era is True: if pub_era is True:
key = d.key2str(diff.key)
value = d.value2str(diff.value)
pub_era = series, key, [value] pub_era = series, key, [value]
elif callable(pub_era): elif callable(pub_era):
pub_era = await maywait(pub_era(diff.key, diff.value)) pub_era = await maywait(pub_era(diff.key, diff.value))
@@ -75,7 +75,8 @@ class RedisState (SeriesCollection):
e, r, a = pub_era e, r, a = pub_era
# noinspection PyTypeChecker # noinspection PyTypeChecker
pubs.append((e,r,a)) pubs.append((e,r,a))
if diff.value is DELETE: if diff.value is DELETE or hasattr(diff.value, '__dexorder_unload'):
key = d.key2str(diff.key)
if t == DataType.SET: if t == DataType.SET:
sdels[series].add(key) sdels[series].add(key)
elif t == DataType.DICT: elif t == DataType.DICT:
@@ -83,6 +84,8 @@ class RedisState (SeriesCollection):
else: else:
raise NotImplementedError raise NotImplementedError
else: else:
key = d.key2str(diff.key)
value = d.value2str(diff.value)
if t == DataType.SET: if t == DataType.SET:
sadds[series].add(key) sadds[series].add(key)
elif t == DataType.DICT: elif t == DataType.DICT:

View File

@@ -3,6 +3,7 @@ import logging
from dataclasses import dataclass from dataclasses import dataclass
from typing import overload from typing import overload
from dexorder import DELETE
from dexorder.base.chain import current_chain from dexorder.base.chain import current_chain
from dexorder.base.order import OrderKey, TrancheKey from dexorder.base.order import OrderKey, TrancheKey
from dexorder.blockstate import BlockDict, BlockSet from dexorder.blockstate import BlockDict, BlockSet
@@ -61,10 +62,7 @@ class Order:
@staticmethod @staticmethod
def of(a, b=None) -> 'Order': def of(a, b=None) -> 'Order':
key = a if b is None else OrderKey(a, b) key = a if b is None else OrderKey(a, b)
try:
return Order.instances[key] return Order.instances[key]
except KeyError:
log.error(f'Could not find {key} among:\n{", ".join(str(k) for k in Order.instances.keys())}')
@staticmethod @staticmethod
@@ -182,10 +180,14 @@ class Order:
except KeyError: except KeyError:
log.warning(f'No vault owner for {k}') log.warning(f'No vault owner for {k}')
return None return None
except AttributeError:
log.error(f'could not dump {v}')
@staticmethod @staticmethod
async def pub_order_fills(k, v): async def pub_order_fills(k, v):
# publish status updates (on placing and completion) to web clients # publish status updates (on placing and completion) to web clients
if v is DELETE:
return None
try: try:
chain_id = current_chain.get().chain_id chain_id = current_chain.get().chain_id
return (f'{chain_id}|{vault_owners[k.vault]}', # publish on the vault owner's channel return (f'{chain_id}|{vault_owners[k.vault]}', # publish on the vault owner's channel