Skip to content

fix(docker): improve tooling reliability and deployment safety - #5

Open
3for wants to merge 44 commits into
developfrom
feat/docker
Open

3for wants to merge 44 commits into
developfrom
feat/docker

Conversation

@3for

@3for 3for commented Sep 1, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

This PR improves and hardens the Docker tooling and documentation for java-tron.

  • Aligns the amd64 and ARM64 Dockerfiles with the maintained tron-docker build definitions.
  • Preserves the standalone docker.sh workflow, allowing users to download only the script and build an image.
  • Makes Dockerfile selection and build context independent of the current working directory.
  • Removes the unused docker-entrypoint.sh wrapper and starts FullNode directly.
  • Improves argument parsing, validation, quoting, error reporting, and exit codes.
  • Uses exact image and container matching instead of ambiguous text searches.
  • Publishes mainnet P2P over both TCP and UDP while binding HTTP and gRPC APIs to localhost by default.
  • Makes private mode suitable for a local single-node development network by loading the private configuration and enabling witness mode.
  • Keeps private P2P, JSON-RPC, and other nonessential ports unpublished by default; multi-machine private networks can expose them explicitly with custom -p options.
  • Downloads the private network configuration only when absent, preserves local changes, supports explicit refresh with --update-config true, and fails safely if the download fails.
  • Validates --run arguments before pulling or inspecting images.
  • Removes unnecessary TTY allocation from background and log commands.
  • Adds lightweight, path-filtered Docker static checks for Bash syntax, ShellCheck, and both Dockerfiles.
  • Updates docker.md and quickstart.md to document build sources, image tags, port exposure, persistence, lifecycle operations, architecture requirements, production JVM and hardware guidance, and the intended scope of docker.sh.

Why are these changes required?

The previous Docker workflow contained several functional, security, and documentation inconsistencies:

  • Docker builds depended on the caller’s current directory and could select the wrong Dockerfile or build context.
  • Some invalid arguments, missing dependencies, and malformed invocations could be ignored or return a successful exit status.
  • Image and container detection used ambiguous matching.
  • Mainnet P2P UDP was not published, while APIs were exposed on all host interfaces.
  • Private mode used ports that did not match its configuration and did not enable witness mode.
  • Publishing the private witness P2P port by default could expose development credentials and the local private chain to untrusted peers.
  • Configuration downloads could overwrite local changes or report failures inconsistently.
  • Shell string concatenation could incorrectly split paths or arguments containing spaces or wildcard characters.
  • The entrypoint persisted the complete FullNode command line, potentially retaining sensitive arguments in the container layer.
  • The documentation did not clearly distinguish Docker Hub images, remote-source local builds, production deployment, single-node private development, and multi-node private networks.
  • Docker-related changes had no dedicated, low-cost CI validation.

The revised design intentionally keeps docker.sh as a lightweight helper for common single-container workflows. Advanced production requirements—including custom JVM settings, storage layouts, multiple instances, SR deployment, and multi-node private networks—remain explicit Docker or dedicated deployment-tool workflows.

This PR has been tested by:

  • Unit Tests
    • No Java unit tests were required because the changes are limited to Docker scripts, Dockerfiles, CI configuration, and documentation.
  • Manual Testing
    • Validated docker/docker.sh with bash -n.
    • Validated docker/docker.sh with ShellCheck.
    • Validated both amd64 and ARM64 Dockerfiles with docker buildx build --check.
    • Verified mainnet and private-network default port mappings.
    • Verified that custom -p, -v, and -c arguments are preserved safely.
    • Verified that invalid or incomplete arguments fail before image lookup or download.
    • Verified private configuration reuse, explicit refresh, failure handling, and stderr output.
    • Verified that git diff --check reports no whitespace errors.

Follow up

  • Consider pinning remote source revisions, base-image digests, and released image tags to immutable references where reproducible builds are required.
  • Runtime Docker integration tests may be added later if the additional CI cost is justified.
  • Advanced deployment features such as arbitrary JVM options, multiple named instances, SR key management, and multi-node orchestration should remain outside the lightweight helper or be handled by dedicated tooling.

Extra details

  • docker.sh --build builds from the configured remote java-tron source rather than the caller’s current checkout.
  • docker.sh --pull uses the configured Docker Hub image and tag; it may therefore produce a different revision from --build.
  • Mainnet HTTP and gRPC endpoints bind to localhost by default, while mainnet P2P remains externally reachable over TCP and UDP.
  • Private mode defaults to a local single-node development environment. HTTP and gRPC bind to localhost, while P2P 16666 and JSON-RPC 8545 are not published unless explicitly requested.
  • Existing containers retain their original port mappings and must be removed and recreated to adopt the new defaults.
  • The Docker CI intentionally remains static-only to avoid materially increasing the total CI runtime.

