Skip to content

payments: keep the state a skipped cycle hands back - #78

Closed
chrishobcroft wants to merge 1 commit into
livepeer:mainfrom
chrishobcroft:payment-loop-state-on-skip
Closed

chrishobcroft wants to merge 1 commit into
livepeer:mainfrom
chrishobcroft:payment-loop-state-on-skip

Conversation

@chrishobcroft

Copy link
Copy Markdown

Client half of livepeer/go-livepeer#4107 (fixes livepeer/go-livepeer#4077).

The remote signer is stateless: the balance and its clock travel with the client. When a payment cycle is skipped, the signer answers HTTP 482. With livepeer/go-livepeer#4107 that 482 also carries the advanced, re-signed state. Today LivePaymentSession drops it, so it resends the same balance every cycle, never mints again, and the orchestrator releases the session once its opening credit runs out.

Change

  • SkipPaymentCycle gains an optional state.
  • http.py reads state from the 482 body when there is one (both the async and sync helpers).
  • LivePaymentSession._payment_request stores that state before re-raising.

Against a signer without livepeer/go-livepeer#4107 the 482 body has no state, so nothing changes: the session keeps what it had.

Tests

New TestSkippedCycleState in tests/test_live_payment_session.py: parsing the 482 body, a session that keeps the state from a skip and sends it on the next request, and one that keeps its old state when a skip carries none. The state-keeping test fails without the change to remote_signer.py.

Full suite: 216 passed. The 2 failures in test_stats_pull.py and 2 teardown errors in test_live_runner.py happen identically on main without this change.

A 482 from the remote signer is a completed cycle, not a failed one: a
signer with livepeer/go-livepeer#4077 fixed debits the service consumed
since its last update and returns new state alongside the error. Dropping
it means resending the same balance every cycle, minting nothing, and
being released by the orchestrator once the opening credit runs out.

Read the state off the 482 body when it is present and store it. Signers
that answer with a bare error are unaffected: the caller keeps what it
had.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

State propagation is consistent, legacy behavior is preserved, and focused regression tests cover the change.

0 open findings

What changed in this PR

Preserves signer state returned with skipped payment cycles, preventing live payment sessions from repeatedly sending stale balances.

Changes:

  • Carries optional state through HTTP 482 exceptions in both HTTP helpers.
  • Saves skipped-cycle state while preserving compatibility with older signers.
  • Adds parsing and session-state regression tests.
File Description
tests/​test_live_payment_session.py Tests state parsing, retention, and legacy behavior.
src/​livepeer_gateway/​remote_signer.py Saves returned state before re-raising a skipped cycle.
src/​livepeer_gateway/​http.py Extracts state from HTTP 482 response bodies.
src/​livepeer_gateway/​errors.py Adds optional state to SkipPaymentCycle.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@j0sh

j0sh commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Since livepeer/go-livepeer#4107 is closed, I'm going to close this one too. The state does not change on a skip, and the next request accounts for the elapsed time since the last successful payment. It's not necessary to retain anything here.

@j0sh j0sh closed this Oct 15, 2026
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.

Remote signer: live session pays once and is then released for insufficient balance

3 participants