Skip to content

fix: check node-tx timelock exhaustion when refund-tx is healthy in checkRenewLeaves - #131

Open
vwinee21 wants to merge 3 commits into
buildonspark:mainfrom
vwinee21:fix/check-renew-leaves-node-tx-exhaustion
Open

fix: check node-tx timelock exhaustion when refund-tx is healthy in checkRenewLeaves#131
vwinee21 wants to merge 3 commits into
buildonspark:mainfrom
vwinee21:fix/check-renew-leaves-node-tx-exhaustion

Conversation

@vwinee21

Copy link
Copy Markdown

Problem

Fixes #127

checkRenewLeaves gates the entire renewal classification on the refund-tx sequence. When refundSequence does not need renewal, the node is immediately pushed to validNodes — without ever checking whether nodeSequence has decayed to ≤ TimeLockInterval.

Result: a leaf whose node-tx timelock is exhausted but whose refund-tx timelock is still healthy is classified as Valid and selected for transfers. The SSP then attempts to construct the next CPFP refund and ValidateSequence rejects with:

current timelock 100 (rounded from 100) in CPFP refund transaction is too small
to subtract TimeLockInterval 100 without reaching zero

The leaf is structurally unspendable but checkRenewLeaves returns 0 renewals on every preflight — no ceremony attempted, no warn logs, leaf stays stuck indefinitely.

Fix

In the else branch (refund-tx healthy), add an explicit check for node-tx exhaustion before classifying the node as valid:

} else {
  if (doesTxnNeedRenewed(nodeSequence)) {
    nodesToRenewNodeTxn.push(node);
    nodeIds.push(node.id);
  } else {
    validNodes.push(node);
  }
}

This mirrors the existing logic inside the if (doesTxnNeedRenewed(refundSequence)) branch and ensures node-tx exhaustion is caught regardless of refund-tx health.

vwinee21 added 3 commits May 13, 2026 08:00
Replace fragile prefix matching with proper btcutil.DecodeAddress
validation which includes checksum verification.

Resolves TODO in IsBitcoinAddressForNetwork.
Pending coop exits with ConfirmationHeightIsNil() can accumulate
indefinitely if the L1 transaction is never confirmed (e.g. evicted
from mempool due to low fee or replaced via RBF).

Add expiry logic that runs each block cycle and deletes pending coop
exits older than KnobWatchChainCoopExitPendingExpiryDays (default: 14
days). The 14-day default matches Bitcoin Core's DEFAULT_MEMPOOL_EXPIRY
so entries are cleaned up around the time Bitcoin Core would drop them.

Expiry count is logged at Info level when non-zero for observability.
The threshold is runtime-configurable via the knob system without a
code deploy.
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.

checkRenewLeaves skips renewal when node-tx timelock is exhausted but refund-tx timelock is healthy

1 participant