@3for 3for changed the title Feat/docker fix(docker): improve tooling reliability and deployment safety Sep 1, 2026
Comment thread docker/docker.sh
tron_args+=(--witness)
fi

docker run -d --name "$CONTAINER_NAME" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Important] run() lacks a container-existence pre-check — a second --run fails with a raw Docker name conflict

docker run -d --name "$CONTAINER_NAME" runs unconditionally. If the container already exists (previous --run, or a rerun after reconfiguring), Docker aborts with a raw Conflict. The container name "/tronprotocol-java-tron" is already in use error and the user has to figure out cleanup themselves. This is a common, always-reachable failure and works against this PR's reliability goal.

Suggestion: before the docker run, reuse the existing docker_container_exists() helper (already used by --start/--stop/--rm) and print a clear message, e.g.:

Container tronprotocol-java-tron already exists. Use --rm to remove it first, or --start to start it.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@warku123 Good catch, thanks! Fixed in 305bb06 by checking whether the container already exists before running docker run, using the existing docker_container_exists() helper.

xxo1shine and others added 5 commits September 11, 2026 14:15
…#6930)

Node and network API responses populated two fields from the wrong source values because of copy-and-paste mapping errors.

Map needSyncFromPeer from the corresponding peer state and assign UDP inbound traffic to the udpInTraffic protobuf field.
…me (tronprotocol#6950)

* chore(deps): upgrade grpc-java from 1.83.0 to 1.83.1

1. bump grpcVersion to 1.83.1 to pick up the upstream fix for
   grpc/grpc-java#12930 (PR grpc/grpc-java#12942), which enforces
   connection.remote().maxActiveStreams(maxStreams) at handler startup
2. drop GrpcNettyMaxConcurrentStreamsLimiter, the local protocol-negotiator
   shim that applied the same limit while 1.83.0 left the remote endpoint
   unbounded until the client acknowledged SETTINGS

* chore(deps): upgrade jackson from 2.18.6 to 2.18.10

bump jackson-databind from 2.18.6 to 2.18.10 to pick up cumulative fixes from the 2.18.x line

* chore(deps): upgrade logback to 1.3.16 and slf4j to 2.0.17

1. bump logback-classic from 1.2.13 to 1.3.16 and slf4j-api,
   jcl-over-slf4j, jul-to-slf4j from 1.7.36 to 2.0.17; logback 1.3
   requires the slf4j 2.0 provider model, and 1.3.16 is the last 1.3.x
   release and the ceiling for the x86_64 JDK 8 build, since 1.5.x
   requires JDK 11
2. rename DelayingShutdownHook to DefaultShutdownHook in the toolkit
   logback.xml; logback 1.3 removed the old class and only auto-maps
   the legacy name with a startup warning
3. drop the CONSOLE appender from the toolkit logback.xml; no logger
   ever referenced it, so it never emitted output on 1.2 either, and
   logback 1.3 now flags it with an unreferenced-appender warning
4. accept one known 1.3.x behavior change: SizeAndTimeBasedRollingPolicy
   now throttles its maxFileSize comparison to once per 60s
   (SimpleInvocationGate) instead of the adaptive ~100-800ms gate of
   1.2.13, so under sustained heavy logging a file can overshoot the
   500MB cap by up to 60s of writes before the %i rollover fires;
   time-based rollover and totalSizeCap/maxHistory cleanup are ungated
   and unaffected
5. note for operators running a custom --log-config file: well-formed
   1.2-era configs using standard elements keep working unchanged
   (jmxConfigurator degrades to an ignored-property warning, the legacy
   shutdown hook name is auto-mapped), and malformed XML still fails
   fast via TronError(LOG_LOAD) exactly as on 1.2; however, a config
   that references an uninstantiable class (e.g. a custom appender
   missing from the classpath) now aborts the whole appender-ref phase
   instead of losing just that one appender, so the node starts with no
   log output while the ERROR statuses are printed to stdout by
   LogService

* chore(deps): upgrade commons-lang3/collections4 and drop commons-math

1. bump commons-lang3 from 3.4 to 3.20.0; the runtime classpath already
   resolved 3.18.0 through libp2p 2.2.9's transitive requirement, so
   align the declaration with what actually ships and move past the
   CVE-2025-48924 range that the nominal 3.4 still sits in
2. bump commons-collections4 from 4.1 to 4.6.0
3. remove commons-math 2.2; no source file imports
   org.apache.commons.math and nothing else in the dependency graph
   requests it

* chore(deps): remove joda-time and use JDK time APIs

1. drop the joda-time 2.3 dependency.
2. replace the six new DateTime(millis) log-formatting call sites in
   DynamicPropertiesStore, DposTask and DposService with a new
   Time.getIsoTimeString helper backed by java.time; its formatter
   (yyyy-MM-dd'T'HH:mm:ss.SSSXXX in the system zone) reproduces joda's
   DateTime.toString() output byte for byte where the JDK and joda 2.3
   time-zone databases agree (UTC nodes are unaffected); zones whose
   rules changed after joda's 2013-era tzdb, e.g. Europe/Moscow, now
   render the corrected offset for the same instant.
3. replace DateTime.now() day arithmetic in four test classes with the
   java.time equivalent, ZonedDateTime.now().minusDays(n)/plusDays(n)
   .toInstant().toEpochMilli(), keeping joda's calendar semantics
   one-to-one, and map plain DateTime.now().getMillis() to
   System.currentTimeMillis()
* feat(api): sanitize HTTP API error responses

Standard HTTP error paths used to expose internal details to clients:
Util.processError prefixed every message with the Java exception class
name, several servlets printed raw Throwable.getMessage() directly, and
the two solidity query endpoints returned bare-text error bodies.

Centralize the client-facing text decision in Util.processError:

* keep the raw non-blank message only for the exact runtime types
  JsonFormat.ParseException, ContractValidateException and
  MaintenanceUnavailableException; a null, empty or whitespace-only
  message falls back to "internal server error"
* preserve the events-deprecation message only for the exact
  IllegalArgumentException type carrying EVENTS_DEPRECATED_MSG
* write the fixed rate-limit and INVALID address messages, along with
  existing GetBlock validation messages, through the package-private
  writeAuditedError helper; these audited callers bypass exception
  classification, and printErrorMsg is private to the shared writer
* return {"Error":"internal server error"} for every other exception,
  with no exception class name

Client-visible changes:

* all processError-based error bodies lose the "class <FQCN> : "
  prefix; unclassified raw messages become "internal server error"
* the rate-limit rejection body becomes
  {"Error":"lack of computing resources"} on every endpoint extending
  RateLimiterServlet, including full-node, solidity and PBFT /jsonrpc
* gettransactionbyid / gettransactioninfobyid on solidity return
  standard {"Error":...} JSON instead of bare text
* validateaddress, getBrokerage and getReward replace leaked library
  messages in their failure branches with existing fixed texts; the
  "INVALID address" body is now written via writeAuditedError and loses
  the space after the colon
* getblock keeps its exact error bodies (refactor only)

Cover Solidity transaction and transaction-info GET/POST input errors,
backend failures, successful lookups and missing records directly with
mocked Wallet calls and in-memory requests and responses. Replace the
transaction servlet tests that accidentally exercised POST in both cases,
changed global stdout and used a shared temporary response file.

Verify both endpoint and global rate-limit rejections across the three
JSON-RPC servlet variants, including status, response body and the absence
of business dispatch on rejection.

HTTP status codes, success responses, request validation rules and
gRPC behavior are unchanged. JSON-RPC behavior is unchanged except for
the shared HTTP rate-limit response described above.

Closes tronprotocol#6936

* fix(api): keep server-side failure logging at error level

The previous commit routed four catch-all blocks through the shared
processError entry point, which logs at debug. Those four catches cover
server-side work only: getburntrx, getnodeinfo and getpendingsize read
no request parameters, and in getreward malformed addresses are already
handled by the preceding DecoderException | IllegalArgumentException
catch. Their failures therefore left no trace under the default log
configuration, where the API topic is INFO.

Add a dedicated processServerError entry point that logs at error and
then applies the same sanitization, and use it at those four call sites.
Logging the exception once inside the helper keeps a single record at
any log level, instead of pairing an error log in the servlet with the
debug log in the shared path.

The shared Exception entry point keeps debug on purpose: its callers
also cover request parsing, so an unauthenticated client can fail it
cheaply and repeatedly, and an unconditional stack trace per request
would amplify that into log pressure. Distinguishing client from server
faults on that path is the parameter/internal split tracked as follow-up
in tronprotocol#6936.

Client-facing responses are unchanged.
@3for
3for force-pushed the feat/docker branch 2 times, most recently from fe06a73 to e01bded Compare September 18, 2026 10:46
halibobo1205 and others added 14 commits September 24, 2026 16:48
)

