Skip to content

Track the rollups-node JSON-RPC API changes - #161

Merged
tuler merged 1 commit into
prerelease/v2-alphafrom
claude/jsonrpc-api-changes-cartesi-xnjga6
Aug 24, 2026
Merged

Track the rollups-node JSON-RPC API changes#161
tuler merged 1 commit into
prerelease/v2-alphafrom
claude/jsonrpc-api-changes-cartesi-xnjga6

Conversation

@tuler

@tuler tuler commented Aug 14, 2026

Copy link
Copy Markdown
Member

Mirrors the JSON-RPC API changes of the Cartesi node in @cartesi/rpc, and bubbles them down to @cartesi/client and @cartesi/react.

This started as a forward-looking port of cartesi/rollups-node#793, which has since merged into next/2.0 as 7558af6, so what follows is no longer provisional. It was re-verified against the merged specification rather than against the pull request diff: @cartesi/rpc declares exactly the 28 methods the merged jsonrpc-discover.json defines — no gaps, no leftovers — and every schema and parameter change between the pre-793 base (9bdd988) and merged next/2.0 is reflected here. Cross-checked against internal/jsonrpc/api/params.go, internal/jsonrpc/jsonrpc.go and internal/model/models.go.

New methods

Node method @cartesi/client @cartesi/react
cartesi_getEpochByVirtualIndex getEpochByVirtualIndex useEpochByVirtualIndex
cartesi_getExecutedOutputCount getExecutedOutputCount useExecutedOutputCount
cartesi_getPendingExecutableOutputCount getPendingExecutableOutputCount usePendingExecutableOutputCount
cartesi_getNodeInfo getNodeInfo useNodeInfo

getNodeInfo returns the chain id, the semantic node version and the node's default block tag in a single call. It replaces getChainId and getNodeVersion, which the node deprecated and which are now marked @deprecated here too — they still work. defaultBlock is documented as the node's finality contract: everything the node exposes carries that tag's stability guarantees.

New listing filters

  • from/to, an inclusive index range, on listEpochs, listInputs, listOutputs and listReports.
  • listEpochs takes a list of statuses (status?: EpochStatus | NonEmptyArray<EpochStatus>).
  • listOutputs takes a list of output types (outputType?: OutputType | NonEmptyArray<OutputType>) and a new executed?: boolean filter.

The node rejects an empty filter list with invalid params, so both list-valued filters use a new NonEmptyArray<T> = [T, ...T[]] type exported by @cartesi/rpc and re-exported by @cartesi/client: status: [] and outputType: [] are compile errors rather than failed requests. Note this is stricter than a plain array in both directions — it also rejects an EpochStatus[]-typed variable, so callers building lists dynamically should type them as NonEmptyArray<EpochStatus>.

Input completion changes

These two landed on next/2.0 alongside — but not as part of — #793. Now that #793 has merged there is a single node API, and leaving these out would mean shipping a client that promises statuses the node cannot return and drops a field it does return.

  • InputStatus lost its resource-limit members. The node collapsed OUTPUTS_LIMIT_EXCEEDED, REPORTS_LIMIT_EXCEEDED, CYCLE_LIMIT_EXCEEDED, TIME_LIMIT_EXCEEDED and PAYLOAD_LENGTH_LIMIT_EXCEEDED into the remaining outcomes, so the union is now NONE | ACCEPTED | REJECTED | EXCEPTION | MACHINE_HALTED. Code switching on a removed member no longer compiles.
  • Input gained exceptionData: Hex | null (exception_data on the wire) — the raw guest-provided CMIO exception payload, non-null only when status is EXCEPTION, with an empty payload encoded as 0x. The bytes are passed through undecoded, the same treatment rawData gets.

waitForInput with rejectErrors listed four of the removed statuses; it now aborts on EXCEPTION, MACHINE_HALTED and REJECTED, which is every terminal status other than ACCEPTED. That also closes a pre-existing gap: the old list omitted REPORTS_LIMIT_EXCEEDED, so an input failing that way slipped through the guard.

