You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Expected Behavior
When executing a function that calls the discrete log lookup from a running event loop, it should process the dlog lookup and allow the function to continue.
Actual Behavior
the dlog throws with RuntimeError('asyncio.run() cannot be called from a running event loop')
That's weird. Also, for what it's worth, the code isn't quite correct for concurrent execution. Once it acquires the lock, it should make a second check to see if the result is already saved. I found that bug when I was rewriting it in Kotlin.
While you're dorking with dlog.py, you should also consider making it return Optional[int] rather than int as a circuit-breaker against bogus inputs which don't have a result.
When I did this, it caused all sorts of changes downstream, so you may prefer to raise an exception and have appropriate try/catch blocks.
__dlog_cache: Dict[ElementModP, int] = {ONE_MOD_P: 0}
__dlog_max_element=ONE_MOD_P__dlog_max_exponent=0__DLOG_MAX=100_000_000__dlog_lock=asyncio.Lock()
defdiscrete_log(e: ElementModP) ->Optional[int]:
""" Computes the discrete log (base g, mod p) of the given element, with internal caching of results. Should run efficiently when called multiple times when the exponent is at most in the single-digit millions. Performance will degrade if it's much larger. If the exponent is enormous (greater than a hundred million or so), then this function will give up and return `None`, which the caller should check. Note: *this function is thread-safe*. For the best possible performance, pre-compute the discrete log of a number you expect to have the biggest exponent you'll ever see. After that, the cache will be fully loaded, and every call will be nothing more than a dictionary lookup. """global__dlog_cache# no need for mutually exclusive access when reading from the cacheifein__dlog_cache:
return__dlog_cache[e]
else:
returnasyncio.run(__discrete_log_internal(e))
asyncdef__discrete_log_internal(e: ElementModP) ->Optional[int]:
global__dlog_cacheglobal__dlog_max_elementglobal__dlog_max_exponentglobal__dlog_lockasyncwith__dlog_lock:
# We need to check the dict again; it might have changed.ifein__dlog_cache:
return__dlog_cache[e]
g=int_to_p_unchecked(G)
whilee!=__dlog_max_element:
__dlog_max_exponent=__dlog_max_exponent+1if__dlog_max_exponent>=__DLOG_MAX:
log_error(f"Discrete log failure: exponent was > {__DLOG_MAX}")
returnNone__dlog_max_element=mult_p(g, __dlog_max_element)
__dlog_cache[__dlog_max_element] =__dlog_max_exponentreturn__dlog_cache[__dlog_max_element]
ahh good catch. We have a couple of items on the docket to deal with caching state a bit better in the library anyway so I added your suggestion to double check the cache but im going to leave the out of bounds for now.
Activity
danwallach commentedon Jul 21, 2021
That's weird. Also, for what it's worth, the code isn't quite correct for concurrent execution. Once it acquires the lock, it should make a second check to see if the result is already saved. I found that bug when I was rewriting it in Kotlin.
While you're dorking with dlog.py, you should also consider making it return
Optional[int]rather thanintas a circuit-breaker against bogus inputs which don't have a result.When I did this, it caused all sorts of changes downstream, so you may prefer to raise an exception and have appropriate try/catch blocks.
AddressXception commentedon Jul 23, 2021
ahh good catch. We have a couple of items on the docket to deal with caching state a bit better in the library anyway so I added your suggestion to double check the cache but im going to leave the out of bounds for now.