Conversation
|
Claude finished @benaadams's task in 8m 19s —— View job Review: Improve JsonRpc plumbing
OverviewThis is a significant and well-structured rework of the JSON-RPC plumbing layer. The key architectural improvements are:
The overall direction is sound and the hot paths are carefully optimized. Below are specific findings. FindingsHigh
Medium
Low
Other observations (not blocking)
SummaryThe architectural direction is correct and the performance improvements are well-reasoned. The two High findings should be addressed before merge (the type-info cache is a latent correctness bug; the duplicate methods invite drift). The two Medium findings affect correctness (Equals contract) and allocation on string IDs. The Low items are cosmetic. |
|
@claude re-review |
|
Claude finished @benaadams's task in 5m 10s —— View job Re-review: Improve JsonRpc plumbing
Previous Findings — Status
New FindingsNo new Critical, High, or Medium issues found in the updated code. The reviewed areas were correct and well-structured:
SummaryAll High and Medium findings from the previous review have been addressed. One Low finding ( |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR refactors JSON-RPC transport plumbing to stream responses via a sink model, tighten/accelerate parsing and serialization hot paths (typed IDs, direct writers), and expand metrics/observability around request/response handling.
Changes:
- Introduces
IJsonRpcResponseSinkand migrates HTTP/WebSocket JSON-RPC processing to write directly to sinks (incl. direct streaming results). - Adds typed
JsonRpcId+ strict envelope/array readers and method-name interning/caching for faster dispatch. - Improves serialization/converters (byte arrays, hashes, hex) and updates metrics (histograms) with expanded tests/benchmarks.
Reviewed changes
Copilot reviewed 94 out of 95 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Nethermind/Nethermind.Sockets.Test/WebSocketExtensionsTests.cs | Updates WebSocket tests to exercise sink-based response writing and metric deltas. |
| src/Nethermind/Nethermind.Serialization.Json/ValueHash256Converter.cs | Optimizes ValueHash256 deserialization using span-based conversion first. |
| src/Nethermind/Nethermind.Serialization.Json/StreamPipeWriter.cs | Adds flush counting + timing instrumentation to counting pipe writers. |
| src/Nethermind/Nethermind.Serialization.Json/HexWriter.cs | Adjusts hex chunk sizing and adds span overloads for chunked/small paths. |
| src/Nethermind/Nethermind.Serialization.Json/HexBytesConverter.cs | Adds HexBytes JSON converter. |
| src/Nethermind/Nethermind.Serialization.Json/Hash256Converter.cs | Optimizes hash deserialization using exact-length span parsing first. |
| src/Nethermind/Nethermind.Serialization.Json/EthereumJsonSerializer.cs | Registers new converters (HexBytes, byte[][]). |
| src/Nethermind/Nethermind.Serialization.Json/Bytes32Converter.cs | Refactors to avoid multi-segment allocations and handles 0x-prefix stripping/padding. |
| src/Nethermind/Nethermind.Serialization.Json/ByteArrayArrayConverter.cs | Adds fast converter for byte[][] as array of hex strings. |
| src/Nethermind/Nethermind.Runner/JsonRpc/HttpJsonRpcResponseSink.cs | Adds HTTP sink implementation with typed result serialization and streamable result support. |
| src/Nethermind/Nethermind.Monitoring/Metrics/MetricsController.cs | Adds support for explicit histogram metric attribute binding. |
| src/Nethermind/Nethermind.Monitoring.Test/MetricsTests.cs | Extends metrics tests for explicit histogram creation/scrape output. |
| src/Nethermind/Nethermind.Merge.Plugin/SszRest/SszMiddleware.cs | Refactors middleware for fast exits and PipeReader-based body handling/metrics. |
| src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs | Reworks handler to build direct-streaming payload bodies V2 response objects. |
| src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV1Handler.cs | Enables returning direct-streaming wrapper for V1 range responses. |
| src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs | Implements direct-streaming V2 response with request-size limits and disposal safety. |
| src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV1Handler.cs | Enables returning direct-streaming wrapper for V1 hash responses. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/StreamableResultWriter.cs | Adds shared flush-threshold helper for streamable JSON writers. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/PayloadBodiesV2DirectResponse.cs | Adds streamable + disposable direct JSON writer for payload bodies V2. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/PayloadBodiesV1DirectResponse.cs | Adds streamable direct JSON writer for payload bodies V1 and shared writer helpers. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV2Result.cs | Adds creation helper for already-encoded transactions to avoid re-encoding. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | Adds null-guard for encoded transactions setter. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/EngineApiJsonContext.cs | Registers ByteArrayArrayConverter in source-gen context. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/BlobsV2DirectResponse.cs | Uses shared flush-threshold helper for backpressure. |
| src/Nethermind/Nethermind.Merge.Plugin/Data/BlobsV1DirectResponse.cs | Uses shared flush-threshold helper for backpressure. |
| src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V6.cs | Adds JSON validity test for PayloadBodiesV2DirectResponse. |
| src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs | Adds test asserting cached typed parameter metadata for hot engine method. |
| src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs | Adds tests for flush batching/backpressure and larger-pipe options. |
| src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V2.cs | Adds JSON validity test for PayloadBodiesV1DirectResponse. |
| src/Nethermind/Nethermind.Merge.Plugin.Test/ByteArrayArrayConverterTests.cs | Adds converter registration and roundtrip tests for byte[][]. |
| src/Nethermind/Nethermind.Merge.Plugin.Benchmark/EngineBenchmarkHost.cs | Updates benchmark host to sink-based processor API. |
| src/Nethermind/Nethermind.Merge.Plugin.Benchmark/BlobsV2DirectResponseBenchmarks.cs | Adds benchmarks for direct blob-response writer including flush stats. |
| src/Nethermind/Nethermind.JsonRpc/WebSockets/SocketJsonRpcResponseSink.cs | Adds WebSocket sink implementation with framing and stats integration. |
| src/Nethermind/Nethermind.JsonRpc/WebSockets/JsonRpcSocketsClient.cs | Migrates socket client processing to sink-based processor API. |
| src/Nethermind/Nethermind.JsonRpc/ResultWrapper.cs | Adds HasErrorData and makes wrapper Data nullable for error-data control. |
| src/Nethermind/Nethermind.JsonRpc/NullJsonRpcLocalStats.cs | Updates local stats interface compliance with non-async no-op reporting. |
| src/Nethermind/Nethermind.JsonRpc/Nethermind.JsonRpc.csproj | Formatting-only adjustment. |
| src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/JsonRpcSubscriptionResponse.cs | Migrates subscription response IDs to typed JsonRpcId. |
| src/Nethermind/Nethermind.JsonRpc/Modules/RpcModuleProvider.cs | Adds hot-method caching, typed param metadata caching, ValueTask module rental, direct invokers. |
| src/Nethermind/Nethermind.JsonRpc/Modules/NullModuleProvider.cs | Updates module provider interface compliance (ValueTask rent + resolved method overloads). |
| src/Nethermind/Nethermind.JsonRpc/Modules/IRpcModuleProvider.cs | Updates API to return resolved method info and use ValueTask for rent/return. |
| src/Nethermind/Nethermind.JsonRpc/Modules/Eth/IEthRpcModule.cs | Changes eth_call result type to HexBytes. |
| src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs | Implements eth_call returning HexBytes. |
| src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs | Updates Tx executor to produce HexBytes and preserve raw bytes. |
| src/Nethermind/Nethermind.JsonRpc/Metrics.cs | Switches RPC latency metric to histogram with explicit buckets (microseconds). |
| src/Nethermind/Nethermind.JsonRpc/KnownRpcMethodNames.cs | Adds interned known-method-name tables for faster dispatch. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcResult.cs | Removes batch-result model and simplifies to single-entry results. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcResponse.cs | Migrates to typed IDs and adds typed result metadata for faster serialization/disposal checks. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcRequest.cs | Adds UTF-8 params storage + lazy JsonDocument parsing and typed IDs. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessingOptions.cs | Adds transport-level processing options record. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcMetricLabels.cs | Adds label wrapper for per-method histogram metric observations. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcLocalStats.cs | Refactors stats recording to lock-free + background report formatting. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcInputMode.cs | Adds enum controlling single vs multiple top-level JSON documents acceptance. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcIdConverter.cs | Adds converter for typed JsonRpcId. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcId.cs | Adds typed ID value supporting numeric/string/missing/null and raw token writing. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcEnvelopeReader.cs | Adds single-pass envelope reader for request bodies. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcEnvelope.cs | Adds envelope struct to carry parsed top-level request details. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcConfig.cs | Adds AdditionalTrustedNetworks configuration surface. |
| src/Nethermind/Nethermind.JsonRpc/JsonRpcArrayReader.cs | Adds fast array counting and item slicing helpers. |
| src/Nethermind/Nethermind.JsonRpc/IResultWrapper.cs | Makes wrapper Data nullable and adds HasErrorData. |
| src/Nethermind/Nethermind.JsonRpc/IJsonRpcService.cs | Switches to ValueTask and typed JsonRpcId for error response IDs. |
| src/Nethermind/Nethermind.JsonRpc/IJsonRpcResponseSink.cs | Adds response-sink abstraction. |
| src/Nethermind/Nethermind.JsonRpc/IJsonRpcProcessor.cs | Changes processor API to push responses to a sink instead of yielding results. |
| src/Nethermind/Nethermind.JsonRpc/IJsonRpcLocalStats.cs | Refactors local stats to synchronous interface and lock-free counters. |
| src/Nethermind/Nethermind.JsonRpc/IJsonRpcConfig.cs | Adds config doc + property for AdditionalTrustedNetworks. |
| src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs | Fixes serializer strict-hex global state leakage across tests. |
| src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs | Updates expected error payloads to match new error-data emission rules. |
| src/Nethermind/Nethermind.JsonRpc.Test/Modules/RpcModuleProviderTests.cs | Adds hot-method resolution and direct-invoker caching tests. |
| src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcSimulateTestsBase.cs | Updates eth_call tests to compare byte payloads via HexBytes. |
| src/Nethermind/Nethermind.JsonRpc.Test/Modules/AdminModuleTests.cs | Updates expected error payloads to match new error-data emission rules. |
| src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcSocketsClientTests.cs | Adds sink framing/limits tests and updates processor substitutes to sink API. |
| src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcServiceTests.cs | Adds coverage around raw UTF-8 params, typed result handling, disposal, and error-data suppression. |
| src/Nethermind/Nethermind.JsonRpc.Test/JsonRpcLocalStatsTests.cs | Updates stats tests for synchronous API + background report generation. |
| src/Nethermind/Nethermind.JsonRpc.Test/Data/IdConverterTests.cs | Adds tests for typed JsonRpcId conversion/serialization edge cases. |
| src/Nethermind/Nethermind.JsonRpc.Test/Data/BlockParameterConverterTests.cs | Makes test non-parallel and controls global strict-hex setting. |
| src/Nethermind/Nethermind.Core/Resettables/RecyclableStream.cs | Adds overload with initial size hint for recyclable streams. |
| src/Nethermind/Nethermind.Core/JsonConverters/ByteArrayConverter.cs | Adds span-based parsing helpers and expands inline buffers for encoding. |
| src/Nethermind/Nethermind.Core/JsonConverters/AddressConverter.cs | Switches to exact-length span conversion via ByteArrayConverter. |
| src/Nethermind/Nethermind.Core/HexBytes.cs | Adds a value type for hex-serialized byte payloads. |
| src/Nethermind/Nethermind.Core/Extensions/HexConverter.cs | Adds TryCopyHexToUtf8 and refactors vector parsing into shared helper. |
| src/Nethermind/Nethermind.Core/BloomConverter.cs | Optimizes bloom reading with span-based conversion first. |
| src/Nethermind/Nethermind.Core/Authentication/JwtAuthentication.cs | Reuses HMAC instance for JWT validation to reduce allocations. |
| src/Nethermind/Nethermind.Core/Attributes/Metrics.cs | Adds HistogramMetricAttribute for explicit histogram configuration. |
| src/Nethermind/Nethermind.Core.Test/Json/Hash256ConverterTests.cs | Adds roundtrip test for ValueHash256Converter. |
| src/Nethermind/Nethermind.Core.Test/Json/ByteArrayConverterTests.cs | Extends coverage around inline-buffer thresholds and output fidelity. |
| src/Nethermind/Nethermind.Core.Test/BytesTests.cs | Adds tests for HexConverter.TryCopyHexToUtf8. |
| src/Nethermind/Nethermind.Benchmark/Core/ByteArrayConverterWriteBenchmarks.cs | Adds benchmark comparing inline-buffer strategies vs production encoding. |
Comments suppressed due to low confidence (5)
src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs:1
- This handler has compile-time issues: array lengths and indices must be
int, butend - start + 1andi - startarelong. Also the log message has an extra$(\": ${error}\") which will print incorrectly. Convert the computed count/index toint(ideally withcheckedsinceMaxCountbounds it), and fix the interpolation to use{error}.
src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs:1 - This handler has compile-time issues: array lengths and indices must be
int, butend - start + 1andi - startarelong. Also the log message has an extra$(\": ${error}\") which will print incorrectly. Convert the computed count/index toint(ideally withcheckedsinceMaxCountbounds it), and fix the interpolation to use{error}.
src/Nethermind/Nethermind.JsonRpc/JsonRpcIdConverter.cs:1 - Throwing a parameterless
NotSupportedExceptionduring JSON deserialization provides an unhelpful error to callers and often bypassesJsonExceptionsemantics (path/line info). Prefer throwing aJsonException(or aNotSupportedExceptionwith a clear message) describing which token types are valid for JSON-RPC IDs.
src/Nethermind/Nethermind.Serialization.Json/StreamPipeWriter.cs:1 AddFlushMeasurement(startTimestamp)is not guaranteed to run if_writer.FlushAsync(...)completes synchronously butGetResult()throws (e.g., underlying stream failure). Wrap the synchronous completion path in atry/finallyso measurements are recorded consistently on both success and failure (matching the async path behavior).
src/Nethermind/Nethermind.Serialization.Json/HexBytesConverter.cs:1- This converter maps JSON
nullto an empty byte sequence, which makesnullindistinguishable from\"0x\"/empty bytes at the API boundary. Ifnullis not a valid wire value forHexBytes, it would be safer to throwJsonExceptiononJsonTokenType.Null(or, ifnullshould be supported, consider making the target typeHexBytes?and returningnull).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
EXPB Benchmark ComparisonRun: View workflow run superblocksScenario: Client Processing (SSE)
K6 TTFB
realblocksScenario: Client Processing (SSE)
K6 TTFB
|
Changes
Architecture
IJsonRpcResponseSink) with dedicatedHttpJsonRpcResponseSinkandSocketJsonRpcResponseSinkimplementations, replacing the bufferedIJsonRpcBatchResultadapter path and migrating HTTP and WebSocket transports onto it.JsonRpcEnvelopeReader) that parses HTTP JSON-RPC documents and batches directly from collected request bodies, with strict single-document parsing and rejection of adjacent JSON documents.object-typed JSON-RPC IDs with a typedJsonRpcIdvalue (numeric, string, and raw-token preserving) threaded through requests, responses, models, sockets, and subscription messages, with safer/escaped JSON output and null-equality fixes.JsonRpcProcessorandJsonRpcServicearound the sink path and removed legacy inbound batch wrappers, reflection-based dispatch fallbacks, and async wrappers on module rent/dispatch (ValueTaskthroughout).Typed JSON-RPC response pipeline
JsonRpcSuccessResponse.Result = (object)dataslot with a typedJsonRpcResponse<T>carrier so the staticTsurvives fromResultWrapper<T>.Datathrough the sink writer; eliminates value-type boxing forlong,ulong,bool,UInt256,Hash256, etc.JsonRpcSuccessResponsepolymorphism are removed.long/ulong/UInt256through Ethereum converters) and engine-payload-typed responses in tests; primitive RPC results write directly without going through STJ.MethodNamefromJsonRpcResponse(method name flows viaRpcReport.Methodto the stats reporter) and removed dispatch reflection fallbacks now that the typed path is closed-world.RPC metadata source generation
IRpcModulesurfaces per assembly (including inherited methods) and emits the closed set of RPC result, parameter, subscription, error-data, and derived-type metadata before compile.JsonTypeInfo<T>without per-call resolver-chain walks.Engine API direct streaming
engine_getBlobsV1/V2andengine_getPayloadBodiesByHash/Range V1/V2via dedicated*DirectResponsewriters and a sharedStreamableResultWriter.HexBytesresults foreth_calland direct streaming for primitive JSON-RPC results.ByteArrayArrayConverterand aHexBytesConverter, raised blob hex chunk size to fit max blob, batched small blob flushes, and tightenedHexWriter/HexConverterpaths.Hot-path JSON-RPC tuning
KnownRpcMethodNames), order method probes by frequency, compare as UTF-8, handle segmented names, and cache resolved method metadata + parameter metadata + direct invokers (zero-parameter and small-parameter fast paths) with pooled invocation arguments.SearchValueswith a fast-path scan; reuse single-document processing options; remove redundant envelope offset checks and the duplicate envelope skip helper.Lockfor rotation), moved report formatting off the hot path, and removed stats task scheduling.Metrics and observability
HTTP/SSZ request handling
Content-Lengthis set.JsonRpcConfig.Serialization
ByteArrayArrayConverter,HexBytesConverter, and tightenedByteArrayConverter,Bytes32Converter,Hash256Converter,ValueHash256Converter,AddressConverter,BloomConverter,HexConverter, andStreamPipeWriter.Tests and benchmarks
Nethermind.JsonRpc.Test(envelope parsing, ID edge cases, batch limits, parameter metadata cache, processor sink path, sockets, response shape verification, typed subscription/error paths),Nethermind.Runner.Test(startup auth failure, recorder/disposal lifetimes, strict HTTP parsing, typed error serialization),Nethermind.Merge.Plugin.Test(Engine V2/V3/V4/V6 typed payload responses, byte-array-array converter),Nethermind.Core.Test,Nethermind.Monitoring.Test, andNethermind.Sockets.Test.BlobsV2DirectResponseBenchmarks,ByteArrayConverterWriteBenchmarks, JSON-RPC response writer benchmarks, benchmark JSON-RPC response sink coverage.JitAsmnow loads the ASP.NET framework so the sink path can be inspected.Types of changes
What types of changes does your code introduce?
Testing
Requires testing
If yes, did you write tests?
Notes on testing
Nethermind.JsonRpc.Test,Nethermind.Runner.Test,Nethermind.Merge.Plugin.Test,Nethermind.Core.Test,Nethermind.Monitoring.Test, andNethermind.Sockets.Test.Nethermind.BenchmarkandNethermind.Merge.Plugin.Benchmarkfor the new serialization hot paths and the typed response writer.Documentation
Requires documentation update
Requires explanation in Release Notes
Remarks
Valid JSON-RPC requests and responses are intended to remain wire-compatible. Invalid, malformed, or oversized HTTP inputs are rejected earlier, large Engine API responses use direct streaming paths, and the typed response pipeline keeps the static
Tof every RPC result alive fromResultWrapper<T>.Datato the serializer so value-type payloads do not box and source-generatedJsonTypeInfo<T>is used end-to-end.