Skip to content

feat(ci): add multinode integration test workflow#7

Open
warku123 wants to merge 11 commits into
developfrom
feat/ci-multinode
Open

feat(ci): add multinode integration test workflow#7
warku123 wants to merge 11 commits into
developfrom
feat/ci-multinode

Conversation

@warku123
Copy link
Copy Markdown
Owner

@warku123 warku123 commented May 12, 2026

Summary

  • Adds a workflow_dispatch + pull_request triggered workflow that runs the full multinode integration test suite (--multinode --clean) against a 3-witness docker-compose stack
  • FullNode.jar is built from the PR head and injected via a local Docker wrapper image (TRON_IMAGE=java-tron-local:pr)
  • DinD path-alignment: compose configs are extracted from the integration-test image to the host workspace so --project-directory resolves for the host daemon
  • --network host is used so the test container can reach witness nodes at 127.0.0.1:50051-50053

Test plan

  • PR trigger fires the workflow on develop
  • FullNode.jar builds successfully
  • 3-witness compose stack starts and nodes become healthy
  • Multinode tests pass (gradle test + multinodeTest)
  • Reports and witness logs are collected as artifacts
  • Compose stack is torn down after tests

🤖 Generated with Claude Code


Summary by cubic

Adds a multinode CI workflow (Full), configurable blocking vs non‑blocking API rate limiting, and enforces event‑plugin >= 3.0.0 to prevent silent failures.

  • New Features

    • CI: Run multinode integration tests on PRs and manual dispatch via “Integration Test Multinode (Full)”. Build FullNode.jar, inject via java-tron-local:pr, use host networking, extract compose files to the host, bind‑mount the multinode dir for Jacoco, collect reports/logs, and clean up. Uses troninfra/troninfra-ci:latest (public).
    • Rate limiting: Add rate.limiter.apiNonBlocking (default false) and blocking acquire() paths, with an acquirePermit() dispatcher on IRateLimiter and GlobalRateLimiter.
    • Events: Enforce event‑plugin >= 3.0.0 via pf4j; default event.subscribe.native.useNativeQueue to false.
  • Bug Fixes

    • --es now takes effect: apply event config after CLI and build plugin config when enabled only via CLI.
    • Rate limiting: keep per‑endpoint‑before‑global and IP‑before‑global ordering; fail closed if IP limiter load fails; HTTP/gRPC now use acquirePermit().
    • Avoid NPE by null‑checking relayNodes in PeerConnection.

Written for commit 564770a. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Configurable rate-limiter non-blocking mode (immediate rejection vs. waiting)
    • Event plugin version compatibility enforcement
    • Multinode integration test workflow added to CI
  • Bug Fixes

    • Prevented a potential null-pointer in relay node handling
  • Configuration Changes

    • Event native queue now defaults to disabled
    • Added rate.limiter.apiNonBlocking (defaults to false)
  • Tests

    • Expanded rate-limiter and event-plugin test coverage

Review Change Stack

317787106 and others added 6 commits May 10, 2026 16:09
* fix(config): remove redundant JSON-RPC config fields and consolidate parameter binding

Remove maxBlockFilterNum, maxAddressSize, and maxRequestTimeout from NodeConfig/CommonParameter
since they were superseded by the jsonRpcMaxBatchSize/jsonRpcMaxResponseSize parameters.
Consolidate the config binding path so all JSON-RPC size limits flow through a single
reference.conf entry, and clean up stale test-config fixtures.

* fix bug of NodeConfigTest

* remove allowShieldedTransactionApi from reference.conf

* add testcase of external.ip is null

* change comment

* fix the bug of --es and 7 failed testcases
…onprotocol#6760)

Pre-3.0.0(The previous event-plugin public release is 2.2.0)
event-plugin builds still link against com.alibaba.fastjson, which
was removed from java-tron in tronprotocol#6701. When such a plugin is loaded, the
NoClassDefFoundError surfaces inside the plugin's own worker thread and is
swallowed by its catch(Throwable) handler. The node keeps running while
silently dropping every trigger, leaving operators with no signal that the
event subscription is broken.

