Skip to content

feat(framework): support admin JSON-RPC over IPC and HTTP - #6979

Open
317787106 wants to merge 4 commits into
tronprotocol:release_v4.8.3from
317787106:feature/admin_rpc
Open

317787106 wants to merge 4 commits into
tronprotocol:release_v4.8.3from
317787106:feature/admin_rpc

Conversation

@317787106

@317787106 317787106 commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

This PR adds the Admin JSON-RPC transport foundation requested by #6497. A single annotated AdminJsonRpc interface is shared by an HTTP endpoint and a local Unix-domain-socket service. The initial admin_example method verifies typed command dispatch and annotation-based JSON-RPC error handling; additional administrative methods can be added to the same interface.

In this PR, JSON-RPC defines the shared API and message format, while HTTP and IPC are the two transports:

                Admin JSON-RPC API
                 /             \
        HTTP transport     IPC transport
          POST /admin      Unix domain socket

Admin HTTP service

The HTTP service is available to FullNode processes at POST /admin and is disabled by default. It binds Jetty to the configured address and port. Enabling it on a non-loopback address emits a warning.

The servlet validates the Host header against the configured virtual-host allowlist, while accepting IPv4 and IPv6 literals. It accepts application/json, application/json-rpc, and application/*+json media types, and rejects unsupported content types with HTTP 415. JSON-RPC parsing reuses a constrained object mapper with nesting-depth and token-count limits. JSON-RPC results, including protocol errors, use HTTP 200 responses.

Admin HTTP is intended for trusted node operators. Deployments must restrict access to loopback or a controlled management network. Under this trust model, Admin HTTP intentionally does not apply the public JSON-RPC node.jsonrpc.maxBatchSize and node.jsonrpc.maxResponseSize limits. The existing HTTP request-size limit and JSON nesting-depth and token-count limits still apply.

The HTTP transport configuration is:

node.admin.http {
  enable = false
  listenAddress = "127.0.0.1"
  port = 8575
  virtualHosts = ["localhost"]
}

Admin IPC service

The IPC service is also FullNode-only and disabled by default. By default it creates an endpoint at:

<output-directory>/ipc/<pid>.sock

node.admin.ipc.socketDirectory can select another socket root and must be an existing absolute directory on a POSIX-compatible filesystem. The service creates the private ipc directory with owner-only access and sets the socket to 0600. It refuses to replace a symbolic link or non-directory at the private directory path.

The complete encoded socket path is limited to 100 bytes for portability across supported Unix-domain-socket implementations. If the path is longer, startup fails with guidance to configure a shorter node.admin.ipc.socketDirectory; it does not silently relocate the endpoint.

node.admin.ipc {
  enable = false
  socketDirectory = ""
}

IPC uses newline-delimited, single-line JSON-RPC messages and the same AdminJsonRpc, error resolver, and constrained JSON mapper as HTTP. Request size is bounded by the existing node.rpc.maxMessageSize value, whose default is 4 MiB. Each client has a ten-minute idle timeout. The bounded client executor supports concurrent console sessions and immediately closes connections that arrive after all handlers are occupied. Unexpected accept failures use a five-second retry delay.

Startup failures and normal shutdown both clean up owned sockets and the private directory. Shutdown closes active clients before stopping executors so blocked native socket reads do not unnecessarily delay node termination.

IPC console

The FullNode executable can attach to an active node without initializing another node instance:

java -jar FullNode.jar --attach <socket-path>

A single command can be executed for scripting:

java -jar FullNode.jar --attach <socket-path> --exec "<command> [arguments]"

Attach mode is handled immediately after CLI argument parsing and before CommonParameter, Logback, database, witness, or node services are initialized. --exec requires --attach, an empty socket path is rejected, and attach mode cannot be combined with --config.

The JLine console derives command names, parameter names, and parameter types from the annotated Admin API. It supports quoted arguments, typed JSON conversion, sorted help, canonical command completion, formatted JSON results, help, exit, and quit. One-shot execution returns a non-zero process status for invalid commands, JSON-RPC errors, communication failures, disconnection before a response, or the 30-second response timeout.

Supporting changes

HttpService now supports binding a service to a specific listen address. The regular JSON-RPC servlet and the Admin transports share the new constrained JsonRpcMapper, and supported JSON media-type matching is centralized in JsonRpcMediaType.

The framework adds JLine for the interactive console and junixsocket for Unix-domain-socket support. Dependency verification metadata is updated accordingly.

Why are these changes required?

Administrative operations need a local, scriptable interface without starting a second node or exposing the existing public APIs as privileged management endpoints. The Unix-domain socket provides a private local transport, while the optional HTTP endpoint supports controlled integration when explicitly enabled.

Sharing one typed Admin API across both transports keeps command names, parameters, results, and JSON-RPC errors consistent. Explicit address binding, virtual-host validation, parser limits, filesystem permissions, bounded clients, and deterministic cleanup provide safer operational defaults.

Testing

The PR adds or updates tests covering:

  • Admin configuration defaults, binding, and attach-mode CLI validation.
  • HTTP listen-address handling, loopback classification, virtual-host validation, JSON media types, and parser limits.
  • IPC path resolution and encoded-length limits, absolute-directory validation, POSIX permissions, stale-path safety, startup rollback, and shutdown cleanup.
  • IPC request limits, annotated errors, concurrent clients, idle timeouts, overload rejection, and active-client shutdown.
  • Console command discovery, typed arguments, quoted whitespace, completion, help, response formatting, one-shot exit codes, timeouts, and disconnect behavior.
  • FullNode startup ordering to ensure attach mode does not initialize node logging.

The related Admin HTTP, IPC, CLI, configuration, and FullNode tests, together with production and test checkstyle checks, passed during development.

Follow-up

Runtime parameter export and Peer management commands will be implemented separately using the Admin transports introduced here.

Add HTTP and Unix-domain-socket transports for the shared Admin JSON-RPC API, with an attach console for trusted node operators.

Include transport configuration, socket permissions, request handling, bounded client lifecycle, and startup isolation regression tests.
}

private void deleteDirectoryWithDirectEntries(Path directory) throws IOException {
try (DirectoryStream<Path> entries = Files.newDirectoryStream(directory)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MUST] Starting a second node with the same socketDirectory deletes the first node's live socket path, so new attach connections to the first node fail. This loop also deletes unrelated files.

Please either give each node its own IPC directory, or support a shared directory safely, and limit startup/shutdown cleanup to the entries the node owns. If the directory is shared, leave it in place while other entries remain.

Please add a regression test showing that starting and stopping a second node does not break the first node's endpoint.

resp.setContentLength(0);
return;
}
rpcServer.handle(req, resp);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MUST] jsonrpc4j swallows parser-limit exceptions here and returns HTTP 200 with an empty body, while IPC turns the same failure into -32603. Malformed JSON also gets the string "null" as the response id.

Please handle these failures in the shared Admin request handling, so that both transports return valid, consistent JSON-RPC errors, with a JSON null id when the request id cannot be determined.

Please also change the depth/token tests to assert the error body, and to check that no Admin method was invoked.

ByteArrayOutputStream output = new ByteArrayOutputStream();

try {
jsonRpcServer.handleRequest(input, output);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MUST] Batch handling is left to jsonrpc4j and does not follow JSON-RPC 2.0. HTTP and IPC both behave this way:

  • [] returns []
  • [{"jsonrpc":"2.0","method":"admin_example","params":["a","b"]}] returns [null]
  • [{"jsonrpc":"2.0","method":"admin_example","params":["x","y"]},{"jsonrpc":"2.0","method":"admin_example","params":["a","b"],"id":2}] returns [null,{"jsonrpc":"2.0","id":2,"result":"a:b"}]

Please handle batches in the shared Admin dispatch layer: return a single -32600 error for an empty batch, leave notifications out of the response, and return no body when every entry is a notification.

Please add empty-batch, notification-only, and mixed-batch tests for both HTTP and IPC.

*/
@Component
@Slf4j(topic = "API")
public class AdminRpcServlet extends RateLimiterServlet {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD] Extending RateLimiterServlet puts Admin HTTP on the same global quota as the public API, so public traffic can delay admin requests, or get them rejected when apiNonBlocking is enabled. If a local reverse proxy makes public requests appear to come from the same loopback address as admin requests, they also share the per-IP quota.

