Repository navigation
Tags
Tags
feat(rpcclient): support Starknet v0.9+ websocket subscriptions (#2318) * feat: update starknet spec for Starknet 0.14.3 / JSON-RPC v0.10 Starknet 0.14.3 (and pathfinder v0.23.0, released 2026-07-20) removed JSON-RPC v0.8 and older; nodes now serve v0.9 (root default) and v0.10 (final v0.10.1 spec on the v0_10 routes). Update STRK and STRKS specs accordingly: - add /rpc/v0_10 and /ws/rpc/v0_10 collections (inheritance-only, mirroring the v0_9 pair) - remove the /rpc/v0_8 and /ws/rpc/v0_8 collections (RPC 0.8 removed by Starknet 0.14.3; pathfinder no longer serves these endpoints) - add missing spec methods: starknet_getBlockWithReceipts, starknet_getCompiledCasm, starknet_getMessagesStatus, starknet_getStorageProof - remove starknet_pendingTransactions (dropped from the RPC spec; no served version supports it) - replace the legacy pathfinder_subscribe/pathfinder_unsubscribe WS API (removed in pathfinder 0.18.0) with the spec WS subscription family: starknet_subscribeNewHeads/Events/TransactionStatus/NewTransactions/ NewTransactionReceipts + starknet_unsubscribe, one SUBSCRIBE parse directive per method (solana-style) - pathfinder extension collection: drop pathfinder_getProof and pathfinder_getTransactionStatus (removed in pathfinder 0.17.0, superseded by starknet_getStorageProof/starknet_getTransactionStatus), add pathfinder_lastL1AcceptedBlockHashAndNumber - average_block_time 30000/32000 -> 2000 for Starknet 0.14.x fast blocks (matches base/optimism convention for ~2s chains) Update TestJsonRpcInternalPathsMultipleVersionsStarkNet to the new path set and refresh the starknet provider example configs (v0_9/v0_10). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MC6w48eT5tW9K8iUNCm7PG * feat: support starknet v0.9+ ws subscription envelope in rpcclient The starknet_subscribe* APIs added to the spec need runtime support the node ws client lacked: - notifications use methods named starknet_subscriptionXxx with params {subscription_id, result}; none of the existing dispatch shapes (ethereum "_subscription" suffix, solana "Notification" suffix, legacy pathfinder top-level result) matched, so they were silently dropped. Add a dedicated predicate and handler keyed by params.subscription_id. - Client.Subscribe rejected the canonical starknet subscribe forms: by-name object params without a tendermint "query" key errored, and omitted params hit "unknown parameters type". Make "query" optional (tendermint-only) and accept nil params. Also apply spec calibration from review: starknet_estimateFee block_id positional index 1 -> 2 (v0.9/v0.10 params are [request, simulation_flags, block_id]) and allowed_block_lag_for_qos_sync -> 5, matching the base/optimism convention for ~2s block chains now that average_block_time is 2000. Adds predicate unit tests and an end-to-end mock-node websocket test covering object params, string subscription ids, envelope delivery and unknown-id drops. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MC6w48eT5tW9K8iUNCm7PG * fix: order subscription registration before unblocking Subscribe caller handleResponse sent op.resp <- msg before writing op.err and registering the subscription in clientSubs, racing with requestOp.wait which reads op.err immediately after receiving. On an error response the caller could observe a stale nil error and treat a failed subscription as successful. The race was previously unreachable in tests; the new starknet subscription end-to-end test tripped it deterministically under -race. Move all op.err writes and subscription registration before the op.resp send for subscription responses (matching upstream go-ethereum's ordering) while preserving this fork's behavior of returning the raw response message to the caller. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MC6w48eT5tW9K8iUNCm7PG * chore: move Starknet spec changes to their own PR Restore the STRK/STRKS spec, the Starknet provider examples and the Starknet internal-path test to main. They move to the ofir/starknet-spec-v010 PR, which must merge only after this code is released and providers have upgraded. This PR keeps the rpcclient runtime support. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * fix(consumer): match by-name unsubscribe params to subscriptions The consumer only matched an unsubscribe to its subscription when the id came positionally (["0x1"]). starknet.js sends starknet_unsubscribe by name ({"subscription_id": "0x1"}), which got "subscription not found" and left the subscription open until the websocket closed. When the raw params match nothing and they are a single-entry object with a string value, retry with that value. The raw match still runs first, so tendermint (whose id is the whole params object) is unchanged. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * fix(rpcclient): reject non-string tendermint query param Subscribe stopped erroring when by-name params carried a "query" that was not a string. That subscription then registered under the response id, while tendermint notifications are routed by query, so they were all dropped silently. Error again when "query" is present but not a string. When "query" is absent (starknet by-name params), the id still comes from the response. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * test(rpcclient): use TestComponent_Scenario names for Starknet tests Rename TestIsStarknetNotification and TestStarknetSubscriptionEndToEnd to TestStarknetNotification_Detection and TestStarknetSubscription_EndToEnd, per the repo's test naming convention. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * chore: bump protocol TARGET_VERSION to 6.2.9 Gives the release with the Starknet v0.9+ subscription support its own protocol version, so ProviderMin/ProviderTarget can require it before the Starknet spec (#2341) goes live. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ignacio Rodriguez <ignacio.rsg@gmail.com>
feat(rpcclient): support Starknet v0.9+ websocket subscriptions (#2318) * feat: update starknet spec for Starknet 0.14.3 / JSON-RPC v0.10 Starknet 0.14.3 (and pathfinder v0.23.0, released 2026-07-20) removed JSON-RPC v0.8 and older; nodes now serve v0.9 (root default) and v0.10 (final v0.10.1 spec on the v0_10 routes). Update STRK and STRKS specs accordingly: - add /rpc/v0_10 and /ws/rpc/v0_10 collections (inheritance-only, mirroring the v0_9 pair) - remove the /rpc/v0_8 and /ws/rpc/v0_8 collections (RPC 0.8 removed by Starknet 0.14.3; pathfinder no longer serves these endpoints) - add missing spec methods: starknet_getBlockWithReceipts, starknet_getCompiledCasm, starknet_getMessagesStatus, starknet_getStorageProof - remove starknet_pendingTransactions (dropped from the RPC spec; no served version supports it) - replace the legacy pathfinder_subscribe/pathfinder_unsubscribe WS API (removed in pathfinder 0.18.0) with the spec WS subscription family: starknet_subscribeNewHeads/Events/TransactionStatus/NewTransactions/ NewTransactionReceipts + starknet_unsubscribe, one SUBSCRIBE parse directive per method (solana-style) - pathfinder extension collection: drop pathfinder_getProof and pathfinder_getTransactionStatus (removed in pathfinder 0.17.0, superseded by starknet_getStorageProof/starknet_getTransactionStatus), add pathfinder_lastL1AcceptedBlockHashAndNumber - average_block_time 30000/32000 -> 2000 for Starknet 0.14.x fast blocks (matches base/optimism convention for ~2s chains) Update TestJsonRpcInternalPathsMultipleVersionsStarkNet to the new path set and refresh the starknet provider example configs (v0_9/v0_10). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MC6w48eT5tW9K8iUNCm7PG * feat: support starknet v0.9+ ws subscription envelope in rpcclient The starknet_subscribe* APIs added to the spec need runtime support the node ws client lacked: - notifications use methods named starknet_subscriptionXxx with params {subscription_id, result}; none of the existing dispatch shapes (ethereum "_subscription" suffix, solana "Notification" suffix, legacy pathfinder top-level result) matched, so they were silently dropped. Add a dedicated predicate and handler keyed by params.subscription_id. - Client.Subscribe rejected the canonical starknet subscribe forms: by-name object params without a tendermint "query" key errored, and omitted params hit "unknown parameters type". Make "query" optional (tendermint-only) and accept nil params. Also apply spec calibration from review: starknet_estimateFee block_id positional index 1 -> 2 (v0.9/v0.10 params are [request, simulation_flags, block_id]) and allowed_block_lag_for_qos_sync -> 5, matching the base/optimism convention for ~2s block chains now that average_block_time is 2000. Adds predicate unit tests and an end-to-end mock-node websocket test covering object params, string subscription ids, envelope delivery and unknown-id drops. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MC6w48eT5tW9K8iUNCm7PG * fix: order subscription registration before unblocking Subscribe caller handleResponse sent op.resp <- msg before writing op.err and registering the subscription in clientSubs, racing with requestOp.wait which reads op.err immediately after receiving. On an error response the caller could observe a stale nil error and treat a failed subscription as successful. The race was previously unreachable in tests; the new starknet subscription end-to-end test tripped it deterministically under -race. Move all op.err writes and subscription registration before the op.resp send for subscription responses (matching upstream go-ethereum's ordering) while preserving this fork's behavior of returning the raw response message to the caller. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MC6w48eT5tW9K8iUNCm7PG * chore: move Starknet spec changes to their own PR Restore the STRK/STRKS spec, the Starknet provider examples and the Starknet internal-path test to main. They move to the ofir/starknet-spec-v010 PR, which must merge only after this code is released and providers have upgraded. This PR keeps the rpcclient runtime support. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * fix(consumer): match by-name unsubscribe params to subscriptions The consumer only matched an unsubscribe to its subscription when the id came positionally (["0x1"]). starknet.js sends starknet_unsubscribe by name ({"subscription_id": "0x1"}), which got "subscription not found" and left the subscription open until the websocket closed. When the raw params match nothing and they are a single-entry object with a string value, retry with that value. The raw match still runs first, so tendermint (whose id is the whole params object) is unchanged. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * fix(rpcclient): reject non-string tendermint query param Subscribe stopped erroring when by-name params carried a "query" that was not a string. That subscription then registered under the response id, while tendermint notifications are routed by query, so they were all dropped silently. Error again when "query" is present but not a string. When "query" is absent (starknet by-name params), the id still comes from the response. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * test(rpcclient): use TestComponent_Scenario names for Starknet tests Rename TestIsStarknetNotification and TestStarknetSubscriptionEndToEnd to TestStarknetNotification_Detection and TestStarknetSubscription_EndToEnd, per the repo's test naming convention. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 * chore: bump protocol TARGET_VERSION to 6.2.9 Gives the release with the Starknet v0.9+ subscription support its own protocol version, so ProviderMin/ProviderTarget can require it before the Starknet spec (#2341) goes live. Claude-Session: https://claude.ai/code/session_01NDkG48rHipQHQWQgcAGKM9 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ignacio Rodriguez <ignacio.rsg@gmail.com>
fix(specs): move Cardano mempool and tx-evaluate routes to addons (#2339 ) The CARDANO spec (inherited by CARDANOT) declared five Blockfrost routes in the mandatory base collections that the self-hosted blockfrost-backend-ryo does not implement: GET /mempool GET /mempool/{hash} GET /mempool/addresses/{address} POST /utils/txs/evaluate POST /utils/txs/evaluate/utxos Confirmed absent from the RYO route tree on master: there is no mempool route directory, and utils holds only addresses/xpub. Serving the mempool needs a live LocalTxMonitor subscription to the node, and evaluation needs a Plutus evaluator; RYO reads confirmed data from its chain index, so both are capability gaps rather than a misconfiguration. The hosted Blockfrost service serves all five. A RYO-backed provider still stakes successfully, because the stake check validates whole collections rather than individual APIs, but every relay to those five routes fails with HTTP 400 and costs it QoS. Move them into a "mempool" GET addon and a "txs-evaluate" POST addon. All 116 API definitions are relocated verbatim: same compute units, categories and block parsing. A provider opts in per node-url and in its stake entry; one that cannot serve them simply omits them and is no longer measured on them. CARDANOT needs no change of its own, since spec expansion carries over parent collections the child does not declare. Addon collections deliberately carry no headers block. Headers register per connection type rather than per addon, so the base project_id header already applies, matching the ETH1 and ETHBEACON addons. No "enabled" verification is defined for either addon yet. An empty mempool is normal, so a naive PARSE_BY_ARG probe of GET /mempool would fail on an idle chain and jail honest providers, and a POST evaluate probe would need a valid CBOR transaction as a static template. Left for a follow-up rather than shipping a flaky verification. Consumers reach an addon only when their plan policy asks for it, so add CARDANO and CARDANOT chain policies to the gateway plan requiring both, with mixed:true so pairing still returns providers that serve the base collection alone. This must be proposed after the spec proposal passes; ValidatePlanFields rejects a policy naming an addon the spec lacks. The four plans carrying an empty chain_policies list are left alone: adding a single entry to those would make every other chain unreachable for them, which is a separate decision. Closes #2333 Claude-Session: https://claude.ai/code/session_0174ZD9merkC3nHPA3KbXLMh
refactor: replace custom compression with native gRPC compression (#2328 ) * refactor(provider): serve native gRPC instead of the grpc-web wrapper The provider listener wrapped its gRPC server in grpc-web and h2c so that browser based lava-sdk clients could reach it. The SDK is gone, so the wrapper only costs us the supported grpc.Server.Serve path: native connection management, graceful stop, and no protocol translation. The listener now runs a real grpc.Server, with cmux routing connections by the protocol they open with: the HTTP/2 client preface goes to gRPC, everything else to the HTTP/1.1 health endpoint that kubernetes httpGet probes use. Matching the preface is a cheap prefix compare and resolves at connection time, unlike matching the gRPC content-type which would have to wait for the first request. The gRPC health checking protocol is registered alongside it for native clients and load balancers, and Shutdown now drains both servers, reports NOT_SERVING while draining, and releases the port. When TLS is enabled the listener advertises ALPN explicitly. gRPC clients offer only "h2" and refuse a connection that negotiated no protocol at all, so terminating TLS without advertising it would have broken every consumer of a TLS enabled provider. "http/1.1" is listed first because ALPN resolves in server preference order, which keeps HTTP health probes on HTTP/1.1 while gRPC clients still get "h2". Relay compression is left to gRPC as well. The consumer already dials with grpc.UseCompressor(gzip) when compression is enabled, and the server echoes that compressor back on the response, so the hand rolled compress/decompress pass and its lava-compression headers were compressing already compressed payloads. The flag is renamed to --enable-grpc-compression accordingly, with the old name still accepted so existing deployments keep working. The gzip helpers stay for the cache, which compresses entries at rest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ka44Dzff68tLrAQ5SJPmaC * refactor(provider): drop the grpc-web CORS check from provider testing PerformCORSCheck required every provider to answer an HTTPS request on any path with Access-Control-Allow-Origin and an Access-Control-Allow-Headers list containing x-grpc-web. Those headers existed only so browsers could preflight grpc-web calls, and the provider listener no longer speaks grpc-web, so the check now fails against every provider. It is a hard failure in both callers: lavap test rpcprovider reports "invalid CORS check" for every endpoint, and the health monitor marks every provider unhealthy. Nothing else is lost by removing it, both callers probe the endpoint over gRPC first, and TLS reachability is already covered by ConnectRawClientWithTimeout and its insecure retry. The cors_test.go scaffolding went with it, including the self signed certificate it wrote into the package directory on every run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ka44Dzff68tLrAQ5SJPmaC --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
chore: bump protocol TARGET_VERSION to 6.2.5 (#2329) The x/protocol TARGET_VERSION constant was left at 6.1.0 while the repo released v6.2.0 through v6.2.4, so lavap self-reported 6.1.0 in its version output, relay metadata and protocol version metrics. The constant is compiled into the binary via upgrade.GetCurrentVersion, so the already-published v6.2.4 artifacts cannot be corrected in place. Target 6.2.5 instead, to be shipped by the next tag. Document the constants while here, so the next release does not miss the manual bump. Claude-Session: https://claude.ai/code/session_0117V1emoRbzp5M19LFjnVs3 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
fix: improve handleConsistency to wait for tracker polls instead of b… …ailing (#2326) * fix(provider): gate consistency bail on tracker poll time, not block production handleConsistency's early bail used a Poisson model of block production to decide whether the provider could reach the consumer's seenBlock. That model does not describe what this code path waits for. Every path that reaches the bail targets a block <= seenBlock (blockGap is clamped to seenBlock - latestBlock just above), and seenBlock was already served to the consumer by another provider — so those blocks already exist on chain. The provider is not waiting for the chain to produce blocks, it is waiting for its own ChainTracker to poll blocks that are already there, which takes at most averageBlockTime/(MostFrequentPollingMultiplier/4) — well under one block time. Modelling it as block production makes the bail fire whenever the spec's average_block_time overstates the chain's real block time. On NEAR (spec says 1200ms, chain runs at ~600ms since the May 2025 optimistic-block upgrade) probabilityBlockError evaluates to 0.62-0.97 for every realistic combination of blockGap >= 2 and relay timeout, so the bail was unconditional and the catch-up wait below it — which would have closed a 2-3 block gap on the next poll — was unreachable. Keep probabilityBlockError for observability and gate the early bail on whether waiting can help at all: - blockGap beyond blockLagForQosSync*2: the node is genuinely behind and polling won't fix it, bail immediately (unchanged) - not enough time left for a single tracker poll: waiting is pointless Otherwise fall through to the existing bounded wait, which already re-checks the tracker every 50ms and whose post-wait check still returns ConsistencyError if the provider truly did not catch up. Trade-off: a provider whose node is genuinely behind by <= blockLagForQosSync*2 now spends up to halfTimeLeft before failing instead of failing instantly. That budget was already spent this way for blockGap == 1, and the consumer races providers in parallel, so it does not serialize onto end-user latency. Adds halfTimeLeft and trackerPollInterval to the bail warning so operators can see which term fired. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCMc3dBY9QkwHkX8H4iK1Q * fix(test): stop uniqueAddressGenerator spinning forever on port probes protocol/integration times out under `go test -timeout 15m` with no output and no report file, taking the whole test-protocol job down with it. The hang is in the first line of the first test, in uniqueAddressGenerator.GetAddress. Two defects combine: isPortInUse treats every error from resolve/listen as "this port is taken". A host without IPv6 loopback fails the [::1] bind with EAFNOSUPPORT, so every port reports busy even though none of them are. GetAddress then loops on that, and its `currentPort > maxPort` guard sits after the loop rather than inside it. When every probe says "in use" the loop never terminates and never reaches the guard, so the intended panic("all ports have been exhausted") is unreachable and the package spins until the test binary is killed. Fixes both: probe failures that mean the address family is unavailable (EAFNOSUPPORT, EADDRNOTAVAIL) now skip that address instead of condemning the port, and the bound is checked inside the loop so exhaustion panics with a usable message instead of hanging. Verified on a host without IPv6 loopback, where this reproduces every time: TestConsumerProviderBasic went from a 15m timeout to passing in 2.09s, and the full protocol/integration suite from timing out to green in 37s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YCMc3dBY9QkwHkX8H4iK1Q --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
feat: add grpcwebcompat package for canonical trailer encoding (#2321) * fix(grpcweb): emit canonical grpc-web trailers gRPC-Web moves grpc-status/grpc-message out of the HTTP/2 TRAILERS frame and into the response body, as a final frame holding an HTTP/1 style header block. improbable-eng/grpc-web builds that block with http.Header.Write, which emits "key: value", so the response ended with: \x80\x00\x00\x00\x10grpc-status: 0\r\n RFC 7230 permits that optional whitespace and requires receivers to strip it, but a receiver that splits on ':' reads the status as " 0" and rejects the call with "transport: malformed grpc-status: strconv.ParseInt: parsing \" 0\": invalid syntax". Since a grpc-status seen at the header stage marks the response Trailers-Only, the RPC ends as Internal and the message already on the wire is never delivered. Serve gRPC-Web through protocol/grpcwebcompat, which writes "key:value\r\n" - the form Envoy and the other reference implementations emit - with lower-cased names and with values stripped of the surrounding whitespace and of CR/LF, which would otherwise split or truncate the trailer block. The library stays a dependency: native gRPC, CORS pre-flights and gRPC-Websockets still go to it unchanged, so only gRPC-Web responses differ. The same wrapper is applied to the other servers built from the same five lines: the provider listener, the chain tracker and the connection test command. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThUAjPPzmZ4wqSJcoyXPRm * fix(grpcweb): satisfy lint and stop reflecting the request content type Three golangci-lint findings on the new package: rawCodec's forced type assertions (forcetypeassert), and CanonicalHeaderKey applied to the key of Header.Set, which already canonicalises (gosimple S1035). CodeQL flagged the response content type as reflected XSS (go/reflected-xss). The value was built by rewriting the one grpc-go derived from the request, so a request could steer what landed in the response header. Assemble it from this package's constants instead and let the request only select between them, then set it explicitly with Header.Set rather than by assigning into the map. A sub-type outside proto and json now collapses to the base content type instead of being echoed; the sub-types real clients send are unaffected. Also rename the tests to the TestComponent_Scenario form AGENTS.md asks for, make the remaining ones table-driven, and unexport wrapHandler and isGrpcWebRequest, which nothing outside the package uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThUAjPPzmZ4wqSJcoyXPRm --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
feat(consumer): per-chain latency cutoff for random provider selection ( #2314) * feat(consumer): per-chain latency cutoff for random provider selection Adds an optional per-endpoint QoS latency cutoff (MaxProviderLatency, in seconds) to RPCEndpoint. During the general random/weighted provider selection, providers whose EWMA latency exceeds the cutoff are put aside via the existing ignored-providers set, so the optimizer never picks them. Includes a safety fallback: the cutoff is applied only if at least one candidate stays under the threshold; otherwise the full pool is kept so relays keep flowing when the whole pairing is slow. Cold-start providers (no QoS data) are treated as under-threshold. Static, header-selected, sticky and stateful selection paths are unaffected. No optimizer/interface changes; the setting rides on csm.rpcEndpoint. 0 (default) disables the cutoff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(consumer): unit tests + docs for per-chain latency cutoff Adds focused unit tests for filterHighLatencyProviders covering: excluding slow providers, keeping fast ones, the all-slow safety fallback, disabled cutoff (0), cold-start (no QoS) handling, preserving pre-existing ignored entries, strict ">" threshold boundary, multi-provider filtering, and a mixed cold-start/slow/fast case. Documents max-provider-latency in the rpcconsumer README and adds a commented example to config/rpcconsumer.yml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(consumer): integration tests + live E2E harness for latency cutoff Go tests: - consumer_session_manager_latency_selection_test.go: drives getValidProviderAddresses through a stub optimizer to prove the cutoff applies only to the general random path (sticky/stateful/selected-provider paths are unaffected). - rpcconsumer_endpoints_test.go: verifies max-provider-latency parses from the YAML endpoints config into RPCEndpoint (0/omitted => disabled). Live E2E (full env: dev chain + mock-backed providers + consumer): - scripts/pre_setups/init_eth_latency_cutoff.sh: brings up 3 ETH1 providers, each behind its own mock RPC backend; one mock is slowed so its provider exceeds the cutoff. Modes: cutoff | regression-disabled | regression-fallback. - scripts/test/verify_latency_cutoff.sh: drives relays and asserts on the lava_consumer_provider_selections metric per mode. - scripts/test/e2e_latency_cutoff.sh: one-command runner (setup -> wait -> verify -> teardown). - config/eth_latency_cutoff_consumer{,_disabled}.yml: the two consumer configs (cutoff 1.0 and disabled 0) selected by mode. The mock RPC server is left untouched; providers use --use-static-spec with a verification-stripped ETH1 spec so they accept the mock backend while still proxying real (latency-bearing) relays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style(consumer): gofmt latency selection test stub methods Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * log change --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
fix(chainlib): tolerate non-scalar JSON-RPC request ids in id validat… …ion (#2302) * fix(chainlib): tolerate non-scalar JSON-RPC request ids in id validation ValidateRequestAndResponseIds parsed the request id before reaching the "null/empty response id" carve-out. A client that sends a non-scalar id (object/array) — invalid per JSON-RPC 2.0, e.g. a .NET CancellationToken serialized into the id — therefore hard-failed with "failed parsing ID", which lavap surfaces as "jsonRPC ID mismatch / insufficient results" plus a cross-provider retry storm, even when the relay itself was fine. Captured against live nodes: Polygon rejects the object id and replies id:null; NEAR accepts it, returns a valid result, and echoes the object id back. Both were being failed. - Check the null/empty/[] response-id carve-out first, before parsing the request id (covers Polygon's id:null rejection). - When either id is non-scalar, compare ids semantically (whitespace/key-order tolerant) instead of erroring (covers NEAR's verbatim echo, preserving the valid response). A genuinely different id is still reported as a mismatch. Scalar id behaviour is unchanged. Root cause is a non-compliant client; this makes lavap surface the node's real response instead of masking it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(chainlib): end-to-end coverage for non-scalar JSON-RPC request id Drives the real ParseMsg -> SendNodeMsg path against a mock node with an object request id, covering both observed node behaviours: a NEAR-like node that echoes the id and returns a valid result (must be returned, not discarded), and a Polygon-like node that rejects with id:null + "invalid request" (must pass through, not become an "ID mismatch" / retry storm). Complements the ValidateRequestAndResponseIds unit test by asserting the full relay outcome. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PreviousNext