Enforce a minimum Plugin-Version at startup in EventPluginLoader.startPlugin
using pf4j's VersionManager (semver). When the descriptor version is below
3.0.0, log a clear upgrade hint and return false; the existing call chain in
Manager.startEventSubscribing wraps that into TronError(EVENT_SUBSCRIBE_INIT)
and aborts node startup instead of silently degrading.
…rs (tronprotocol#6761)

- Add `rate.limiter.apiNonBlocking` switch (default false). When off, callers wait for a permit; when on, they reject immediately and shed load.
- Wire the switch through `RateLimiterConfig` → `Args` → `CommonParameter`; update `reference.conf` / `config.conf`.
- Add blocking `acquire()` paths on `IRateLimiter`, `GlobalRateLimiter`, and `QpsStrategy` / `IPQpsStrategy` / `GlobalPreemptibleStrategy`. Only the semaphore-based strategy is bounded (2s); the QPS-based paths use unbounded Guava `RateLimiter.acquire()`.
- Introduce `acquirePermit()` dispatcher (default method on `IRateLimiter`; static on `GlobalRateLimiter`) that picks blocking vs non-blocking based on the switch.
- Swap `tryAcquire` → `acquirePermit` at the `RateLimiterServlet` and `RateLimiterInterceptor` call sites; preserve per-endpoint-before-global ordering to avoid consuming global quota on per-endpoint rejection.
- Extract `loadIpLimiter()` in `GlobalRateLimiter` and `loadLimiter()` in `IPQpsStrategy` to share cache+exception handling between `tryAcquire` and `acquire`.
- Add unit tests covering dispatcher routing (both directions), acquire IP-before-global ordering, and IP-loader-failure fail-closed behaviour.
Adds a workflow_dispatch-only workflow that runs the full multinode
test suite (--multinode --clean) against a 3-witness docker-compose
stack. FullNode.jar is built from the PR head and injected via a local
Docker wrapper image (TRON_IMAGE=java-tron-local:pr).

DinD path-alignment: the compose configs are extracted from the
integration-test image to the host workspace so --project-directory
resolves for the host daemon. --network host is used so the test
container can reach the witness nodes at 127.0.0.1:50051-50053.
workflow_dispatch requires the file to exist on the default branch,
which makes it impossible to trigger from a feature branch. Add
pull_request trigger on develop and release_** so the workflow fires
when a PR targeting those branches is opened or updated.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

This PR adds a configurable rate-limiter mode (blocking vs non-blocking), implements blocking acquire() paths and adapter dispatch, enforces a minimum event-plugin version and reorders event config application, updates tests, and adds a multinode integration GitHub Actions workflow.

Changes

Rate Limiter API Non-Blocking Mode

Layer / File(s) Summary
Rate limiter configuration and defaults
common/src/main/java/org/tron/common/parameter/CommonParameter.java, common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java, common/src/main/resources/reference.conf, framework/src/main/resources/config.conf, common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java
New apiNonBlocking boolean field added to configuration beans with false default; reference and framework configs updated; test coverage added for default and override scenarios.
Rate limiter strategies: blocking acquire() methods
framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java, framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java, framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java
Blocking acquire() methods added to strategy implementations; GlobalPreemptibleStrategy adds timed semaphore acquisition with interrupt handling; IPQpsStrategy refactors per-IP limiter loading into shared helper.
Rate limiter adapters and interface dispatch
framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java, framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java, framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java, framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java, framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java
Adapter classes implement blocking acquire() delegation to strategies; IRateLimiter interface adds acquirePermit() default that selects tryAcquire() vs acquire() based on apiNonBlocking configuration.
GlobalRateLimiter per-IP loading and dispatch
framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java
Per-IP limiter loading refactored into loadIpLimiter() helper; tryAcquire(), acquire(), and acquirePermit() updated to use shared loader and dispatch logic.
RateLimiterServlet and RateLimiterInterceptor integration
framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java, framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java
Both servlet and interceptor switched from tryAcquire() to acquirePermit() for per-endpoint limiter decisions, delegating blocking/non-blocking behavior to configuration.
Rate limiter test coverage
framework/src/test/java/org/tron/core/services/http/RateLimiterServletTest.java, framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java, framework/src/test/java/org/tron/core/services/ratelimiter/RateLimiterInterceptorTest.java, framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java
Tests updated to verify acquirePermit() semantics, per-IP and global limiter interaction ordering, dispatch routing based on configuration, and release/cleanup behavior across all flows.

Event Configuration and Plugin Version Management

Layer / File(s) Summary
Event plugin version validation
framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java, framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java
EventPluginLoader enforces minimum plugin version (3.0.0) via version comparison before startup; test validates rejection of lower/empty versions and acceptance of compatible versions.
Event config application ordering and config defaults
framework/src/main/java/org/tron/core/config/args/Args.java, common/src/main/java/org/tron/core/config/args/EventConfig.java, common/src/main/resources/reference.conf
applyEventConfig() moved to execute after CLI parameter application; contractParse switch set in applyConfigParams for later CLI override; EventConfig.NativeConfig useNativeQueue default changed from true to false.
Event config and ordering test coverage
framework/src/test/java/org/tron/core/config/args/ArgsTest.java, framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java
Tests for event config application ordering, CLI event subscription enabling, and test initialization with global Args setup/teardown helpers.

CI Workflow and Miscellaneous Fixes

Layer / File(s) Summary
Multinode integration test GitHub Actions workflow
.github/workflows/integration-test-multinode.yml
New workflow triggered on PR and manual dispatch that builds FullNode.jar, creates local Docker image, extracts compose configs from integration-test image, runs multinode test container with host networking, and uploads aggregated test reports and logs.
PeerConnection safety guard
framework/src/main/java/org/tron/core/net/peer/PeerConnection.java
PeerConnection.setChannel adds null guard before relayNodes stream check to prevent NullPointerException.

Sequence Diagram(s)

sequenceDiagram
  participant Servlet as RateLimiterServlet
  participant PerEndpoint as IRateLimiter
  participant Global as GlobalRateLimiter
  participant Strategy as RateLimiterStrategy

  Servlet->>PerEndpoint: acquirePermit(runtimeData)
  alt apiNonBlocking = true
    PerEndpoint->>Strategy: tryAcquire(runtimeData)
  else apiNonBlocking = false
    PerEndpoint->>Strategy: acquire(runtimeData)
  end
  Strategy-->>PerEndpoint: permit result
  PerEndpoint-->>Servlet: per-endpoint acquired?
  alt per-endpoint acquired
    Servlet->>Global: acquirePermit(runtimeData)
    alt apiNonBlocking = true
      Global->>Global: tryAcquire(ip)
    else apiNonBlocking = false
      Global->>Global: acquire(ip)
    end
    Global-->>Servlet: global permit result
  else per-endpoint rejected
    Servlet-->>Servlet: deny request / skip global
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A rabbit nudges the limiter gate,
Blocking waits and non-blocking fate.
Plugins checked, configs in line,
Multinode tests run on CI time.
Logs collected, reports take flight — hop!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ci): add multinode integration test workflow' clearly and accurately summarizes the primary change—adding a new GitHub Actions CI workflow for multinode integration testing, which is the explicit focus of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ci-multinode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
.github/workflows/integration-test-multinode.yml (1)