Synchronization patterns

These are the node's documented patterns, mirrored in the JSDoc and docs pages of all three packages:

  • Epochs — keep discovery and refresh separate: advance from to the next unseen epoch index to discover new epochs, and refresh the epochs already seen by filtering them on the non-terminal statuses. Terminal statuses never regress, so a settled epoch leaves the refresh set for good.
  • Output executions — poll getExecutedOutputCount (monotone) and, when it changes, re-query the bounded executable-output working set with executed: false and outputType: ["Voucher", "DelegateCallVoucher"], then diff that pending set against the previous result to identify the executions. getPendingExecutableOutputCount is a gauge and must not be used for change detection.

No resume cursor over the executed filter is sound — not an output index, a pagination offset, or the executed count — because executions are observed out of output-index order. The node documents a race-free execution cursor as expected in a future ingestion API.

Breaking changes

  • cartesi_getMatchAdvanced was renamed to cartesi_getMatchAdvance, following the node. The action is now getMatchAdvance, the hook is useMatchAdvance (with matchAdvanceOptions / matchAdvanceQueryKey), and GetMatchAdvancedParams / GetMatchAdvancedReturnType are now GetMatchAdvanceParams / GetMatchAdvanceReturnType. No back-compat aliases were kept — these packages are on 2.0.0-alpha prereleases and the node made the same break. The entity type MatchAdvanced keeps its name, matching the node's MatchAdvancedGetResult schema.
  • The node's application-level error codes moved out of the JSON-RPC reserved range: application not found is now -31002 (was -32002) and resource not found is now -31001 (was -32001).
  • InputStatus shrank, as described above.

Error codes and batch limits

@cartesi/rpc now exports the node's codes as errorCodes, including the new batch (-32040), timeout (-32070), response-size-limit (-31003) and batch-list-work (-31004) ones, alongside the constants that bound a batch: maxBatchSize (100), maxBatchListWork (10 000) and defaultListLimit (50).

Two budgets apply to a batch beyond its entry count:

  • Response size — 10 MB per HTTP request, cumulative across a batch. An entry whose response would exceed the remaining budget is discarded without consuming it and gets -31003; the budget then closes, so every later entry gets -31003 too, even one that would still have fit.
  • List work — before dispatching anything, the node sums the effective limit of every list entry, counting an omitted or zero limit as defaultListLimit and capping each entry at maxBatchListWork. A total above maxBatchListWork rejects the whole batch with a single -31004 and dispatches nothing. Being raised before dispatch, -31004 is a batch-level error and is not attributed to any one method.

Neither budget meters the COUNT queries behind pagination or offset traversal, so a deep offset over a broad filter can still make the database scan and discard rows before the requested page. offset itself is bounded to the signed 64-bit range — far above Number.MAX_SAFE_INTEGER, so only a deliberately out-of-range value is rejected.

Notes

  • Batch requests needed no transport change — the underlying json-rpc-2.0 client already batches — so they are documented rather than implemented.
  • Upstream gave the two output counts their own result schemas, but both are { data: UnsignedInteger }, which the separate return types here already matched. No wire-shape change.
  • The node's repository layer was fixed to treat an empty output-type list as no filter (avoiding invalid SQL). That sits below the JSON-RPC handler, which still rejects empty status and output_type lists with invalid params, so NonEmptyArray<T> continues to match node behavior.
  • Two OpenRPC typing fixes needed no code change: prev_randao and voucher value are now UnsignedInteger256 in the spec. Both were already 256-bit-safe here — prev_randao is Hex on the wire converted with hexToBigInt, and value is a HexNumber.
  • chainId in NodeInfo is a number, matching what the existing getChainId action returns.
  • listOutputs maps output types to selectors through the head of the list separately, because Array.prototype.map would widen the tuple back to Hex[]. That keeps non-emptiness proven rather than asserted.
  • Error message wording changed on the node in the final commits before merge ("invalid request" split into "Invalid Request", "Invalid request ID", "Unsupported JSON-RPC version", "Parse error"). Nothing here matches on message text — @cartesi/rpc exports codes — so this is not tracked.
  • Beyond the API tracking, four @cartesi/react hooks (useApplication, useCommitments, useTournaments, useWithdrawals) were already failing biome check on the base branch for import formatting; this branch lets biome fix them so pnpm lint passes.

