Skip to content

Commit fa46c4f

Browse files
authored
feat(*): optimize old rewards withdrawal (#5742)
* remove useNewRewardAlgorithmFromStart * code clear * exit when the calculation task gets an exception * tools: add `db root` to calculate the data root * make reward-vi root configurable * fix race-condition for Merkel root calculation * optimize proposal design
1 parent 96afa15 commit fa46c4f

File tree

30 files changed

+738
-100
lines changed

30 files changed

+738
-100
lines changed

actuator/src/main/java/org/tron/core/utils/ProposalUtil.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -743,13 +743,9 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
743743
}
744744
if (!dynamicPropertiesStore.useNewRewardAlgorithm()) {
745745
throw new ContractValidateException(
746-
"[ALLOW_NEW_REWARD] proposal must be approved "
746+
"[ALLOW_NEW_REWARD] or [ALLOW_TVM_VOTE] proposal must be approved "
747747
+ "before [ALLOW_OLD_REWARD_OPT] can be proposed");
748748
}
749-
if (dynamicPropertiesStore.useNewRewardAlgorithmFromStart()) {
750-
throw new ContractValidateException(
751-
"no need old reward opt, ALLOW_NEW_REWARD from start cycle 1");
752-
}
753749
break;
754750
}
755751
default:

chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import java.util.stream.Collectors;
66
import java.util.stream.IntStream;
77
import lombok.Getter;
8+
import net.jcip.annotations.NotThreadSafe;
89
import org.tron.common.parameter.CommonParameter;
910
import org.tron.common.utils.Sha256Hash;
1011

