|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +Guidance for AI coding assistants and new contributors working on java-tron: how to build, test, and navigate the codebase, plus the constraints that CI enforces and the invariants that must not be broken. For running a node, see the [README](./README.md) and [`docs/`](./docs). |
| 4 | + |
| 5 | +## Guidelines |
| 6 | + |
| 7 | +- **Keep changes minimal and focused.** Only modify code directly related to the task at hand. Do not refactor unrelated code, rename existing variables or functions for style, or bundle unrelated fixes into the same commit or PR. |
| 8 | +- **Do not add, remove, or update dependencies** unless the task explicitly requires it. Dependency changes in a consensus node are high-risk, need separate review, and require regenerating dependency verification metadata (see checklist step 6). |
| 9 | +- **Never hand-edit generated sources.** Protobuf / gRPC Java stubs are generated at build time from `protocol/src/main/protos/` (`core/`, `api/`) and are git-ignored. Rebuild after changing a `.proto`. |
| 10 | + |
| 11 | +## Build & Test |
| 12 | + |
| 13 | +Supported platforms: **Linux** and **macOS** only. The JDK requirement is determined by CPU architecture: **JDK 8** on x86_64, **JDK 17** on ARM64/aarch64 (Apple Silicon, AWS Graviton). The build fails fast if the JDK major version does not match the architecture. |
| 14 | + |
| 15 | +```bash |
| 16 | +./gradlew clean build -x test # build without tests |
| 17 | +./gradlew build # build with tests |
| 18 | +./gradlew test # run all tests |
| 19 | +./gradlew :framework:test # test one module |
| 20 | +./gradlew :framework:test --tests "org.tron.core.db.TronDatabaseTest" # one class |
| 21 | +./gradlew :framework:test --tests "org.tron.core.db.TronDatabaseTest.TestGetUnchecked" # one method |
| 22 | +./gradlew :framework:testWithRocksDb # RocksDB tests (x86 only) |
| 23 | +./gradlew jacocoTestReport # coverage report |
| 24 | +``` |
| 25 | + |
| 26 | +- Main entry point: `org.tron.program.FullNode`. |
| 27 | +- Tests also run in parallel in CI: `framework/build.gradle` sets `maxParallelForks` without checking the `CI` env var. Each module configures its own test parallelism in its `build.gradle`. |
| 28 | +- On ARM64/aarch64, only the RocksDB storage engine is supported; the build forces RocksDB and skips the LevelDB tests. |
| 29 | +- CI builds the full matrix: **JDK 8 / x86_64** (rockylinux, debian11) and **JDK 17 / aarch64** (macOS, ubuntu24). A change must compile on both. |
| 30 | + |
| 31 | +## Pre-Commit Checklist |
| 32 | + |
| 33 | +Run **all** applicable checks before committing. Each maps to a CI job that will otherwise fail the PR. |
| 34 | + |
| 35 | +### 1. Build |
| 36 | + |
| 37 | +```bash |
| 38 | +./gradlew clean build -x test |
| 39 | +``` |
| 40 | + |
| 41 | +### 2. Tests |
| 42 | + |
| 43 | +```bash |
| 44 | +./gradlew test |
| 45 | +``` |
| 46 | + |
| 47 | +### 3. Checkstyle |
| 48 | + |
| 49 | +Run exactly what CI runs (`.github/workflows/pr-check.yml`): |
| 50 | + |
| 51 | +```bash |
| 52 | +./gradlew :framework:checkstyleMain :framework:checkstyleTest :plugins:checkstyleMain |
| 53 | +``` |
| 54 | + |
| 55 | +The `pr-check.yml` Checkstyle gate covers only `framework` and `plugins`; `protocol` is checked by `protoLint` instead (see Protobuf below). `p2p` also applies Checkstyle, but with a separate config at the repo root (`config/checkstyle/checkStyleAll.xml`, distinct from the `framework/config/checkstyle/` one that `framework` and `plugins` use) — it is still enforced, by `./gradlew clean build` in `pr-build.yml`, just not part of the `pr-check.yml` command. A bare `./gradlew checkstyleMain` does not reproduce the gate. |
| 56 | + |
| 57 | +### 4. Forbidden `Math` usage |
| 58 | + |
| 59 | +CI (`.github/workflows/math-check.yml`) **fails the build on any use of `java.lang.Math`** anywhere in the repository: bare `Math.` calls, fully qualified `java.lang.Math.` calls (including static imports), and `import java.lang.Math`. Only `StrictMathWrapper.java` and `MathWrapper.java` are exempt. |
| 60 | + |
| 61 | +Use `org.tron.common.math.StrictMathWrapper` instead. Self-check before pushing with the script CI runs (`StrictMath.` and occurrences in strings or comments are ignored): |
| 62 | + |
| 63 | +```bash |
| 64 | +bash .github/scripts/check_math_usage.sh |
| 65 | +``` |
| 66 | + |
| 67 | +No output means the gate passes. |
| 68 | + |
| 69 | +This exists because `java.lang.Math` gives platform-dependent results for floating-point operations, which breaks cross-JVM determinism between the x86/JDK 8 and ARM64/JDK 17 builds. |
| 70 | + |
| 71 | +### 5. Config validation — only if you touched `reference.conf` |
| 72 | + |
| 73 | +```bash |
| 74 | +pip install -r .github/scripts/requirements.txt # pyhocon, required by the first script |
| 75 | +python3 .github/scripts/check_reference_conf.py common/src/main/resources/reference.conf |
| 76 | +python3 .github/scripts/check_reference_comments.py common/src/main/resources/reference.conf |
| 77 | +``` |
| 78 | + |
| 79 | +Both gates run in CI. The rules: |
| 80 | + |
| 81 | +- Every key path segment must match `^[a-z][a-zA-Z0-9]*$` (only the first character is constrained; acronyms such as `httpPBFTEnable` are fine). This is what `ConfigBeanFactory` bean-binding requires. |
| 82 | +- Total path depth ≤ **5**; each list/array step counts as one level. |
| 83 | +- Service-binding port values (leaf named `port` or ending in `Port`, outside arrays) must be unique; `0` and `-1` are reserved sentinels. |
| 84 | +- **Every key needs a comment** — inline on the same line, or on the immediately preceding line. Blank lines do not count. |
| 85 | + |
| 86 | +See [`docs/configuration-conventions.md`](./docs/configuration-conventions.md). |
| 87 | + |
| 88 | +### 6. Dependency verification — only if you touched dependencies |
| 89 | + |
| 90 | +`gradle/verification-metadata.xml` pins checksums for every resolved artifact (`verify-metadata=true`). Adding, removing, or upgrading any dependency requires regenerating it, or the build fails for everyone: |
| 91 | + |
| 92 | +```bash |
| 93 | +./gradlew --write-verification-metadata sha256 help |
| 94 | +``` |
| 95 | + |
| 96 | +Review the resulting diff — it must contain only the artifacts your change actually introduces. |
| 97 | + |
| 98 | +### 7. Do not commit binaries |
| 99 | + |
| 100 | +No `*.jar`, `build/`, logs, or database files — whether produced by the main build or as byproducts of investigation. |
| 101 | + |
| 102 | +## Module Layout |
| 103 | + |
| 104 | +| Module | Responsibility | |
| 105 | +|--------|----------------| |
| 106 | +| `framework` | Main entry (`org.tron.program.FullNode`); wires all modules; largest test suite | |
| 107 | +| `protocol` | Protobuf / gRPC definitions | |
| 108 | +| `chainbase` | Blockchain storage abstraction (LevelDB / RocksDB); snapshot & rollback | |
| 109 | +| `consensus` | Pluggable DPoS consensus engine | |
| 110 | +| `actuator` | Transaction execution; one Actuator class per transaction type | |
| 111 | +| `crypto` | Cryptographic primitives (depends only on `common`) | |
| 112 | +| `common` | Shared utilities | |
| 113 | +| `p2p` | Peer discovery, connection management and DNS-based node lists; vendored from [tronprotocol/libp2p](https://github.com/tronprotocol/libp2p) (see [`p2p/README.md`](./p2p/README.md)) | |
| 114 | +| `platform` | Architecture-specific implementations selected at build time (separate `x86` / `arm` / `common` source sets): math wrappers, LevelDB/RocksDB order-price comparators — relevant to cross-JVM determinism | |
| 115 | +| `plugins` | Standalone tools (`Toolkit.jar`, `ArchiveManifest.jar`) | |
| 116 | + |
| 117 | +`errorprone` and `example:actuator-example` are build-support and sample modules, not part of the node. |
| 118 | + |
| 119 | +**Module dependency direction is one-way — do not introduce reverse dependencies:** |
| 120 | + |
| 121 | +```text |
| 122 | +framework → chainbase, protocol, actuator, consensus, p2p |
| 123 | +chainbase → protocol, common, crypto |
| 124 | +common → protocol, p2p, platform |
| 125 | +actuator → chainbase, protocol, crypto |
| 126 | +consensus → chainbase, protocol (only via ConsensusDelegate; never call Manager directly) |
| 127 | +crypto → common |
| 128 | +``` |
| 129 | + |
| 130 | +`platform` and `p2p` are leaf modules with no dependencies of their own. `platform` holds the architecture-specific code; `common` and `plugins` depend on it directly, while `framework` picks it up only transitively through `common`. `plugins` depends on `crypto`, `platform`, and `protocol`. |
| 131 | + |
| 132 | +## Hard Constraints |
| 133 | + |
| 134 | +**Cross-JVM determinism** (consensus, state transition, block ordering) — the same block must produce the same state on every supported platform: |
| 135 | +- Never use `java.lang.Math` — use `org.tron.common.math.StrictMathWrapper` instead (CI-enforced, see checklist step 4). |
| 136 | +- Never use `float` / `double` in consensus-relevant arithmetic. |
| 137 | +- Never depend on `HashMap` iteration order for a business decision. |
| 138 | +- Use the DPoS slot time for produced-block timestamps, not `System.currentTimeMillis()`. |
| 139 | +- Never call `String.toLowerCase()` / `toUpperCase()` without an explicit `Locale` — ErrorProne enforces this as a compile error (`StringCaseLocaleUsage`). |
| 140 | + |
| 141 | +**DB / Store:** |
| 142 | +- State changes run inside the revoking session that `Manager` opens per transaction and per block; it is `merge()`d / `commit()`ted on success and revoked otherwise. |
| 143 | +- Actuators, the VM and stores call `put()` / `delete()` directly and must never open their own session: an `ISession` closed without `merge()` / `commit()` discards its writes, and `buildSession()` moves the snapshot head of every store. |
| 144 | +- A new consensus-state store extends `TronStoreWithRevoking<T>`, which registers itself in `@PostConstruct init()`; never call `revokingDatabase.add()` yourself. Auxiliary, non-revocable stores extend `TronDatabase<T>`. |
| 145 | + |
| 146 | +**Actuator:** |
| 147 | +- New actuators are registered automatically: place the class in the `org.tron.core.actuator` package, extend `AbstractActuator`, and pass the `ContractType` to `super(...)` from a no-arg constructor. `TransactionRegister.registerActuator()` discovers it by reflection at startup — there is no manual registration step. |
| 148 | +- `validate()` must check that the owner can afford `calcFee()` plus any amount being moved; the fee itself is charged inside `execute()` together with the state change, so a failed `execute()` rolls back both. Bandwidth, multi-signature and memo fees are charged by `Manager.processTransaction()` before the actuator runs — an actuator never touches them. |
| 149 | +- `validate()` must not mutate state. |
| 150 | + |
| 151 | +**Protobuf:** |
| 152 | +- Fields may only be added — never removed or renumbered. |
| 153 | +- Message field numbers start at `1`; the first enum value must be `0`. |
| 154 | +- In a new enum, the zero value's name must start with `UNKNOWN_` (e.g. `UNKNOWN_STATUS = 0;`, not `SUCCESS = 0;`). `protocol/protoLint.gradle` enforces this during `./gradlew build`; only the existing enums in its `legacyEnums` whitelist are exempt. |
| 155 | + |
| 156 | +**API / Threads:** |
| 157 | +- New HTTP servlets must go through `HttpApiAccessFilter` and use `Wallet` (never inject `Manager` directly). |
| 158 | +- A new gRPC or HTTP query that depends on historical data (unavailable on a lite fullnode) must be added to the deny-list in `LiteFnQueryGrpcInterceptor` / `LiteFnQueryHttpFilter`; other methods need no action — the interceptor and filter are installed server-wide. |
| 159 | +- No bare `new Thread()` — use a named Executor, shut down via `shutdown()` → `awaitTermination()` → `shutdownNow()`. |
| 160 | + |
| 161 | +## Common Pitfalls |
| 162 | + |
| 163 | +1. **ErrorProne is skipped below JDK 17.** An x86/JDK 8 build therefore does not report `StringCaseLocaleUsage` violations, while the same code fails to compile in the aarch64/JDK 17 CI jobs. |
| 164 | +2. **`./gradlew checkstyleMain` is not the CI gate.** Use the exact module-scoped command in checklist step 3. |
| 165 | +3. **Adding a config key without a comment fails CI**, even if the key itself is valid. |
| 166 | +4. **Changing a dependency without regenerating `verification-metadata.xml` breaks the build for everyone**, not just you. |
| 167 | +5. **Generated protobuf sources are git-ignored.** If a build error references a missing generated class, rebuild instead of creating the file. |
| 168 | +6. **Consensus-affecting behaviour changes need a proposal / fork gate**, not just a code change. Changing how an existing transaction validates or executes will fork the network unless gated. When in doubt, ask before implementing. |
| 169 | + |
| 170 | +## Authoritative Documentation |
| 171 | + |
| 172 | +- **Build / run / node operation:** [README](./README.md) |
| 173 | +- **Configuration:** [`docs/configuration.md`](./docs/configuration.md), [`docs/configuration-conventions.md`](./docs/configuration-conventions.md) |
| 174 | +- **Protobuf protocol:** the `.proto` files under `protocol/src/main/protos/` are the source of truth; [`docs/protobuf-protocol-document.md`](./docs/protobuf-protocol-document.md) explains the main messages (the Markdown copies under `protocol/src/main/protos/` are outdated). |
| 175 | +- **Extending / deployment:** the [`docs/`](./docs) directory (customized actuator, modular deployment). |
| 176 | +- **P2P module:** [`p2p/README.md`](./p2p/README.md) (standalone use, DNS node-list publishing, API). |
| 177 | +- **Contributing:** [CONTRIBUTING.md](./CONTRIBUTING.md) (workflow, coding style, commit/PR conventions). |
| 178 | +- **Security policy:** [SECURITY.md](./SECURITY.md) (supported versions, vulnerability disclosure). |
| 179 | + |
| 180 | +## Commit & PR Convention |
| 181 | + |
| 182 | +Commit messages and PR titles follow `type(scope): description`. The allowed types, the full scope list and the subject rules are defined in [CONTRIBUTING.md](./CONTRIBUTING.md#commit-messages) — follow it there. |
| 183 | + |
| 184 | +PR titles and descriptions are validated in CI by `.github/workflows/pr-check.yml`. Fill in `.github/PULL_REQUEST_TEMPLATE.md`. |
0 commit comments