* feat(vm): add optimized storage layout and alias write checks

* feat(api): separate outbound JSON parsing

* perf(vm): cache alias check at Storage construction

---------

Co-authored-by: halibobo1205 <halibobo1205@gmail.com>
tronprotocol#6982)

* refactor(api): replace response copy wrappers with Jetty content count

* fix(api): preserve legacy Unicode replacement in JsonFormat

Use JDK UTF-8 conversion to replace malformed surrogates in parser error descriptions and isolated low surrogates in string output. Match the legacy writer's question-mark replacement while preserving valid Unicode and parsing behavior.

Cover identifier, ABI escape, numeric errors, and low-surrogate output with regression tests, including strict UTF-8 checks through native Jetty responses.
waynercheung and others added 19 commits October 12, 2026 16:30
* fix(jsonrpc): handle null parameters consistently

Validate nullable JSON-RPC arguments before they are dereferenced.
Return stable protocol errors instead of jsonrpc4j's unhandled -32001
fallback, which exposes Java exception types.

Validate eth_call's required transaction argument before its block
selector. The double-null case now returns -32602 instead of -32600.

Align eth_uninstallFilter lookup misses with geth: null, unknown and
already removed IDs return false. Empty and non-hexadecimal strings
also return false when no filter matches, without new format checks.
Non-null lookup misses now return false instead of -32000.
Use the value returned by remove to report whether a filter was removed
and avoid the race between checking and removing the same entry.