Please give Admin HTTP an independent rate-limit policy, and decide what an overloaded request should return. The inherited {"Error":"lack of computing resources"} body is not a JSON-RPC response.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0e624ea. Given its trusted-operator, low-frequency usage, Admin HTTP now extends HttpServlet and does not participate in public API rate limiting. This removes both the shared-quota dependency and the inherited REST-style rate-limit response.

Existing Host, media-type, request-size, and parser checks remain in place, along with HTTP latency metrics. Added regression coverage through service() confirming that Admin requests succeed when the public limiter rejects, under both blocking and non-blocking configurations. All 37 related tests and Checkstyle passed locally.

exit(0);
}
List<ParameterDescription> assignedParameters = arguments.getAssignedParameters();
if (arguments.isAttachMode()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD] FullNode.main() already handles attach mode and returns before calling Args.setParam(), so this branch and the attach fields stored here are unused in the current FullNode startup path. They also let a direct caller skip node configuration without any error.

Please keep attach dispatch in FullNode, remove this duplicate state from Args, and reject attach options explicitly if they ever reach this method.

System.err.println("Disconnected from server before receiving a response.");
return EXIT_FAILURE;
}
IpcResponse parsedResponse = IpcResponse.parse(response);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD] The response is never checked against the request that was sent. {"result":true}, or a response with a different id, still counts as success and exits 0.

Please check that jsonrpc is "2.0", that the response id matches the request, and that exactly one of result or error is present. An invalid or mismatched response should exit with a non-zero status, since scripts use this status to tell whether the command succeeded.

maxMessageSize = 4194304
}

# Administrative API settings. Disabled by default.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD] Please also add disabled Admin HTTP/IPC examples to framework/src/main/resources/config.conf. The defaults from reference.conf apply at runtime, but the config file we ship to operators doesn't show any of the new settings.

Next to the examples, please note that:

  • HTTP must only be exposed on a trusted network.
  • For IPC, a non-empty node.admin.ipc.socketDirectory must be an existing absolute directory, and the full socket path must not exceed 100 bytes once encoded.
  • IPC request size is controlled by node.rpc.maxMessageSize, so operators know that changing it affects both gRPC and IPC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Design of a Secure IPC and JSON-RPC Based Administration Framework for FullNode

4 participants