Skip to content

Commit 2926e5e

Browse files
authored
Merge pull request #927 from tronprotocol/release_v4.9.5
Release v4.9.5
2 parents 56b8bbc + 16a1563 commit 2926e5e

81 files changed

Lines changed: 20303 additions & 323 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Build Artifact
2+
3+
on:
4+
push:
5+
branches:
6+
- "release_*"
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
name: Build wallet-cli artifact
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Java
22+
uses: actions/setup-java@v4
23+
with:
24+
distribution: temurin
25+
java-version: "8"
26+
cache: gradle
27+
28+
- name: Build
29+
run: ./gradlew clean build shadowJar shadowDistZip
30+
31+
- name: Package artifacts
32+
shell: bash
33+
run: |
34+
set -euo pipefail
35+
36+
version="${GITHUB_REF_NAME#release_}"
37+
if [ "$version" = "$GITHUB_REF_NAME" ]; then
38+
version="dev"
39+
fi
40+
41+
artifact_dir="artifacts"
42+
artifact_base="wallet-cli-${version}-rc.${GITHUB_RUN_NUMBER}"
43+
artifact_name="wallet-cli-artifact-${GITHUB_REF_NAME}"
44+
45+
mkdir -p "$artifact_dir"
46+
cp build/libs/wallet-cli.jar "$artifact_dir/${artifact_base}.jar"
47+
cp build/distributions/*shadow*.zip "$artifact_dir/${artifact_base}.zip"
48+
git rev-parse HEAD > "$artifact_dir/git-sha.txt"
49+
echo "ARTIFACT_NAME=${artifact_name}" >> "$GITHUB_ENV"
50+
51+
(
52+
cd "$artifact_dir"
53+
shasum -a 256 "${artifact_base}.jar" "${artifact_base}.zip" git-sha.txt > checksums.txt
54+
)
55+
56+
- name: Upload artifact
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: ${{ env.ARTIFACT_NAME }}
60+
path: artifacts/*

.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.claude/settings.local.json
12
.DS_Store
23
build
34
out
@@ -11,4 +12,27 @@ src/gen
1112
tools
1213
src/main/resources/static/js/tronjs/tron-protoc.js
1314
logs
15+
docs
16+
!docs/
17+
!docs/standard-cli-contract-spec.md
1418
FileTest
19+
bin
20+
21+
# Wallet keystore files created at runtime
22+
Wallet/
23+
Mnemonic/
24+
wallet_data/
25+
26+
# QA runtime output
27+
qa/results/
28+
qa/runtime/
29+
qa/report.txt
30+
qa/.verify.lock/
31+
32+
# claude
33+
.claude/
34+
35+
.private/
36+
37+
# graphify
38+
graphify-out/

CLAUDE.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build & Run
6+
7+
```bash
8+
# Build the project (generates protobuf sources into src/main/gen/)
9+
./gradlew build
10+
11+
# Build fat JAR (output: build/libs/wallet-cli.jar)
12+
./gradlew shadowJar
13+
14+
# Run in REPL 交互模式 (human-friendly, interactive prompts)
15+
./gradlew run
16+
# Or after building: java -jar build/libs/wallet-cli.jar
17+
18+
# Run in standard CLI mode (non-interactive, scriptable)
19+
java -jar build/libs/wallet-cli.jar --network nile get-account --address TXyz...
20+
java -jar build/libs/wallet-cli.jar --output json --network nile get-account --address TXyz...
21+
22+
# Run tests
23+
./gradlew test
24+
25+
# Run a single test class
26+
./gradlew test --tests "org.tron.keystore.StringUtilsTest"
27+
28+
# Clean (also removes src/main/gen/)
29+
./gradlew clean
30+
```
31+
32+
Java 8 source/target compatibility. Protobuf sources are in `src/main/protos/` and generate into `src/main/gen/` — this directory is git-tracked but rebuilt on `clean`.
33+
34+
## QA Verification
35+
36+
The `qa/` directory contains shell-based parity tests that compare interactive REPL output vs standard CLI (text and JSON modes). Requires a funded Nile testnet account.
37+
38+
```bash
39+
# Run QA verification (needs TRON_TEST_PRIVATE_KEY env var for private key)
40+
TRON_TEST_PRIVATE_KEY=<nile-private-key> bash qa/run.sh verify
41+
42+
# QA config is in qa/config.sh; test commands are in qa/commands/*.sh
43+
# MASTER_PASSWORD env var is used for keystore auto-login (default: testpassword123A)
44+
```
45+
46+
## Architecture
47+
48+
This is a **TRON blockchain CLI wallet** built on the [Trident SDK](https://github.com/tronprotocol/trident). It communicates with TRON nodes via gRPC.
49+
50+
### Two CLI Modes
51+
52+
1. **REPL 交互模式** (human-friendly) — `Client` class with JCommander `@Parameters` inner classes. Entry point: `org.tron.walletcli.Client`. Features tab completion, interactive prompts, and conversational output. This is the largest file (~4700 lines). Best for manual exploration and day-to-day wallet management by humans.
53+
2. **Standard CLI 模式** (AI-agent-friendly) — `StandardCliRunner` with `CommandRegistry`/`CommandDefinition` pattern in `org.tron.walletcli.cli.*`. Supports `--output json`, `--network`, `--quiet` flags. Commands are registered in `cli/commands/` classes (e.g., `WalletCommands`, `TransactionCommands`, `QueryCommands`). Designed for automation: deterministic exit codes, structured JSON output, no interactive prompts, and env-var-based authentication — ideal for AI agents, scripts, and CI/CD pipelines.
54+
55+
The standard CLI suppresses all stray stdout/stderr in JSON mode to ensure machine-parseable output. Authentication is automatic via `MASTER_PASSWORD` env var + keystore files in `Wallet/`.
56+
57+
### Standard CLI Contract
58+
59+
Before changing parser behavior, auth flow, JSON output, command success/failure semantics, or `qa/` expectations for
60+
the standard CLI, read:
61+
62+
- `docs/standard-cli-contract-spec.md`
63+
64+
Treat that file as the source of truth for the standard CLI contract unless the repository owner explicitly decides to
65+
revise it.
66+
67+
### Request Flow
68+
69+
```
70+
# Standard CLI mode:
71+
User Input → GlobalOptions → StandardCliRunner → CommandRegistry → CommandHandler → WalletApiWrapper → WalletApi → Trident SDK → gRPC → TRON Node
72+
73+
# Interactive REPL mode:
74+
User Input → Client (JCommander) → WalletApiWrapper → WalletApi → Trident SDK → gRPC → TRON Node
75+
```
76+
77+
### Key Classes
78+
79+
- **`org.tron.walletcli.Client`** — Legacy REPL entry point and CLI command dispatcher. Each command is a JCommander `@Parameters` inner class.
80+
- **`org.tron.walletcli.cli.StandardCliRunner`** — New standard CLI executor. Handles network init, auto-authentication, JSON stream suppression, and command dispatch.
81+
- **`org.tron.walletcli.cli.CommandRegistry`** — Maps command names/aliases to `CommandDefinition` instances. Supports fuzzy suggestion on typos.
82+
- **`org.tron.walletcli.cli.CommandDefinition`** — Immutable command metadata (name, aliases, options, handler). Built via fluent `Builder` API.
83+
- **`org.tron.walletcli.cli.OutputFormatter`** — Formats output as text or JSON. In JSON mode, wraps results in `{"success":true,"data":...}` envelope.
84+
- **`org.tron.walletcli.WalletApiWrapper`** — Orchestration layer between CLI and core wallet logic. Handles transaction construction, signing, and broadcasting.
85+
- **`org.tron.walletserver.WalletApi`** — Core wallet operations: account management, transaction creation, proposals, asset operations. Delegates gRPC calls to Trident.
86+
- **`org.tron.walletcli.ApiClientFactory`** — Creates gRPC client instances for different networks (mainnet, Nile testnet, Shasta testnet, custom).
87+
88+
### Adding a New Standard CLI Command
89+
90+
1. Create or extend a class in `cli/commands/` (e.g., `TransactionCommands.java`)
91+
2. Build a `CommandDefinition` via `CommandDefinition.builder()` with name, aliases, options, and handler
92+
3. Register it in the appropriate `register(CommandRegistry)` method
93+
4. The handler receives `(ParsedOptions, WalletApiWrapper, OutputFormatter)` — use `formatter.success()/error()` for output
94+
95+
### Package Organization
96+
97+
| Package | Purpose |
98+
|---------|---------|
99+
| `walletcli` | CLI entry points, API wrapper |
100+
| `walletcli.cli` | Standard CLI framework: registry, definitions, options, formatter |
101+
| `walletcli.cli.commands` | Standard CLI command implementations by domain |
102+
| `walletserver` | Core wallet API and gRPC communication |
103+
| `common` | Crypto utilities, encoding, enums, shared helpers |
104+
| `core` | Configuration, data converters, DAOs, exceptions, managers |
105+
| `keystore` | Wallet file encryption/decryption, key management |
106+
| `ledger` | Ledger hardware wallet integration via HID |
107+
| `mnemonic` | BIP39 mnemonic seed phrase support |
108+
| `multi` | Multi-signature transaction handling |
109+
| `gasfree` | GasFree transaction API (transfer tokens without gas) |
110+
111+
### Configuration
112+
113+
- **Network config:** `src/main/resources/config.conf` (HOCON format via Typesafe Config)
114+
- **Logging:** `src/main/resources/logback.xml` (Logback, INFO level console + rolling file)
115+
- **Lombok:** `lombok.config` — uses `logger` as the log field name (not the default `log`)
116+
117+
### Key Frameworks & Libraries
118+
119+
- **Trident SDK 0.10.0** — All gRPC API calls to TRON nodes
120+
- **JCommander 1.82** — CLI argument parsing (REPL 交互模式)
121+
- **JLine 3.25.0** — Interactive terminal/readline
122+
- **BouncyCastle** — Cryptographic operations
123+
- **Protobuf 3.25.5 / gRPC 1.60.0** — Protocol definitions and transport
124+
- **Lombok**`@Getter`, `@Setter`, `@Slf4j` etc. (annotation processing)

build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ protobuf {
130130
}
131131
}
132132

133+
tasks.named("processResources") {
134+
dependsOn tasks.named("generateProto")
135+
}
136+
133137
clean.doFirst {
134138
delete "src/main/gen"
135139
}
@@ -146,3 +150,19 @@ shadowJar {
146150
version = null
147151
mergeServiceFiles() // https://github.com/grpc/grpc-java/issues/10853
148152
}
153+
154+
task qaJar(type: Jar, dependsOn: [shadowJar, testClasses]) {
155+
from zipTree(shadowJar.archiveFile)
156+
from sourceSets.test.output
157+
archiveBaseName.set('wallet-cli-qa')
158+
archiveClassifier.set('')
159+
archiveVersion.set('')
160+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
161+
}
162+
163+
task qaRun(type: JavaExec) {
164+
classpath = sourceSets.test.runtimeClasspath
165+
mainClass = 'org.tron.qa.QARunner'
166+
args = project.hasProperty('qaArgs') ? project.property('qaArgs').split(' ') : ['list']
167+
standardInput = System.in
168+
}

0 commit comments

Comments
 (0)