Remove the obsolete uninstall ItemNotFoundException declaration and
error mapping. Java binaries remain compatible, but source callers
catching that checked exception, or implementations still declaring it,
may need adjustment when recompiled.
Keep getFilterChanges and getFilterLogs on their existing lookup-miss
errors, and preserve request-source precedence and filter isolation.

Treat null block-detail flags as false and retain DTO defaults for
explicitly null optional fields. Preserve filter wildcard semantics.

Add binding, service, wire and VM execution regressions for null
arguments, error contracts, filter removal, source isolation and
concurrent uninstall results.

* fix(jsonrpc): reject null required positional parameters

Return invalid params for an explicit null filter ID in
eth_uninstallFilter, eth_getFilterChanges and eth_getFilterLogs, and for
a null fullTransactionObjects flag in eth_getBlockByHash and
eth_getBlockByNumber. The block hash or selector is still validated
first, and the flag is checked before any block lookup, so the result
does not depend on whether the block exists. Unknown non-null filter
IDs keep their existing results.

This replaces the earlier null-as-false handling of the flag and the
filter ID. go-ethereum v1.17.6 rejects null for these required
arguments; earlier releases decoded it as a zero value.

* refactor(jsonrpc): share the null check for required parameters

Route the null checks for the ten required JSON-RPC parameter
positions through one requireParam helper instead of repeating the
same if/throw block at each site. The "invalid params" and "invalid
filter request" messages are unchanged and are now both private
constants in TronJsonRpcImpl.

Remove the null check from the LogFilter constructor. In production
code, LogFilter is only built through LogFilterWrapper from
eth_getLogs and eth_newFilter, and both reject a null filter request
before reaching it, so the constructor check and its unit test are
dropped. Null filter requests remain covered at the service and wire
level.

Extract getBlockByHash(byte[]) and use it from ethGetBlockByHash and
getBlockByJsonHash, so ethGetBlockByHash no longer inlines the block
lookup. The validation order is unchanged.

No behavior change.

* test(jsonrpc): use the three-argument TronJsonRpcImpl constructor

Adapt the tests added for null parameter handling to the constructor
that now takes the Manager, and drop the removed setManager call.
Tests built only from mocks pass a mocked Manager, and tests backed by
the shared database pass dbManager, matching the existing tests.
…col#6985)

Replace jsonrpc4j's unhandled -32001 fallback with a fixed -32603
response so unmapped exception types and raw messages no longer reach
clients. Keep the documented -32001 contract for net_version and
eth_chainId through explicit mappings with fixed message and data.

Propagate VirtualMachineError, ThreadDeath, LinkageError, and TronError
through the JSON-RPC boundary. Scan complete cause chains without
allocating a visited set, detecting cycles without missing fatal causes.
Classify Throwables escaping dispatch before logging or mapping them.

Before rethrowing an Error, make a best-effort attempt to commit an
empty HTTP 500 so the container does not render its default error page.
Preserve the original error if cleanup fails, and leave an already
committed response untouched.

