Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,20 @@ DB lite provides lite database, parameters are compatible with previous `LiteFul
- `-fn | --fn-data-path`: The database path to be split or merged.
- `-ds | --dataset-path`: When operation is `split`,`dataset-path` is the path that store the `snapshot` or `history`, when
operation is `split`, `dataset-path` is the `history` data path.
- `--exclude-historical-balance`: Only used with `operate=split -t snapshot`, default: false. When set to true, `balance-trace` and `account-trace` are excluded from the lite snapshot. The flag has functional impact only when the source full node ran with `historyBalanceLookup=true` (off by default; most operators are unaffected). **WARNING:** for nodes that had `historyBalanceLookup=true`, this loss is permanent — a lite node booted from such a snapshot cannot answer historical balance lookups (`getBlockBalance` / `getAccountBalance`), and running `merge` afterwards will NOT restore the feature. If you need historical balance lookup on the resulting lite node, do **not** enable this flag. `split -t history` and `merge` ignore this flag.
- `-h | --help`: Provide the help info.

### Examples:

```shell script
# full command
java -jar Toolkit.jar db lite [-h] -ds=<datasetPath> -fn=<fnDataPath> [-o=<operate>] [-t=<type>]
java -jar Toolkit.jar db lite [-h] -ds=<datasetPath> -fn=<fnDataPath> [-o=<operate>] [-t=<type>] [--exclude-historical-balance]
# examples
#split and get a snapshot dataset
java -jar Toolkit.jar db lite -o split -t snapshot --fn-data-path output-directory/database --dataset-path /tmp
#split and get a snapshot dataset without balance-trace / account-trace (smaller snapshot;
#historical balance lookup will be permanently unavailable on the resulting lite node)
java -jar Toolkit.jar db lite -o split -t snapshot --fn-data-path output-directory/database --dataset-path /tmp --exclude-historical-balance
#split and get a history dataset
java -jar Toolkit.jar db lite -o split -t history --fn-data-path output-directory/database --dataset-path /tmp
#merge history dataset and snapshot dataset
Expand Down
67 changes: 65 additions & 2 deletions plugins/src/main/java/common/org/tron/plugins/DbLite.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import me.tongfei.progressbar.ProgressBar;
import org.rocksdb.RocksDBException;
Expand Down Expand Up @@ -57,6 +58,8 @@ public class DbLite implements Callable<Integer> {
private static final String TRANSACTION_HISTORY_DB_NAME = "transactionHistoryStore";
private static final String PROPERTIES_DB_NAME = "properties";
private static final String TRANS_CACHE_DB_NAME = "trans-cache";
private static final String BALANCE_TRACE_DB_NAME = "balance-trace";
private static final String ACCOUNT_TRACE_DB_NAME = "account-trace";

private static final List<String> archiveDbs = Arrays.asList(
BLOCK_DB_NAME,
Expand All @@ -65,6 +68,10 @@ public class DbLite implements Callable<Integer> {
TRANSACTION_RET_DB_NAME,
TRANSACTION_HISTORY_DB_NAME);

private static final List<String> traceDbs = Arrays.asList(
BALANCE_TRACE_DB_NAME,
ACCOUNT_TRACE_DB_NAME);

enum Operate { split, merge }

enum Type { snapshot, history }
Expand Down Expand Up @@ -105,8 +112,25 @@ enum Type { snapshot, history }
private String datasetPath;

@CommandLine.Option(
names = {"--help", "-h"},
names = {"--exclude-historical-balance"},
defaultValue = "false",
description = "only used with `operate=split -t snapshot`: when true, balance-trace "
+ "and account-trace are excluded from the lite snapshot. "
+ "Default: ${DEFAULT-VALUE} (legacy behavior; trace stores stay in the snapshot). "
+ "This flag only has a functional impact when the source full node ran with "
+ "`historyBalanceLookup=true` (off by default; most operators are unaffected). "
+ "WARNING: when historyBalanceLookup was enabled, this loss is permanent: a lite "
+ "node booted from such a snapshot cannot answer historical balance lookups "
+ "(getBlockBalance / getAccountBalance), and running merge afterwards will NOT "
+ "restore the feature. If you need to keep historyBalanceLookup working on the "
+ "resulting lite node, do NOT enable this flag. `split -t history` and `merge` "
+ "ignore this flag.",
order = 5)
private boolean excludeHistoricalBalance;

@CommandLine.Option(
names = {"--help", "-h"},
order = 6)
private boolean help;


Expand All @@ -119,6 +143,7 @@ public Integer call() {
try {
switch (this.operate) {
case split:
warnIfExcludingHistoricalBalance();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[NIT] warnIfExcludingHistoricalBalance() runs for every split, so split -t history --exclude-historical-balance will still print a snapshot-specific permanent-loss warning even though history split ignores this flag.

if (Type.snapshot == this.type) {
generateSnapshot(fnDataPath, datasetPath);
} else if (Type.history == type) {
Expand Down Expand Up @@ -253,12 +278,50 @@ public void completeHistoryData(String historyDir, String liteDir) {
spec.commandLine().getOut().format("Merge history finished, take %d s.", during).println();
}

/**
* Compute the directories to exclude from the lite snapshot.
* <p>
* Default ({@code --exclude-historical-balance=false}): the legacy archive set
* (5 dbs); {@link #BALANCE_TRACE_DB_NAME} / {@link #ACCOUNT_TRACE_DB_NAME}
* stay with the snapshot as state-style stores.
* <p>
* Opt-in ({@code --exclude-historical-balance=true}): the trace stores are
* additionally excluded, producing a smaller lite snapshot at the cost of
* dropping historical balance lookup support on the resulting lite node.
* Only {@code split -t snapshot} consults this. {@code split -t history}
* and {@code merge} always use the legacy archive set.
*/
private List<String> snapshotExclusion() {
if (!excludeHistoricalBalance) {
return archiveDbs;
}
return Stream.concat(archiveDbs.stream(), traceDbs.stream())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[MUST] When this exclusion is enabled on a source node that had historyBalanceLookup=true, the resulting lite snapshot can still serve historical-balance RPCs, but not safely.

For blocks that are still retained in the snapshot block-index, account-trace is recreated as an empty store on boot, AccountTraceStore#getPrevBalance() maps an empty lookup to Pair.of(number, 0L), and Wallet.getAccountBalance() returns that as a successful balance=0. For the same block, getBlockBalance() throws because balance-trace is absent.

So this flag does not merely "disable" historical balance lookup; it can silently return wrong account balances. That is a correctness issue, and it does not match the current help / warning / README wording of "cannot answer".

Please make this mode fail closed before merge, for example by persisting a marker in the snapshot and rejecting getAccountBalance / getBlockBalance on lite nodes created with this flag, or by retaining the trace coverage needed for snapshot-retained blocks. At minimum, the operator-facing text must describe the real asymmetry explicitly.

.collect(Collectors.toList());
}

private void warnIfExcludingHistoricalBalance() {
if (!excludeHistoricalBalance) {
return;
}
String msg = "WARNING: --exclude-historical-balance is enabled. balance-trace / account-trace "
+ "will be excluded from the lite snapshot. This only matters when the source full "
+ "node ran with historyBalanceLookup=true (off by default; most operators are "
+ "unaffected). When that switch was enabled, this loss is permanent: lite nodes "
+ "booted from this snapshot cannot answer historical balance lookups "
+ "(getBlockBalance / getAccountBalance), and running merge afterwards will NOT "
+ "restore the feature. If you need to keep historyBalanceLookup working on the "
+ "resulting lite node, do NOT use this flag.";
logger.warn(msg);
spec.commandLine().getErr().println(msg);
}

private List<String> getSnapshotDbs(String sourceDir) {
List<String> snapshotDbs = Lists.newArrayList();
File basePath = new File(sourceDir);
List<String> excluded = snapshotExclusion();
Arrays.stream(Objects.requireNonNull(basePath.listFiles()))
.filter(File::isDirectory)
.filter(dir -> !archiveDbs.contains(dir.getName()))
.filter(dir -> !excluded.contains(dir.getName()))
.forEach(dir -> snapshotDbs.add(dir.getName()));
return snapshotDbs;
}
Expand Down
28 changes: 25 additions & 3 deletions plugins/src/test/java/org/tron/plugins/DbLiteTest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package org.tron.plugins;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.tron.common.utils.PublicMethod.getRandomPrivateKey;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
Expand Down Expand Up @@ -89,10 +92,19 @@ public void clear() {

public void testTools(String dbType, int checkpointVersion)
Comment thread
halibobo1205 marked this conversation as resolved.
throws InterruptedException, IOException {
logger.info("dbType {}, checkpointVersion {}", dbType, checkpointVersion);
testTools(dbType, checkpointVersion, false);
}

public void testTools(String dbType, int checkpointVersion, boolean excludeHistoricalBalance)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[SHOULD] This opt-in test path never enables historyBalanceLookup=true on the source node, so it only covers the default configuration where this flag has no functional effect.

config-localtest.conf inherits storage.balance.history.lookup=false, which means the source node never produces meaningful balance-trace / account-trace data. The assertions here prove "the directories were excluded", but they do not cover the only configuration this flag is meant for.

Please add a case with historyBalanceLookup=true and assert the post-snapshot behavior of getAccountBalance / getBlockBalance for a snapshot-retained block.

throws InterruptedException, IOException {
logger.info("dbType {}, checkpointVersion {}, excludeHistoricalBalance {}",
dbType, checkpointVersion, excludeHistoricalBalance);
init(dbType);
final String[] argsForSnapshot =
new String[] {"-o", "split", "-t", "snapshot", "--fn-data-path",
final String[] argsForSnapshot = excludeHistoricalBalance
? new String[] {"-o", "split", "-t", "snapshot", "--fn-data-path",
dbPath + File.separator + databaseDir, "--dataset-path",
dbPath, "--exclude-historical-balance"}
: new String[] {"-o", "split", "-t", "snapshot", "--fn-data-path",
dbPath + File.separator + databaseDir, "--dataset-path",
dbPath};
final String[] argsForHistory =
Expand All @@ -114,6 +126,16 @@ public void testTools(String dbType, int checkpointVersion)
FileUtil.deleteDir(Paths.get(dbPath, databaseDir, "trans-cache").toFile());
// generate snapshot
cli.execute(argsForSnapshot);
Comment thread
halibobo1205 marked this conversation as resolved.
Path snapshotDir = Paths.get(dbPath, "snapshot");
if (excludeHistoricalBalance) {
// when --exclude-historical-balance=true, the lite snapshot must not ship
// balance-trace / account-trace
assertFalse(snapshotDir.resolve("balance-trace").toFile().exists());
assertFalse(snapshotDir.resolve("account-trace").toFile().exists());
} else {
assertTrue(snapshotDir.resolve("balance-trace").toFile().exists());
assertTrue(snapshotDir.resolve("account-trace").toFile().exists());
}
// start fullNode
startApp();
// produce transactions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.tron.plugins.rocksdb;

import java.io.IOException;
import org.junit.Test;
import org.tron.plugins.DbLiteTest;

public class DbLiteExcludeHistoricalBalanceRocksDbTest extends DbLiteTest {

@Test
public void testToolsWithExcludeHistoricalBalance() throws InterruptedException, IOException {
testTools("ROCKSDB", 1, true);
}
}
Loading