Tags: lavanet/lava
Tags
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>
feat: support multiple --use-static-spec sources with aggregation (#2225 ) Allow --use-static-spec flag to be specified multiple times, aggregating specs from local files, directories, GitHub, and GitLab repositories. When the same chain ID appears in multiple sources, the later source completely replaces the earlier spec (full replacement, not merge). Changes: - Change --use-static-spec from String to StringArray flag type - Add spec aggregation logic with last-wins override semantics - Support comma-separated local file paths (file1.json,file2.json) - Add helper functions for loading all specs from various sources - Add comprehensive tests for aggregation behavior - Remove unused GetSpecsFromPath function Multiple local files can be specified either as separate flags or comma-separated: --use-static-spec file1.json --use-static-spec file2.json --use-static-spec file1.json,file2.json Co-authored-by: Cursor <cursoragent@cursor.com>
fix(chainlib): include response error in ID mismatch logs (#2192) When a node returns an error (e.g., "Apikey is expired") with an empty/invalid JSON-RPC ID, the ID validation fails with a confusing parsing error. This adds the actual response error message to the log output, making the root cause immediately visible.
PreviousNext