Testing

  • pnpm lint and pnpm check-types clean across the workspace.
  • @cartesi/client (12), @cartesi/react (122), @cartesi/codec (70) and @cartesi/rollup (24) suites pass. Coverage added for the new nodeInfoConverter, the renamed match-advance query keys, the stringification of the new from/to bigints in every listing query key, and an EXCEPTION input carrying a non-null exceptionData.
  • The empty-list constraint is guarded by a __tests__/params.test-d.ts type suite, which required enabling vitest type testing in @cartesi/client — CI runs pnpm test but no tsc --noEmit over test files, so without it a regression would go unnoticed. Verified non-vacuous: reverting the type to EpochStatus[] makes the suite fail, including Unused '@ts-expect-error' directive. It earned its keep on the exception_data change too, failing the build on a now-incomplete Input fixture that a value-only run would have accepted.
  • The vocs docs site builds with no dead links.
  • @cartesi/machine's suite is not run here — it needs an installed cartesi-machine emulator — and is untouched by this change.

A changeset marking all three packages major is included.

@changeset-bot

changeset-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3d6c2bc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@cartesi/rpc Major
@cartesi/client Major
@cartesi/react Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@tuler
tuler changed the base branch from main to prerelease/v2-alpha August 14, 2026 16:01
@brunomenezes brunomenezes moved this to 🧑‍💻 In Progress in Rollups Tooling Aug 19, 2026
@tuler
tuler force-pushed the claude/jsonrpc-api-changes-cartesi-xnjga6 branch from 0760176 to 0041415 Compare August 24, 2026 18:58
Mirror the JSON-RPC API changes of the Cartesi node in @cartesi/rpc, and
bubble them down to @cartesi/client and @cartesi/react.

Derived from cartesi/rollups-node#793, which has since merged into
next/2.0 as 7558af6, so this is no longer a forward-looking port. It was
verified against the merged jsonrpc-discover.json rather than the pull
request diff: @cartesi/rpc declares exactly the 28 methods the merged
specification defines, and every schema and parameter change between the
pre-793 base (9bdd988) and merged next/2.0 is reflected here.
Cross-checked against internal/jsonrpc/api/params.go,
internal/jsonrpc/jsonrpc.go and internal/model/models.go.

New methods:

- cartesi_getEpochByVirtualIndex, fetching an epoch by its dense
  insertion rank (getEpochByVirtualIndex / useEpochByVirtualIndex)
- cartesi_getExecutedOutputCount and
  cartesi_getPendingExecutableOutputCount (getExecutedOutputCount /
  useExecutedOutputCount, getPendingExecutableOutputCount /
  usePendingExecutableOutputCount). The executed count is monotone and
  meant to be polled for change detection; the pending count is a gauge
  and is not.
- cartesi_getNodeInfo, returning the chain id, the node version and the
  node's default block tag in one call (getNodeInfo / useNodeInfo). It
  replaces cartesi_getChainId and cartesi_getNodeVersion, which the node
  deprecated and which are now marked @deprecated here too.

New listing filters:

- from/to inclusive index ranges on listEpochs, listInputs, listOutputs
  and listReports
- a list of statuses on listEpochs, and a list of output types plus the
  new executed flag on listOutputs