41-52: ⚡ Quick win

Add verification that FullNode.jar was built successfully.

The workflow copies build/libs/FullNode.jar without verifying it exists. If the build silently fails or produces no artifact, the Docker build will fail with a less clear error message.

✅ Proposed verification step

Add a verification step between the build and Docker image creation:

      - name: Verify FullNode.jar exists
        run: |
          if [ ! -f build/libs/FullNode.jar ]; then
            echo "Error: FullNode.jar not found after build"
            exit 1
          fi
          ls -lh build/libs/FullNode.jar
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/integration-test-multinode.yml around lines 41 - 52, Add a
verification step after the "Build FullNode.jar" job to ensure
build/libs/FullNode.jar exists before the Docker build: create a step named
"Verify FullNode.jar exists" that checks for the file build/libs/FullNode.jar,
prints an error and exits non‑zero if missing, and runs an ls -lh on the
artifact so the workflow fails early with a clear message rather than letting
the subsequent docker build fail cryptically.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/integration-test-multinode.yml:
- Around line 70-85: Add a brief documented acknowledgment in the Run multinode
full tests step about the security tradeoffs of using --network host and
mounting /var/run/docker.sock, and immediately mitigate by pinning the
warku123/java-tron-integration-test:latest image to a reviewed digest (replace
image reference with its SHA), add a pre-step to scan that pinned image (e.g.,
Trivy) before running docker run, and note alternatives (rootless Docker or
sysbox runtime, avoid socket mount if possible, or host a vetted image in the
tronprotocol org and enable GitHub Actions OIDC for private registry pulls) so
reviewers can see the risk and the applied mitigations.
- Around line 44-56: Replace the unpinned :latest image tags used in the
workflow steps with digest-pinned references to ensure reproducible, secure
builds: update the Dockerfile base line in the "Build local java-tron Docker
image (wraps PR-built FullNode.jar)" step so the FROM uses a specific digest
(e.g., tronprotocol/java-tron@sha256:...), and change the "Pull integration-test
image" step to pull the warku123/java-tron-integration-test image by digest
(warku123/java-tron-integration-test@sha256:...); obtain the correct digests
using docker pull + docker inspect and substitute those digest-pinned strings in
place of tronprotocol/java-tron:latest and
warku123/java-tron-integration-test:latest so the workflow always uses exact
image versions.