Route single requests through handleRequest because the servlet handle
API catches Throwable. Catch RuntimeException, IOException, and Error at
both dispatch boundaries, then propagate a classified fatal cause or
return -32603 when a response is appropriate. This also covers non-fatal
Errors from interceptor hooks. Discard partial dispatch output on
failure.

Recover each failed batch element without discarding earlier results or
skipping later requests. Share internal-error construction across
serialization, dispatch, and response-parsing failures. Count only the
replacement when parsing fails, retaining existing overflow rules and
the current single/batch handling of requests without an id.

Log unmapped exceptions, chain-identity failures, and asynchronous
log-query failures and interruptions at DEBUG with the Throwable,
classifying fatal causes before logging the failures. Log batch
request-serialization and response-parsing recoveries at DEBUG with the
element index and the Throwable. Log the first escaped dispatch failure
in each batch at ERROR with the Throwable; repeats use DEBUG with only
the index and exception type. Restore interrupted status for
asynchronous log queries.

Reject Boolean, object, and array request IDs before dispatch with
-32600 and id:null. Reject non-null scalar params with -32600, preserve
valid IDs, and isolate invalid batch elements so their siblings run.
Missing, null, array, and object params keep their existing semantics.

Remove the obsolete request replay wrapper and HTTP status provider.
Add resolver, servlet, embedded-Jetty, chain-identity, asynchronous
failure, dispatch-contract, and request-envelope regression coverage.
Exercise the real HTTP interceptor chain and observe commitment without
performing it on the servlet's behalf.

Test interceptor Errors with a real server and preserve the existing
notification response-suppression rules with characterization tests.

Add a package-private server injection seam for servlet tests without
changing production initialization.
The node.rpc.maxRstStream and secondsPerWindow options defaulted to
0, which left the HTTP/2 RST_STREAM frame limit unset, and the limit
was configured only when both values were positive. Give them
sensible defaults, validate them at load time, and apply the limit
unconditionally so the configuration behaves consistently.

- NodeConfig: default maxRstStream/secondsPerWindow to 1000/5.
  In postProcess, reject negative values for either option and reject
  Integer.MAX_VALUE specifically for maxRstStream, which grpc-java
  treats as disabling the limit. Independently replace each zero with
  its default and log a warning.
- RpcService: always configure maxRstFramesPerWindow from the config
  values; validation stays solely in NodeConfig, consistent with the
  neighboring maxConcurrentCallsPerConnection option.
- reference.conf, docs/configuration.md and config/README.md:
  document the new defaults and the 0 fallback for compatibility.
- Tests: pin the Java and reference defaults, preserve positive
  overrides, and verify PING at the RST limit followed by GOAWAY
  after one additional reset using a prebuilt HTTP/2 frame burst.

Note: an explicit 0 now falls back to the default instead of leaving
the limit unset, and maxRstStream = 2147483647 is rejected at startup.
1. bump jsonrpc4j to 1.7, the latest release
2. add jakarta.servlet-api 5.0.0 as a test dependency; jsonrpc4j
   declares it optional, and Mockito needs it on the classpath to mock
   JsonRpcServer, whose new handle overload takes jakarta servlet
   types; the servlet dispatches through handleRequest and never
   calls that overload
3. drop javax.jws-api; jsonrpc4j 1.7 logs a missing
   javax.jws.WebParam at debug level instead of error, and no code
   uses @WebParam
4. accept one wire-level behavior change: when a positional parameter
   cannot be converted to the declared Java type (for example an
   object or array where a string is expected), the response changes
   from -32700 "JSON parse error" with id "null" to -32602 "Failed
   to read method parameter at index N" with the request id preserved
   and no data field; this also applies to batch entries, and a
   request without an id still gets this error with id null; the
   error is produced inside jsonrpc4j and does not pass through the
   error resolver
5. transitive changes on the runtime classpath: commons-codec 1.15 ->
   1.16.1, httpcore 4.4.13 -> 4.4.16, httpcore-nio 4.4.5 -> 4.4.16,
   jakarta.jws-api 3.0.0 added, net.iharder:base64 removed; slf4j-api
   and jackson keep resolving to 2.0.17 and 2.18.10
6. update the dispatch contract test, extend the error integration
   test with conversion errors in single, batch and id-less requests,
   and update the dependency verification metadata accordingly
Accept upstream removal of the root .dockerignore because Docker builds use docker/ as their build context. Preserve the branch's Docker image, lifecycle, documentation and CI changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.