Support grpc requests in NativeCall - #354
Merged
Merged
Conversation
tonatoz
requested changes
Aug 31, 2026
tonatoz
approved these changes
Aug 31, 2026
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.
NativeCall: serve native gRPC calls via
grpc_dataitemsSummary
The dshackle
Blockchain.NativeCallstream now accepts native unary gRPC calls (Suisui.rpc.v2.*) alongside JSON-RPC and REST items. ANativeCallItemwith the newgrpc_dataoneof branch is routed through the same execution flow as the gRPC chainingress and answered with one buffered reply item carrying the response message bytes,
the upstream's headers and trailers, and — on failure — a canonical gRPC status.
Bytes-only throughout: request and response messages are never parsed.
Depends on
github.com/drpcorg/publicv1.1.0 (bumped here), which added:Contract for a gRPC item
Request —
methodis the full gRPC method name (/sui.rpc.v2.LedgerService/GetObject),grpc_data.payloadthe serialized request message (no 5-byte wire frame prefix),grpc_data.metadatathe call metadata to forward. Credential metadata (x-nodecore-key,x-nodecore-token,authorization) is stripped before forwarding, exactly like the ingress does.chunk_sizeis ignored: a unary gRPC reply is always one unchunked message.Success —
payload= response message bytes verbatim,signatureover those bytes whena nonce is given,
response_headers/response_trailers= upstream metadata.Error — existing fields, gRPC vocabulary:
item_error_codeResponseError.Codeerror_messageerror_as_isgoogle.rpc.Statuswhen the upstream attached typed details; empty otherwiseerror_dataresponse_headers/response_trailersRESOURCE_EXHAUSTEDrate-limit hints ride in trailers)An upstream status is replayed verbatim; nodecore's own errors (no upstreams, rate limit,
unknown method, timeout, auth, …) are mapped onto the 17-code model in nodecore, so the
client needs no mapping table of its own:
status.FromProto(error_as_is)when set, elsestatus.New(item_error_code, error_message).Rejected up front (no upstream involved): unknown method →
UNIMPLEMENTED; a server-streamspec method →
INVALID_ARGUMENTpointing atNativeSubscribe; missinggrpc_data→INVALID_ARGUMENT.Changes
protocol.GrpcStatusOf(*ResponseError) *status.Status— the nodecore-error → gRPCstatus mapping, moved out of
grpc_ingress/chain_ingress.go(grpcStatusFromResponseError)so the ingress and the emerald server share one table. The ingress behaviour is unchanged.
internal/server/emeraldnative_call_adapter.gois split per API kind:native_call_adapter_jsonrpc.go,native_call_adapter_rest.go,native_call_adapter_grpc.go(new adapter); the sharedfile keeps the
nativeCallAdapterinterface,adapterForand the reply machinery.nativeCallAdaptergainsErrorItem(requestID, err), so pre-dispatch failures (invalidpayload, selector conflicts, signature requested without a signer) render in the item's
own error vocabulary. The adapter is now chosen before the signing check.
sendReplytakes anerrorItemRenderer— nodecore codes +error_datafor JSON-RPC/REST,canonical status for gRPC — and reads response metadata via the
HasResponseHeaders/HasResponseTrailerscapabilities instead of a*GenericUpstreamResponsetype assertion, so metadata on*ReplyError(retryable upstreamerrors) is no longer dropped. Trailers are stamped on success, error and first stream chunk.
replyMeta(request id, upstream id/version, finalization, headers, trailers) builtonce per response and
stamped onto items;streamNativeCallBodygoes from 11 positionalparams to
(stream, reader, mode, hint, meta).go.mod—github.com/drpcorg/public v1.0.0 → v1.1.0.JSON-RPC and REST items are unaffected apart from the (previously empty) trailers field and
the
*ReplyErrorheaders that now ride along.Out of scope (follow-ups)
NativeSubscribefor gRPC server-stream methods (still JSON-RPC only).string, so quorum is unreachable via
NativeCallfor any item kind today.Testing
internal/protocol/grpc_status_of_test.go— verbatim replay with/without details, everynodecore code mapping, nil input.
internal/server/emerald/grpc_blockchain_test.go— gRPC item build (method, body, sanitizedmetadata,
chunk_sizeignored), unknown / server-stream / missing-data / signing-unavailableitems, success item with headers + trailers, upstream status with details round-tripping
through
error_as_is, status without details, nodecore error →UNAVAILABLE, trailers on*ReplyError, JSON-RPC regression (nodecore codes kept, trailers empty).go test -race ./...green,golangci-lintclean.