In
`@framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java`:
- Around line 34-45: The current acquire(RuntimeData) uses blocking
RateLimiter.acquire() (both for per-IP limiter returned by loadIpLimiter(ip) and
the global rateLimiter) which can block request threads; change both calls to
non-blocking tryAcquire with a reasonable timeout (e.g., a small constant like
PERMIT_TIMEOUT_MS) and if tryAcquire returns false immediately return false so
callers (via acquirePermit()/RateLimiterServlet.service() and
RateLimiterInterceptor.interceptCall()) fail fast; ensure you import/use the
same TimeUnit and update any callers/tests expecting blocking behavior
accordingly.

---

Nitpick comments:
In @.github/workflows/integration-test-multinode.yml:
- Around line 41-52: Add a verification step after the "Build FullNode.jar" job
to ensure build/libs/FullNode.jar exists before the Docker build: create a step
named "Verify FullNode.jar exists" that checks for the file
build/libs/FullNode.jar, prints an error and exits non‑zero if missing, and runs
an ls -lh on the artifact so the workflow fails early with a clear message
rather than letting the subsequent docker build fail cryptically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1ea3ef9-8e89-4e8b-8a90-4b31425cda8a

📥 Commits

Reviewing files that changed from the base of the PR and between 381d369 and fb5ca63.

📒 Files selected for processing (28)
  • .github/workflows/integration-test-multinode.yml
  • common/src/main/java/org/tron/common/parameter/CommonParameter.java
  • common/src/main/java/org/tron/core/config/args/EventConfig.java
  • common/src/main/java/org/tron/core/config/args/RateLimiterConfig.java
  • common/src/main/resources/reference.conf
  • common/src/test/java/org/tron/core/config/args/RateLimiterConfigTest.java
  • framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java
  • framework/src/main/java/org/tron/core/config/args/Args.java
  • framework/src/main/java/org/tron/core/net/peer/PeerConnection.java
  • framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java
  • framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java
  • framework/src/main/resources/config.conf
  • framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java
  • framework/src/test/java/org/tron/core/config/args/ArgsTest.java
  • framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java
  • framework/src/test/java/org/tron/core/services/http/RateLimiterServletTest.java
  • framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java
  • framework/src/test/java/org/tron/core/services/ratelimiter/RateLimiterInterceptorTest.java
  • framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java

Comment on lines +44 to +56
- name: Build local java-tron Docker image (wraps PR-built FullNode.jar)
run: |
mkdir -p /tmp/tron-image
cp build/libs/FullNode.jar /tmp/tron-image/
cat > /tmp/tron-image/Dockerfile <<'EOF'
FROM tronprotocol/java-tron:latest
COPY FullNode.jar /java-tron/lib/FullNode.jar
EOF
docker build -t java-tron-local:pr /tmp/tron-image