1112
@Getter
13+
@NotThreadSafe
1214
public class MerkleTree {
1315

1416
private static volatile MerkleTree instance;

chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@ public interface DBIterator extends Iterator<Entry<byte[], byte[]>>, AutoCloseab
1616

1717
void seekToLast();
1818

19-
default UnmodifiableIterator<Entry<byte[], byte[]>> prefixQueryAfterThat
20-
(byte[] key, byte[] afterThat) {
21-
this.seek(afterThat == null ? key : afterThat);
22-
return Iterators.filter(this, entry -> Bytes.indexOf(entry.getKey(), key) == 0);
23-
}
24-
2519
/**
2620
* An iterator is either positioned at a key/value pair, or
2721
* not valid. This method returns true iff the iterator is valid.

chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,12 @@ public boolean valid() {
108108

109109
@Override
110110
public byte[] getKey() {
111-
checkState();
112111
checkValid();
113112
return dbIterator.key();
114113
}
115114

116115
@Override
117116
public byte[] getValue() {
118-
checkState();
119117
checkValid();
120118
return dbIterator.value();
121119
}

chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,12 @@ public boolean valid() {
9393

9494
@Override
9595
public byte[] getKey() {
96-
checkState();
9796
checkValid();
9897
return dbIterator.peekNext().getKey();
9998
}
10099

101100
@Override
102101
public byte[] getValue() {
103-
checkState();
104102
checkValid();
105103
return dbIterator.peekNext().getValue();
106104
}

chainbase/src/main/java/org/tron/core/service/RewardViCalService.java

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import static org.tron.core.store.DelegationStore.DECIMAL_OF_VI_REWARD;
44
import static org.tron.core.store.DelegationStore.REMARK;
55

6-
import com.google.common.annotations.VisibleForTesting;
76
import com.google.common.collect.Streams;
87
import com.google.common.primitives.Bytes;
98
import com.google.protobuf.ByteString;
@@ -18,7 +17,6 @@
1817
import java.util.stream.Collectors;
1918
import java.util.stream.LongStream;
2019
import javax.annotation.PreDestroy;
21-
import lombok.Setter;
2220
import lombok.extern.slf4j.Slf4j;
2321
import org.bouncycastle.util.encoders.Hex;
2422
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,9 +25,9 @@
2725
import org.tron.common.es.ExecutorServiceManager;
2826
import org.tron.common.parameter.CommonParameter;
2927
import org.tron.common.utils.ByteArray;
28+
import org.tron.common.utils.MerkleRoot;
3029
import org.tron.common.utils.Pair;
3130
import org.tron.common.utils.Sha256Hash;
32-
import org.tron.core.capsule.utils.MerkleTree;
3331
import org.tron.core.db.common.iterator.DBIterator;
3432
import org.tron.core.db2.common.DB;
3533
import org.tron.core.store.DelegationStore;
@@ -55,10 +53,11 @@ public class RewardViCalService {
5553

5654
private volatile long lastBlockNumber = -1;
5755

58-
@VisibleForTesting
59-
@Setter
60-
private Sha256Hash rewardViRoot = Sha256Hash.wrap(
61-
ByteString.fromHex("9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8"));
56+
private static final String MAIN_NET_ROOT_HEX =
57+
"9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8";
58+
59+
private final Sha256Hash rewardViRoot = CommonParameter.getInstance().getStorage().getDbRoot(
60+
"reward-vi", Sha256Hash.wrap(ByteString.fromHex(MAIN_NET_ROOT_HEX)));
6261

6362
private final CountDownLatch lock = new CountDownLatch(1);
6463

@@ -99,25 +98,30 @@ private boolean isDone() {
9998
}
10099

101100
private void maybeRun() {
102-
if (enableNewRewardAlgorithm()) {
103-
if (this.newRewardCalStartCycle > 1) {
104-
if (isDone()) {
105-
this.clearUp(true);
106-
logger.info("rewardViCalService is already done");
107-
} else {
108-
if (lastBlockNumber == Long.MAX_VALUE // start rewardViCalService immediately
109-
|| this.getLatestBlockHeaderNumber() > lastBlockNumber) {
110-
// checkpoint is flushed to db, so we can start rewardViCalService
111-
startRewardCal();
112-
clearUp(true);
101+
try {
102+
if (enableNewRewardAlgorithm()) {
103+
if (this.newRewardCalStartCycle > 1) {
104+
if (isDone()) {
105+
this.clearUp(true);
106+
logger.info("rewardViCalService is already done");
113107
} else {
114-
logger.info("startRewardCal will run after checkpoint is flushed to db");
108+
if (lastBlockNumber == Long.MAX_VALUE // start rewardViCalService immediately
109+
|| this.getLatestBlockHeaderNumber() > lastBlockNumber) {
110+
// checkpoint is flushed to db, so we can start rewardViCalService
111+
startRewardCal();
112+
clearUp(true);
113+
} else {
114+
logger.info("startRewardCal will run after checkpoint is flushed to db");
115+
}
115116
}
117+
} else {
118+
clearUp(false);
119+
logger.info("rewardViCalService is no need to run");
116120
}
117-
} else {
118-
clearUp(false);
119-
logger.info("rewardViCalService is no need to run");
120121
}
122+
} catch (Exception e) {
123+
logger.error(" Find fatal error, program will be exited soon.", e);
124+
System.exit(1);
121125
}
122126
}
123127

@@ -137,7 +141,7 @@ private void destroy() {
137141

138142
public long getNewRewardAlgorithmReward(long beginCycle, long endCycle,
139143
List<Pair<byte[], Long>> votes) {
140-
if (!rewardViStore.has(IS_DONE_KEY)) {
144+
if (!isDone()) {
141145
logger.warn("rewardViCalService is not done, wait for it");
142146
try {
143147
lock.await();
@@ -174,10 +178,13 @@ private void calcMerkleRoot() {
174178
.map(this::getHash)
175179
.collect(Collectors.toCollection(ArrayList::new));
176180

177-
Sha256Hash rewardViRootLocal = MerkleTree.getInstance().createTree(ids).getRoot().getHash();
181+
Sha256Hash rewardViRootLocal = MerkleRoot.root(ids);
178182
if (!Objects.equals(rewardViRoot, rewardViRootLocal)) {
179-
logger.warn("merkle root mismatch, expect: {}, actual: {}",
180-
rewardViRoot, rewardViRootLocal);
183+
logger.warn("Merkle root mismatch, expect: {}, actual: {}."
184+
+ " If you are quite sure that there is no miscalculation (not on the main network)"
185+
+ ", please configure 'storage.merkleRoot.reward-vi = {}'"
186+
+ "(for a specific network such as Nile, etc.) in config.conf to fix the hints",
187+
rewardViRoot, rewardViRootLocal, rewardViRootLocal);
181188
}
182189
logger.info("calcMerkleRoot: {}", rewardViRootLocal);
183190
}

chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,10 +2525,6 @@ public boolean useNewRewardAlgorithm() {
25252525
return getNewRewardAlgorithmEffectiveCycle() != Long.MAX_VALUE;
25262526
}
25272527

2528-
public boolean useNewRewardAlgorithmFromStart() {
2529-
return getNewRewardAlgorithmEffectiveCycle() == 1;
2530-
}
2531-
25322528
public void saveNewRewardAlgorithmEffectiveCycle() {
25332529
if (getNewRewardAlgorithmEffectiveCycle() == Long.MAX_VALUE) {
25342530
long currentCycle = getCurrentCycleNumber();
@@ -2839,19 +2835,8 @@ public boolean supportMaxDelegateLockPeriod() {
28392835
getUnfreezeDelayDays() > 0;
28402836
}
28412837

2842-
/**
2843-
* @require NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE != Long.MAX_VALUE
2844-
* @require NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE > 1
2845-
*/
28462838
public void saveAllowOldRewardOpt(long allowOldRewardOpt) {
2847-
if (useNewRewardAlgorithm()) {
2848-
if (useNewRewardAlgorithmFromStart()) {
2849-
throw new IllegalStateException("no need old reward opt, ALLOW_NEW_REWARD from start");
2850-
}
2851-
this.put(ALLOW_OLD_REWARD_OPT, new BytesCapsule(ByteArray.fromLong(allowOldRewardOpt)));
2852-
} else {
2853-
throw new IllegalStateException("not support old reward opt, ALLOW_NEW_REWARD not set");
2854-
}
2839+
this.put(ALLOW_OLD_REWARD_OPT, new BytesCapsule(ByteArray.fromLong(allowOldRewardOpt)));
28552840
}
28562841

28572842
public boolean allowOldRewardOpt() {

common/src/main/java/org/tron/common/prometheus/MetricKeys.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ public static class Histogram {
6262
public static final String MESSAGE_PROCESS_LATENCY = "tron:message_process_latency_seconds";
6363
public static final String BLOCK_FETCH_LATENCY = "tron:block_fetch_latency_seconds";
6464
public static final String BLOCK_RECEIVE_DELAY = "tron:block_receive_delay_seconds";
65-
public static final String DO_REWARD_CAL_DELAY = "tron:do_reward_cal_seconds";
66-
6765

6866
private Histogram() {
6967
throw new IllegalStateException("Histogram");

common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ public class MetricsHistogram {
4848
init(MetricKeys.Histogram.BLOCK_FETCH_LATENCY, "fetch block latency.");
4949
init(MetricKeys.Histogram.BLOCK_RECEIVE_DELAY,
5050
"receive block delay time, receiveTime - blockTime.");
51-
init(MetricKeys.Histogram.DO_REWARD_CAL_DELAY,
52-
"do reward cal delay time.", "depth");
5351
}
5452

5553
private MetricsHistogram() {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.tron.common.utils;
2+
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.IntStream;
6+
import lombok.Getter;
7+
8+
public class MerkleRoot {
9+
10+
private MerkleRoot() {
11+
12+
}
13+
14+
public static Sha256Hash root(List<Sha256Hash> hashList) {
15+
List<Leaf> leaves = createLeaves(hashList);
16+
while (leaves.size() > 1) {
17+
leaves = createParentLeaves(leaves);
18+
}
19+
return leaves.isEmpty() ? Sha256Hash.ZERO_HASH : leaves.get(0).hash;
20+
}
21+
22+
private static List<Leaf> createParentLeaves(List<Leaf> leaves) {
23+
int step = 2;
24+
int len = leaves.size();
25+
return IntStream.iterate(0, i -> i + step)
26+
.limit(len)
27+
.filter(i -> i < len)
28+
.mapToObj(i -> {
29+
Leaf right = i + 1 < len ? leaves.get(i + 1) : null;
30+
return createLeaf(leaves.get(i), right);
31+
}).collect(Collectors.toList());
32+
}
33+
34+
private static List<Leaf> createLeaves(List<Sha256Hash> hashList) {
35+
int step = 2;
36+
int len = hashList.size();
37+
return IntStream.iterate(0, i -> i + step)
38+
.limit(len)
39+
.filter(i -> i < len)
40+
.mapToObj(i -> {
41+
Leaf right = i + 1 < len ? createLeaf(hashList.get(i + 1)) : null;
42+
return createLeaf(createLeaf(hashList.get(i)), right);
43+
}).collect(Collectors.toList());
44+
}
45+
46+
private static Leaf createLeaf(Leaf left, Leaf right) {
47+
Leaf leaf = new Leaf();
48+
leaf.hash = right == null ? left.hash : computeHash(left.hash, right.hash);
49+
return leaf;
50+
}
51+
52+
private static Leaf createLeaf(Sha256Hash hash) {
53+
Leaf leaf = new Leaf();
54+
leaf.hash = hash;
55+
return leaf;
56+
}
57+
58+
private static Sha256Hash computeHash(Sha256Hash leftHash, Sha256Hash rightHash) {
59+
return Sha256Hash.of(true,
60+
leftHash.getByteString().concat(rightHash.getByteString()).toByteArray());
61+
}
62+
63+
@Getter
64+
private static class Leaf {
65+
66+
private Sha256Hash hash;
67+
}
68+
}

0 commit comments

Comments
 (0)