Skip to content

Commit 7a61516

Browse files
authored
Merge pull request #880 from tronprotocol/release_v4.9.1
Release v4.9.1
2 parents ec719c5 + a7e3c6d commit 7a61516

File tree

18 files changed

+1842
-395
lines changed

18 files changed

+1842
-395
lines changed

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ For more information on a specific command, just type the command in the termina
136136
| [Unlock](#unlock) | [UpdateAccount](#update-account) | [UpdateAccountPermission](#How-to-use-the-multi-signature-feature-of-wallet-cli) |
137137
| [UpdateAsset](#Update-parameters-of-trc10-token) | [UpdateBrokerage](#Brokerage) | [UpdateEnergyLimit](#Update-smart-contract-parameters) |
138138
| [UpdateSetting](#Update-smart-contract-parameters) | [UpdateWitness](#update-witness) | [VoteWitness](#How-to-vote) |
139-
| [WithdrawBalance](#withdraw-balance) | [WithdrawExpireUnfreeze](#withdraw-expire-unfreeze) | |
139+
| [WithdrawBalance](#withdraw-balance) | [WithdrawExpireUnfreeze](#withdraw-expire-unfreeze) | [ModifyWalletName](#Modify-wallet-name) |
140+
| [ViewBackupRecords](#View-backup-records) | [ViewTransactionHistory](#View-transaction-history) | |
140141

141142

142143
Type any one of the listed commands, to display how-to tips.
@@ -1833,7 +1834,7 @@ unlock successful !!!
18331834

18341835
## switch network
18351836
> SwitchNetwork
1836-
>This command allows for flexible network switching at any time. Unlocking can specify parameters in seconds.
1837+
>This command allows for flexible network switching at any time.
18371838
>`switchnetwork local` will switch to the network configured in local config.conf.
18381839
18391840
Example:
@@ -2101,6 +2102,51 @@ Example:
21012102
> UpdateAccount test-name
21022103
```
21032104

2105+
### Modify wallet name
2106+
> ModifyWalletName new_wallet_name
2107+
2108+
Modify wallet's name.
2109+
2110+
Example:
2111+
```console
2112+
wallet> ModifyWalletName new-name
2113+
Modify Wallet Name successful !!
2114+
```
2115+
2116+
### View backup records
2117+
> ViewBackupRecords
2118+
2119+
View backup records. You can configure the maximum number of records that `maxRecords` can retain in `config.conf`, excluding the number of buffer records.
2120+
2121+
Example:
2122+
```console
2123+
wallet> ViewBackupRecords
2124+
2125+
=== View Backup Records ===
2126+
1. View all records
2127+
2. Filter by time range
2128+
Choose an option (1-2): 1
2129+
```
2130+
### View transaction history
2131+
> ViewTransactionHistory
2132+
2133+
View transaction history. You can configure the maximum number of records that `maxRecords` can retain in `config.conf`, excluding the number of buffer records.
2134+
2135+
Example:
2136+
```console
2137+
wallet> ViewTransactionHistory
2138+
====================================
2139+
TRANSACTION VIEWER
2140+
====================================
2141+
2142+
MAIN MENU:
2143+
1. View all transactions
2144+
2. Filter by time range
2145+
3. Help
2146+
4. Exit
2147+
Select option: 1
2148+
```
2149+
21042150

21052151
## Wallet related commands
21062152

src/main/java/org/tron/common/utils/Utils.java

Lines changed: 576 additions & 13 deletions
Large diffs are not rendered by default.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.tron.core.dao;
2+
3+
import java.time.LocalDateTime;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
@Getter
8+
@Setter
9+
public class BackupRecord {
10+
private String command;
11+
private String walletName;
12+
private String ownerAddress;
13+
private LocalDateTime timestamp;
14+
15+
public BackupRecord(String command, String walletName, String ownerAddress, LocalDateTime timestamp) {
16+
this.command = command;
17+
this.walletName = walletName;
18+
this.ownerAddress = ownerAddress;
19+
this.timestamp = timestamp;
20+
}
21+
22+
@Override
23+
public String toString() {
24+
return "BackupRecord{" +
25+
"command='" + command + '\'' +
26+
", walletName='" + walletName + '\'' +
27+
", ownerAddress='" + ownerAddress + '\'' +
28+
", timestamp=" + timestamp +
29+
'}';
30+
}
31+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package org.tron.core.dao;
2+
3+
import java.time.LocalDateTime;
4+
import java.time.format.DateTimeFormatter;
5+
import java.util.Objects;
6+
import lombok.Getter;
7+
import lombok.Setter;
8+
9+
@Getter
10+
@Setter
11+
public class Tx {
12+
private String id;
13+
private String type;
14+
private String from;
15+
private String to;
16+
private String amount;
17+
private LocalDateTime timestamp;
18+
private String status;
19+
private String note;
20+
private String fullNodeEndpoint;
21+
22+
public Tx() {
23+
}
24+
25+
public Tx(String id, String type, String from, String to, String amount, LocalDateTime timestamp, String note, String fullNodeEndpoint) {
26+
this.id = Objects.requireNonNull(id);
27+
this.type = Objects.requireNonNull(type);
28+
this.from = Objects.requireNonNull(from);
29+
this.to = Objects.requireNonNull(to);
30+
this.amount = amount;
31+
this.timestamp = timestamp;
32+
this.note = note;
33+
this.fullNodeEndpoint = fullNodeEndpoint;
34+
}
35+
36+
public boolean isRelatedTo(String address) {
37+
return from.equalsIgnoreCase(address) || to.equalsIgnoreCase(address);
38+
}
39+
40+
@Override
41+
public String toString() {
42+
return String.format("Tx[id=%s, type=%s, from=%s, to=%s, amount=%s, timestamp=%s, note=%s, fullNodeEndpoint=%s]",
43+
id,
44+
type,
45+
from,
46+
to,
47+
amount,
48+
timestamp.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
49+
note,
50+
fullNodeEndpoint);
51+
}
52+
53+
private String abbreviate(String address) {
54+
return address.length() > 8
55+
? address.substring(0, 6) + "..." + address.substring(address.length() - 4)
56+
: address;
57+
}
58+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package org.tron.core.manager;
2+
3+
import com.typesafe.config.Config;
4+
import java.io.BufferedReader;
5+
import java.io.BufferedWriter;
6+
import java.io.File;
7+
import java.io.FileWriter;
8+
import java.io.IOException;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.nio.file.Paths;
12+
import java.time.LocalDateTime;
13+
import java.time.format.DateTimeFormatter;
14+
import java.util.ArrayList;
15+
import java.util.Comparator;
16+
import java.util.List;
17+
import java.util.stream.Collectors;
18+
import org.tron.core.config.Configuration;
19+
import org.tron.core.dao.BackupRecord;
20+
21+
public class BackupRecordManager {
22+
private static final String DATA_DIR = "wallet_data";
23+
private static final String STORAGE_FILE = DATA_DIR + File.separator + "wallet_backup_records.log";
24+
private static final DateTimeFormatter TIMESTAMP_FORMAT =
25+
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
26+
private static final int PAGE_SIZE = 10;
27+
private static int maxRecords;
28+
private static final int BUFFER_THRESHOLD = 100;
29+
30+
static {
31+
try {
32+
Config config = Configuration.getByPath("config.conf");
33+
if (config != null && config.hasPath("maxRecords")) {
34+
int value = config.getInt("maxRecords");
35+
if (value <= 0) {
36+
System.out.println("Invalid maxRecords value " + value + ", must be positive. Using default.");
37+
}
38+
maxRecords = value;
39+
}
40+
} catch (Exception e) {
41+
System.out.println("Failed to load maxRecords from config, using default value.");
42+
maxRecords = 1000;
43+
}
44+
}
45+
46+
public BackupRecordManager() {
47+
initializeStorageFile();
48+
}
49+
50+
private void initializeStorageFile() {
51+
try {
52+
Path dataDir = Paths.get(DATA_DIR);
53+
if (!Files.exists(dataDir)) {
54+
Files.createDirectories(dataDir);
55+
}
56+
Path path = Paths.get(STORAGE_FILE);
57+
if (!Files.exists(path)) {
58+
Files.createFile(path);
59+
}
60+
} catch (IOException e) {
61+
System.err.println("Failed to initialize backup records file: " + e.getMessage());
62+
}
63+
}
64+
65+
public void saveRecord(BackupRecord br) {
66+
try {
67+
List<BackupRecord> records = loadAllRecords();
68+
records.add(br);
69+
70+
if (records.size() > maxRecords + BUFFER_THRESHOLD) {
71+
records = records.subList(records.size() - maxRecords, records.size());
72+
rewriteFile(records);
73+
} else {
74+
appendRecord(br);
75+
}
76+
} catch (IOException e) {
77+
System.err.println("Failed to save backup record: " + e.getMessage());
78+
}
79+
}
80+
81+
private void appendRecord(BackupRecord br) throws IOException {
82+
try (BufferedWriter writer = new BufferedWriter(new FileWriter(STORAGE_FILE, true))) {
83+
writer.write(recordToCsvLine(br));
84+
writer.newLine();
85+
}
86+
}
87+
88+
private void rewriteFile(List<BackupRecord> records) throws IOException {
89+
try (BufferedWriter writer = new BufferedWriter(new FileWriter(STORAGE_FILE))) {
90+
for (BackupRecord br : records) {
91+
writer.write(recordToCsvLine(br));
92+
writer.newLine();
93+
}
94+
}
95+
}
96+
97+
public List<BackupRecord> loadAllRecords() {
98+
List<BackupRecord> records = new ArrayList<>();
99+
Path path = Paths.get(STORAGE_FILE);
100+
101+
if (!Files.exists(path)) {
102+
return records;
103+
}
104+
105+
try (BufferedReader reader = Files.newBufferedReader(path)) {
106+
String line;
107+
while ((line = reader.readLine()) != null) {
108+
if (!line.trim().isEmpty()) {
109+
BackupRecord br = parseCsvLine(line);
110+
if (br != null) {
111+
records.add(br);
112+
}
113+
}
114+
}
115+
} catch (IOException e) {
116+
System.err.println("Failed to load backup records: " + e.getMessage());
117+
}
118+
return records;
119+
}
120+
121+
public List<BackupRecord> getRecordsByTimeRange(LocalDateTime start, LocalDateTime end) {
122+
return loadAllRecords().stream()
123+
.filter(br -> !br.getTimestamp().isBefore(start))
124+
.filter(br -> !br.getTimestamp().isAfter(end))
125+
.sorted(Comparator.comparing(BackupRecord::getTimestamp).reversed())
126+
.collect(Collectors.toList());
127+
}
128+
129+
public int calculateTotalPages(List<BackupRecord> records) {
130+
if (records == null || records.isEmpty()) {
131+
return 0;
132+
}
133+
return (int) Math.ceil((double) records.size() / PAGE_SIZE);
134+
}
135+
136+
public int getRecordsTotalPages() {
137+
List<BackupRecord> backupRecords = loadAllRecords();
138+
return calculateTotalPages(backupRecords);
139+
}
140+
141+
public int getRecordsTotalPagesByTimeRange(LocalDateTime start, LocalDateTime end) {
142+
List<BackupRecord> list = getRecordsByTimeRange(start, end);
143+
return calculateTotalPages(list);
144+
}
145+
146+
public List<BackupRecord> getPaginatedRecords(List<BackupRecord> records, int page) {
147+
int fromIndex = (page - 1) * PAGE_SIZE;
148+
if (fromIndex >= records.size()) {
149+
return new ArrayList<>();
150+
}
151+
152+
int toIndex = Math.min(fromIndex + PAGE_SIZE, records.size());
153+
return records.subList(fromIndex, toIndex);
154+
}
155+
156+
private String recordToCsvLine(BackupRecord br) {
157+
return String.join(",",
158+
escapeCsvField(br.getCommand()),
159+
escapeCsvField(br.getWalletName()),
160+
escapeCsvField(br.getOwnerAddress()),
161+
br.getTimestamp().format(TIMESTAMP_FORMAT));
162+
}
163+
164+
private BackupRecord parseCsvLine(String line) {
165+
try {
166+
String[] parts = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
167+
168+
if (parts.length != 4) {
169+
throw new IllegalArgumentException("Invalid CSV line format");
170+
}
171+
172+
return new BackupRecord(
173+
unescapeCsvField(parts[0]),
174+
unescapeCsvField(parts[1]),
175+
unescapeCsvField(parts[2]),
176+
LocalDateTime.parse(parts[3], TIMESTAMP_FORMAT));
177+
} catch (Exception e) {
178+
System.err.println("Failed to parse CSV line: " + line + ", error: " + e.getMessage());
179+
return null;
180+
}
181+
}
182+
183+
private String escapeCsvField(String field) {
184+
if (field.contains(",") || field.contains("\"") || field.contains("\n")) {
185+
return "\"" + field.replace("\"", "\"\"") + "\"";
186+
}
187+
return field;
188+
}
189+
190+
private String unescapeCsvField(String field) {
191+
if (field.startsWith("\"") && field.endsWith("\"")) {
192+
return field.substring(1, field.length() - 1).replace("\"\"", "\"");
193+
}
194+
return field;
195+
}
196+
}

0 commit comments

Comments
 (0)