- name: Pull integration-test image
run: docker pull warku123/java-tron-integration-test:latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

What is the latest stable version of tronprotocol/java-tron Docker image?

💡 Result:

Latest stable version of the tronprotocol/java-tron Docker image (the DockerHub “latest” tag) is: latest (digest sha256:a9e70a6c3…). [1] How this maps to a specific Java-Tron release: the Docker image “latest” is updated from the repository’s master branch, but DockerHub does not expose a human-readable Java-Tron version number on the page; it only provides the “latest” tag and its digest. [2] If you need the exact pinned tag (e.g., GreatVoyage-vX.Y.Z) rather than “latest”, you’ll need to check the specific tag listings or pull by digest and inspect the container’s reported version at runtime.

Citations:


🏁 Script executed:

# Read the full workflow file to verify all claims
cat -n .github/workflows/integration-test-multinode.yml | head -100

Repository: warku123/java-tron

Length of output: 4902


🌐 Web query:

warku123 java-tron-integration-test Docker image official tronprotocol

💡 Result:

Official TRON Java Docker image: tronprotocol/java-tron (Docker Hub). [1] What’s going on with “warku123 java-tron integration test Docker image”: warku123/java-tron is a GitHub fork of tronprotocol/java-tron, not an official Docker image name. [2] Official “docker image” details (tronprotocol): - The Docker Hub listing identifies “tronprotocol/java-tron” as the official Java implementation of the TRON protocol, updated about 1 month ago, with tag information including “latest”. [1] - tronprotocol’s documentation in tron-docker states there are two official Java-Tron Docker images: tronprotocol/java-tron (for Mainnet; mostly usable for Nile) and tronnile/java-tron (for Nile testnet hard-fork testing when needed). [3][4] - The same docs show example usage: docker run -it --name tron-node -d --memory="16g" -p 8090:8090 -p 50051:50051 tronprotocol/java-tron. [3][4] Integration testing from Docker (what the official tooling provides): - The official tron-docker repository includes “Gradle Docker” to automate Docker image builds and testing of java-tron. [3][4] Practical guidance (how to use the official image for testing): 1) Pull the official image: docker pull tronprotocol/java-tron [5] 2) Run a node container (Mainnet by default per docs/examples): docker run -it --name tron-node -d --memory="16g" -p 8090:8090 -p 50051:50051 tronprotocol/java-tron [3][4] 3) For CI/integration workflows, use the official tron-docker repo tooling (Gradle Docker / test-related commands) rather than relying on a third-party fork/image name. [3][4] If you want, paste the exact Docker image reference you saw for “java-tron-integration-test” (e.g., full repo/name:tag). I can verify whether it’s official (tronprotocol/*) or just a community/testing fork.

Citations:


Pin Docker image versions for reproducibility and supply chain security.

Using :latest tags makes the workflow non-reproducible and introduces supply chain risk:

  1. Line 49: tronprotocol/java-tron:latest could change between runs
  2. Lines 55, 66, 84: warku123/java-tron-integration-test:latest (from a community fork, not official tronprotocol) receives full Docker daemon access (line 78), creating a supply chain vulnerability if the image or warku123's account is compromised

Unpinned images can introduce breaking changes or malicious code silently.

Recommended fix: Pin to specific digests

Replace :latest tags with digest-pinned versions:

-          FROM tronprotocol/java-tron:latest
+          FROM tronprotocol/java-tron:GreatVoyage-v4.7.6@sha256:ACTUAL_DIGEST_HERE
-        run: docker pull warku123/java-tron-integration-test:latest
+        run: docker pull warku123/java-tron-integration-test@sha256:ACTUAL_DIGEST_HERE

Update lines 66 and 84 similarly:

-          docker create --name it-extract warku123/java-tron-integration-test:latest
+          docker create --name it-extract warku123/java-tron-integration-test@sha256:ACTUAL_DIGEST_HERE
-            warku123/java-tron-integration-test:latest \
+            warku123/java-tron-integration-test@sha256:ACTUAL_DIGEST_HERE \

To get the digest: docker pull IMAGE:TAG && docker inspect --format='{{index .RepoDigests 0}}' IMAGE:TAG

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/integration-test-multinode.yml around lines 44 - 56,
Replace the unpinned :latest image tags used in the workflow steps with
digest-pinned references to ensure reproducible, secure builds: update the
Dockerfile base line in the "Build local java-tron Docker image (wraps PR-built
FullNode.jar)" step so the FROM uses a specific digest (e.g.,
tronprotocol/java-tron@sha256:...), and change the "Pull integration-test image"
step to pull the warku123/java-tron-integration-test image by digest
(warku123/java-tron-integration-test@sha256:...); obtain the correct digests
using docker pull + docker inspect and substitute those digest-pinned strings in
place of tronprotocol/java-tron:latest and
warku123/java-tron-integration-test:latest so the workflow always uses exact
image versions.

Comment on lines +70 to +85
- name: Run multinode full tests
run: |
# --network host: multinode tests talk to nodes via 127.0.0.1:50051 etc.
# DinD socket + HOST_WORKDIR path-alignment lets the container orchestrate
# the 3-witness compose stack via the host daemon.
# Don't override --workdir so the container's default /app entrypoint works.
docker run --name integration-multinode \
--network host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "${{ github.workspace }}:${{ github.workspace }}" \
-e HOST_WORKDIR="${{ github.workspace }}" \
-e TRON_IMAGE=java-tron-local:pr \
-e JAVA_HOME=/usr/lib/jvm/temurin-8 \
-e JAVA_HOME_17=/opt/java/openjdk \
warku123/java-tron-integration-test:latest \
--multinode --clean
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Document security tradeoffs of privileged container configuration.

This step uses two high-privilege patterns that warrant explicit acknowledgment:

  1. --network host (line 77): Removes network isolation, exposing all host network interfaces. The container can access any localhost service and bind to any host port.

  2. Docker socket mount (line 78): Grants full Docker daemon control, effectively root-equivalent access. The container can manipulate any container, mount any host path, and access the entire Docker environment.

While these are necessary for the Docker-in-Docker pattern, they create significant attack surface—especially combined with the unpinned personal repository image flagged above. If warku123/java-tron-integration-test:latest is compromised, an attacker gains full host network and Docker access.

Mitigation recommendations:

  1. Pin the integration-test image (see previous comment) to reduce supply chain risk
  2. Use official/verified images if available, or self-host in the tronprotocol organization
  3. Consider rootless Docker or sysbox runtime to reduce privilege escalation risk
  4. Add image scanning (e.g., Trivy) in a pre-step to detect vulnerabilities before granting Docker access
  5. Use GitHub Actions OIDC for image pulls if moving to a private registry

For immediate risk reduction, at minimum pin the integration-test image to a reviewed digest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/integration-test-multinode.yml around lines 70 - 85, Add a
brief documented acknowledgment in the Run multinode full tests step about the
security tradeoffs of using --network host and mounting /var/run/docker.sock,
and immediately mitigate by pinning the
warku123/java-tron-integration-test:latest image to a reviewed digest (replace
image reference with its SHA), add a pre-step to scan that pinned image (e.g.,
Trivy) before running docker run, and note alternatives (rootless Docker or
sysbox runtime, avoid socket mount if possible, or host a vetted image in the
tronprotocol org and enable GitHub Actions OIDC for private registry pulls) so
reviewers can see the risk and the applied mitigations.

Comment on lines +34 to +45
public static boolean acquire(RuntimeData runtimeData) {
String ip = runtimeData.getRemoteAddr();
if (!Strings.isNullOrEmpty(ip)) {
RateLimiter r = loadIpLimiter(ip);
if (r == null) {
return false;
}
r.acquire();
}
rateLimiter.acquire();
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Blocking acquire() calls on request handler threads can exhaust thread pool under high load.

The acquire() method calls RateLimiter.acquire() at lines 41 and 43, which are blocking operations that will wait indefinitely until a permit becomes available. This method is invoked via acquirePermit() from:

  • RateLimiterServlet.service() (Tomcat request handler threads)
  • RateLimiterInterceptor.interceptCall() (gRPC request handler threads)

When rate limits are exceeded under high load, request handler threads will block waiting for permits, which can:

  • Exhaust the fixed-size thread pool (both Tomcat and gRPC use bounded thread pools by default)
  • Cause cascading failures as new requests queue up waiting for threads
  • Lead to service unavailability

Consider one of these alternatives:

  1. Use tryAcquire(timeout) with a reasonable timeout to fail fast rather than block indefinitely
  2. Document that blocking mode requires unbounded thread pools and should be used carefully
  3. Move rate-limiting to a separate thread pool to isolate blocking operations
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java`
around lines 34 - 45, The current acquire(RuntimeData) uses blocking
RateLimiter.acquire() (both for per-IP limiter returned by loadIpLimiter(ip) and
the global rateLimiter) which can block request threads; change both calls to
non-blocking tryAcquire with a reasonable timeout (e.g., a small constant like
PERMIT_TIMEOUT_MS) and if tryAcquire returns false immediately return false so
callers (via acquirePermit()/RateLimiterServlet.service() and
RateLimiterInterceptor.interceptCall()) fail fast; ensure you import/use the
same TimeUnit and update any callers/tests expecting blocking behavior
accordingly.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 28 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/integration-test-multinode.yml">

<violation number="1" location=".github/workflows/integration-test-multinode.yml:49">
P2: Use an immutable base image reference (version tag or digest) instead of `tronprotocol/java-tron:latest` for reproducible test environments.</violation>

<violation number="2" location=".github/workflows/integration-test-multinode.yml:55">
P1: Unpinned `:latest` tag from a personal Docker Hub account (`warku123/java-tron-integration-test`) combined with Docker socket access at line 78 creates a supply chain vulnerability. If the `warku123` account is compromised, the attacker gains full Docker daemon control (root-equivalent) on the runner. Pin to a digest (`@sha256:...`) or move the image to the `tronprotocol` organization to reduce this risk.</violation>
</file>

<file name="common/src/main/resources/reference.conf">

<violation number="1" location="common/src/main/resources/reference.conf:762">
P2: Changing the default `useNativeQueue` to false makes event subscription startup fail for configs that only enable `event.subscribe.enable` and rely on defaults.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

docker build -t java-tron-local:pr /tmp/tron-image

- name: Pull integration-test image
run: docker pull warku123/java-tron-integration-test:latest
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Unpinned :latest tag from a personal Docker Hub account (warku123/java-tron-integration-test) combined with Docker socket access at line 78 creates a supply chain vulnerability. If the warku123 account is compromised, the attacker gains full Docker daemon control (root-equivalent) on the runner. Pin to a digest (@sha256:...) or move the image to the tronprotocol organization to reduce this risk.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/integration-test-multinode.yml, line 55:

<comment>Unpinned `:latest` tag from a personal Docker Hub account (`warku123/java-tron-integration-test`) combined with Docker socket access at line 78 creates a supply chain vulnerability. If the `warku123` account is compromised, the attacker gains full Docker daemon control (root-equivalent) on the runner. Pin to a digest (`@sha256:...`) or move the image to the `tronprotocol` organization to reduce this risk.</comment>

<file context>
@@ -0,0 +1,116 @@
+          docker build -t java-tron-local:pr /tmp/tron-image
+
+      - name: Pull integration-test image
+        run: docker pull warku123/java-tron-integration-test:latest
+
+      - name: Extract compose configs to host (for DinD path-alignment)
</file context>

mkdir -p /tmp/tron-image
cp build/libs/FullNode.jar /tmp/tron-image/
cat > /tmp/tron-image/Dockerfile <<'EOF'
FROM tronprotocol/java-tron:latest
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Use an immutable base image reference (version tag or digest) instead of tronprotocol/java-tron:latest for reproducible test environments.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/integration-test-multinode.yml, line 49:

<comment>Use an immutable base image reference (version tag or digest) instead of `tronprotocol/java-tron:latest` for reproducible test environments.</comment>

<file context>
@@ -0,0 +1,116 @@
+          mkdir -p /tmp/tron-image
+          cp build/libs/FullNode.jar /tmp/tron-image/
+          cat > /tmp/tron-image/Dockerfile <<'EOF'
+          FROM tronprotocol/java-tron:latest
+          COPY FullNode.jar /java-tron/lib/FullNode.jar
+          EOF
</file context>


native = {
useNativeQueue = true
useNativeQueue = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Changing the default useNativeQueue to false makes event subscription startup fail for configs that only enable event.subscribe.enable and rely on defaults.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At common/src/main/resources/reference.conf, line 762:

<comment>Changing the default `useNativeQueue` to false makes event subscription startup fail for configs that only enable `event.subscribe.enable` and rely on defaults.</comment>

<file context>
@@ -758,7 +759,7 @@ event.subscribe = {
 
   native = {
-    useNativeQueue = true
+    useNativeQueue = false
     bindport = 5555
     sendqueuelength = 1000
</file context>
Suggested change
useNativeQueue = false
useNativeQueue = true

warku123 added 3 commits May 12, 2026 18:18
The jacocoagent.jar is downloaded at runtime by start-multinode.sh
into /app/docker/multi-node/ inside the container, but compose's
path-alignment resolves ./jacocoagent.jar to the HOST path
($GITHUB_WORKSPACE/docker/multi-node/). Without a bind-mount at
/app/docker/multi-node, the download never reaches the host and
compose fails with "Error opening zip file: /jacoco/jacocoagent.jar".
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/integration-test-multinode.yml">

<violation number="1" location=".github/workflows/integration-test-multinode.yml:54">
P2: This workflow logs into DockerHub with repository secrets on every `pull_request`, but secrets are unavailable for fork PRs, so the integration job will fail for external contributors.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic

Comment thread .github/workflows/integration-test-multinode.yml Outdated
@warku123 warku123 force-pushed the feat/ci-multinode branch from 0b667ac to f4d7ec6 Compare May 18, 2026 08:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.github/workflows/integration-test-multinode.yml (1)

22-40: ⚡ Quick win

Harden GitHub Actions for supply chain security.

Static analysis (zizmor) flags several security concerns:

  1. Unpinned actions (lines 24, 27, 33): Using version tags like @v5 or @v4 instead of commit SHAs allows supply chain attacks if the action repository is compromised.

  2. Credential persistence (line 24): The checkout action doesn't set persist-credentials: false, meaning git credentials remain available and could leak via artifacts.

♻️ Recommended fix
      - name: Checkout java-tron
-       uses: actions/checkout@v5
+       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+       with:
+         persist-credentials: false

      - name: Set up JDK 8
-       uses: actions/setup-java@v5
+       uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
        with:
          java-version: '8'
          distribution: 'temurin'

      - name: Cache Gradle packages
-       uses: actions/cache@v4
+       uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
        with:

Get current SHAs via: gh api repos/actions/checkout/git/ref/tags/v4.2.2 --jq .object.sha

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/integration-test-multinode.yml around lines 22 - 40, The
workflow uses unpinned action tags (actions/checkout@v5, actions/setup-java@v5,
actions/cache@v4) and leaves git credentials persistent; replace each action tag
with its corresponding commit SHA (pin to the immutable git ref) and add
persist-credentials: false to the Checkout step (the actions/checkout
invocation) to avoid leaking credentials; update the three uses entries
(checkout, setup-java, cache) to the SHAs you obtain from GitHub and keep the
descriptive names the same so the steps remain identifiable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/integration-test-multinode.yml:
- Around line 22-40: The workflow uses unpinned action tags
(actions/checkout@v5, actions/setup-java@v5, actions/cache@v4) and leaves git
credentials persistent; replace each action tag with its corresponding commit
SHA (pin to the immutable git ref) and add persist-credentials: false to the
Checkout step (the actions/checkout invocation) to avoid leaking credentials;
update the three uses entries (checkout, setup-java, cache) to the SHAs you
obtain from GitHub and keep the descriptive names the same so the steps remain
identifiable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 624ba88d-f44c-4396-ba19-36ac40d12f4f

📥 Commits

Reviewing files that changed from the base of the PR and between 993b604 and 564770a.

📒 Files selected for processing (1)
  • .github/workflows/integration-test-multinode.yml

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.

4 participants