The node rejects an empty status or output_type list with invalid
params, so the list-valued filters are typed as NonEmptyArray<T> rather
than T[]: `status: []` and `outputType: []` are compile errors instead
of failed requests. listOutputs maps the output types to selectors
through the head of the list separately, so the result stays non-empty
for the type checker, which Array.prototype.map would widen back to
Hex[]. The constraint is guarded by a *.test-d.ts suite in
@cartesi/client, which needed vitest type testing enabled there — CI
runs `pnpm test` but no `tsc --noEmit` over the test files, so without
it the constraint would go unchecked.

Input completion changes, which landed on next/2.0 alongside but not as
part of #793:

- InputStatus loses its resource-limit members. The node collapsed
  OUTPUTS_LIMIT_EXCEEDED, REPORTS_LIMIT_EXCEEDED, CYCLE_LIMIT_EXCEEDED,
  TIME_LIMIT_EXCEEDED and PAYLOAD_LENGTH_LIMIT_EXCEEDED into the
  remaining outcomes, leaving NONE, ACCEPTED, REJECTED, EXCEPTION and
  MACHINE_HALTED. waitForInput listed four of them under rejectErrors;
  it now aborts on EXCEPTION, MACHINE_HALTED and REJECTED, which is
  every terminal status other than ACCEPTED — and no longer omits a
  failure status the way the old list omitted REPORTS_LIMIT_EXCEEDED.
- Input gains exception_data / exceptionData, the raw guest-provided
  CMIO exception payload, non-null only when the status is EXCEPTION and
  empty-encoded as 0x. The bytes are passed through undecoded, matching
  how raw_data is handled.

Breaking changes:

- cartesi_getMatchAdvanced is now cartesi_getMatchAdvance, so the
  getMatchAdvanced action is getMatchAdvance, the useMatchAdvanced hook
  is useMatchAdvance and the GetMatchAdvanced* types are GetMatchAdvance*
- the node's application-level error codes moved out of the JSON-RPC
  reserved range (-31001/-31002 instead of -32001/-32002); they are now
  exported from @cartesi/rpc as errorCodes, along with the new batch
  (-32040), timeout (-32070), response-size-limit (-31003) and
  batch-list-work (-31004) codes, plus the maxBatchSize (100),
  maxBatchListWork (10000) and defaultListLimit (50) constants that
  bound a batch
- InputStatus shrank, as described above

Batch requests needed no transport change — the underlying json-rpc-2.0
client already batches — so they are documented rather than implemented,
including the two budgets that apply beyond the entry count: the
response-size budget closes once exhausted, so every later entry gets
-31003 even if its response would still have fit, and the list-work
budget rejects the whole batch with a single -31004 before dispatching
anything. Neither meters the COUNT queries behind pagination nor offset
traversal, so a deep offset over a broad filter can still make the
database scan and discard rows before the requested page.

Two OpenRPC typing fixes needed no code change: prev_randao and voucher
value are now UnsignedInteger256 in the specification, and both were
already 256-bit-safe here.

Also reformats four @cartesi/react hooks that biome was already
reporting as unformatted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UJpa3KXxvUarBGpHvW8Qdb
@tuler
tuler force-pushed the claude/jsonrpc-api-changes-cartesi-xnjga6 branch from 0041415 to 3d6c2bc Compare August 24, 2026 19:01
@tuler
tuler marked this pull request as ready for review August 24, 2026 19:03
@tuler tuler changed the title feat: track the rollups-node JSON-RPC API changes Track the rollups-node JSON-RPC API changes Aug 24, 2026
@tuler
tuler merged commit 07e4357 into prerelease/v2-alpha Aug 24, 2026
3 checks passed
@tuler
tuler deleted the claude/jsonrpc-api-changes-cartesi-xnjga6 branch August 24, 2026 19:32
@github-project-automation github-project-automation Bot moved this from 🧑‍💻 In Progress to 📦 Done in Rollups Tooling Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 📦 Done

Development

Successfully merging this pull request may close these issues.

3 participants