Skip to content

fix(magma): add timeout and validation to get_fast_fee() - #42

Merged
TrezorHannes merged 1 commit into
TrezorHannes:mainfrom
Wired4ncer:fix/get-fast-fee-timeout
Aug 24, 2026
Merged

fix(magma): add timeout and validation to get_fast_fee()#42
TrezorHannes merged 1 commit into
TrezorHannes:mainfrom
Wired4ncer:fix/get-fast-fee-timeout

Conversation

@Wired4ncer

Copy link
Copy Markdown
Contributor

Problem

get_fast_fee() is the only requests call in magma_sale_process.py without a timeout= — the other six call sites set one. requests applies no default timeout, so against an unresponsive endpoint (a dropped connection rather than a refused one) it blocks indefinitely.

The consequence extends past the function. The main loop is:

while True:
    schedule.run_pending()
    time.sleep(1)

schedule.run_pending() executes due jobs synchronously in the calling thread, so a blocked get_fast_fee() inside execute_bot_behavior stops the loop and every other scheduled job with it — including check_pending_confirmations_timeouts, which is what keeps auto-approval responsive.

It is also hard to notice: the process never exits, so a Restart=always systemd unit never fires, and the Telegram poller runs in its own daemon thread, so the bot keeps answering commands while the scheduler is stopped.

Two smaller issues in the same function: there is no raise_for_status(), so an HTML error page reaches .json(); and data["fastestFee"] raises KeyError if the response shape changes.

Change

  • timeout=MEMPOOL_API_TIMEOUT_SECONDS (15s)
  • raise_for_status()
  • .get("fastestFee") instead of direct indexing
  • reject non-numeric, boolean, zero and negative values
  • fail closed: return None on any failure

Caller impact

None. open_channel() already treats None as "Fee rate could not be determined" and returns before opening a channel, so the abort path already exists and is unchanged.

get_fast_fee() was the only requests call in magma_sale_process.py without a
timeout; the other six call sites set one. requests applies no default timeout,
so against an unresponsive endpoint it blocks indefinitely.

Because schedule.run_pending() executes due jobs synchronously in the calling
thread, a block there stops the main loop and every other scheduled job with
it, including check_pending_confirmations_timeouts.

Also adds raise_for_status(), replaces direct ["fastestFee"] indexing with
.get(), and rejects non-numeric, boolean, zero and negative values. Fails
closed by returning None, which open_channel() already handles as "Fee rate
could not be determined" before opening a channel.

@TrezorHannes TrezorHannes left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #42 (fix(magma): add timeout and validation to get_fast_fee)

Summary of Findings

  • Security & Reliability (P0/P1): Zero critical or high issues. The PR directly fixes a socket hang vulnerability where an unresponsive mempool API call would synchronously stall the single-threaded schedule.run_pending() loop indefinitely.
  • Error Handling & Status Codes: Correctly introduces timeout=15, raise_for_status() to protect against HTML error bodies, and safe .get("fastestFee") dictionary lookups.
  • Validation: Strict type validation correctly guards against booleans (since in Python isinstance(True, int) evaluates to True), non-numerics, zero, and negative values.
  • Fails Closed: Returns None on any failure, which open_channel() handles cleanly without opening a channel at undefined fee rates.

Recommendation

LGTM / Approved. Clean, defensive, and ready to merge.

@TrezorHannes
TrezorHannes merged commit fa8f69f into TrezorHannes:main Aug 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants