Repository navigation
feat(solana): poll media jobs with a wallet ownership proof, not a payment - #176
Merged
Merged
Conversation
…yment sol.blockrun.ai (rev 00561-ghx, 2026-10-10) accepts x-poll-wallet / x-poll-timestamp / x-poll-signature on the video, audio and image poll routes: an ed25519 signature by the submitting wallet over "blockrun-poll:v1:<kind>:<jobId>:<unix seconds>". solanaPaidAsyncPost now polls with that proof instead of re-signing an SPL payment every 20s. - video: pending polls carry the proof only; the finished job's 402 (body status "completed") is the challenge for the ONE payment, pinned to the submit's amount/recipient/fee payer, then re-sent on the existing payment-signed path. - music/image: paid at submit; polls never sign a payment. - a refused proof (video 400 "Invalid job id", audio/image 404 "Job not found") or a non-completion 402 falls back once to payment-signed polling, logged to stderr. - Base rail and the account rail are unchanged. Signing is node:crypto ed25519 over the session keypair's seed; bs58 (already installed via @blockrun/llm) is now a declared dependency.
… signer that cannot be built is logged
Node 20.19's --experimental-test-module-mocks loader resolves bs58 through its
CJS build, where the default import is { default: codec }, so bs58.encode was
undefined and 13 poll-ownership tests failed on node 20.19 only. The published
ESM bundle was unaffected (checked on 20.19 and 22). Unwrap either shape.
The createPollSigner failure was swallowed, which surfaced as 10s poll
timeouts instead of the TypeError; it now goes to stderr (message only).
The completion 402 set lastStatus to "completed" before anything settled, and the poll classifier read that remembered status: a later 2xx with no status and no receipt (a bodiless 202) returned as the delivered video, so blockrun_video booked the quote and then failed on the missing URL. The classifier now decides on each answer's own status; lastStatus is kept for messages only. Regression test added (red before, green after). Found by codex review.
VickyXAI
force-pushed
the
feat/solana-poll-ownership
branch
from
October 11, 2026 21:58
2a78522 to
5405392
Compare
VickyXAI
added a commit
that referenced
this pull request
Oct 11, 2026
…sions Release notes for #176 (Solana media polls carry a wallet ownership proof instead of a payment signature; one payment at video completion) join the decide skill rewrite: POST api.blockrun.ai/v1/decisions (OpenAI Decisions contract, gpt-6-luna), the only rail, free with a key. Gateway #804 took Decisions off x402, so the skill names no paid route. The legacy /v1/decide shape is still accepted there. Version 0.54.3: 0.54.2 shipped the withdrawal fix (#174).
VickyXAI
added a commit
that referenced
this pull request
Oct 11, 2026
…sions Release notes for #176 (Solana media polls carry a wallet ownership proof instead of a payment signature; one payment at video completion) join the decide skill rewrite: POST api.blockrun.ai/v1/decisions (OpenAI Decisions contract, gpt-6-luna), the only rail, free with a key. Gateway #804 took Decisions off x402, so the skill names no paid route. The legacy /v1/decide shape is still accepted there. Version 0.54.3: 0.54.2 shipped the withdrawal fix (#174).
VickyXAI
added a commit
that referenced
this pull request
Oct 11, 2026
…sions (#172) Release notes for #176 (Solana media polls carry a wallet ownership proof instead of a payment signature; one payment at video completion) join the decide skill rewrite: POST api.blockrun.ai/v1/decisions (OpenAI Decisions contract, gpt-6-luna), the only rail, free with a key. Gateway #804 took Decisions off x402, so the skill names no paid route. The legacy /v1/decide shape is still accepted there. Version 0.54.3: 0.54.2 shipped the withdrawal fix (#174).
VickyXAI
added a commit
that referenced
this pull request
Oct 12, 2026
…ed out, never "no charge" (#177) Every finished video now reaches the payment-signed path on its first paid poll (#176). When that settle times out, sol answers 402 "Payment settlement outcome unknown" with Retry-After: 90 and holds the job's claim; other polls get "settlement in progress on a concurrent poll". The helper ignored Retry-After, spent its 3 re-signs one poll interval apart and threw "no charge was made" inside the hold, while that payment could still land. - Held answers (isSettleHeld, classified on THIS answer's body and reason, never the remembered reason) honour Retry-After, re-sign with a fresh blockhash and do not spend the re-sign budget; the deadline bounds them. A later header-less 402 ("Payment verification failed") still takes the bounded re-sign path. - Once a payment's outcome is open, EVERY exit is a BilledJobError billing "unknown" — the deadline, a 500 poll answer, an unreadable challenge, a funding refusal of the fresh settle — so the tool books the quote and says the job MAY have been charged. - A completion 402 whose challenge cannot be read falls back to payment-signed polling instead of aborting the finished job. Tests: 5 added in test/solana-poll-ownership.test.ts; each is red against the pre-fix solana-402.ts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On the Solana wallet rail, async media polls now carry the x-poll-* ownership proof that sol.blockrun.ai accepts since rev 00561-ghx (blockrun-sol
src/lib/poll-ownership.ts), so a pending job no longer needs a fresh SPL payment signature every 20s.status:"completed"; the client signs one payment from that challenge (pinned to the submit's amount, recipient and fee payer) and re-sends the poll on the existing payment-signed path. Stale-blockhash re-signs, in-flight and give-up handling are unchanged.Invalid job id, audio/image 404Job not found) or returns any 402 that is not the completion challenge, the client switches once to payment-signed polling and logs it to stderr.The MCP owns this polling in
src/utils/solana-402.ts; it does not go through the @blockrun/llm SDK. Signing uses node:crypto ed25519 over the session key's seed.bs58(already installed transitively via @blockrun/llm) is now a declared dependency.Tests: added
test/solana-poll-ownership.test.ts(18 tests: the signature verifies under node:crypto over the exact message, pending polls never call the payment signer, completion makes exactly one payment, music and image never pay, fallback). The legacy suite is pinned topollOwnership:falseas the fallback path, and a Base-unchanged test is invideo-money-path. Each of the four planted defects turned the suite red. typecheck passes, andnpm testpasses 1413/1413.