From 8b58775d7039b666e4369f477032936ef03d9386 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Thu, 12 Jun 2025 17:59:24 +0800 Subject: [PATCH 01/22] add wallet draft logic --- .../src/main/java/org/tron/core/Wallet.java | 119 +++++++++++------- .../services/http/FullNodeHttpApiService.java | 5 + .../GetPaginatedNowWitnessListServlet.java | 53 ++++++++ 3 files changed, 130 insertions(+), 47 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 27c942bf6b4..b0758a9d3d0 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -40,25 +40,16 @@ import static org.tron.core.vm.utils.FreezeV2Util.getV2NetUsage; import static org.tron.protos.contract.Common.ResourceCode; -import com.google.common.collect.ContiguousSet; -import com.google.common.collect.DiscreteDomain; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Range; +import com.google.common.collect.*; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolStringList; import java.math.BigInteger; import java.security.SignatureException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; +import java.util.stream.Collectors; + import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; @@ -137,31 +128,8 @@ import org.tron.core.actuator.ActuatorFactory; import org.tron.core.actuator.UnfreezeBalanceV2Actuator; import org.tron.core.actuator.VMActuator; -import org.tron.core.capsule.AbiCapsule; -import org.tron.core.capsule.AccountCapsule; -import org.tron.core.capsule.AssetIssueCapsule; -import org.tron.core.capsule.BlockBalanceTraceCapsule; -import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.*; import org.tron.core.capsule.BlockCapsule.BlockId; -import org.tron.core.capsule.BytesCapsule; -import org.tron.core.capsule.CodeCapsule; -import org.tron.core.capsule.ContractCapsule; -import org.tron.core.capsule.ContractStateCapsule; -import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule; -import org.tron.core.capsule.DelegatedResourceCapsule; -import org.tron.core.capsule.ExchangeCapsule; -import org.tron.core.capsule.IncrementalMerkleTreeCapsule; -import org.tron.core.capsule.IncrementalMerkleVoucherCapsule; -import org.tron.core.capsule.MarketAccountOrderCapsule; -import org.tron.core.capsule.MarketOrderCapsule; -import org.tron.core.capsule.MarketOrderIdListCapsule; -import org.tron.core.capsule.PedersenHashCapsule; -import org.tron.core.capsule.ProposalCapsule; -import org.tron.core.capsule.TransactionCapsule; -import org.tron.core.capsule.TransactionInfoCapsule; -import org.tron.core.capsule.TransactionResultCapsule; -import org.tron.core.capsule.TransactionRetCapsule; -import org.tron.core.capsule.WitnessCapsule; import org.tron.core.capsule.utils.MarketUtils; import org.tron.core.config.args.Args; import org.tron.core.db.BandwidthProcessor; @@ -191,16 +159,7 @@ import org.tron.core.net.TronNetDelegate; import org.tron.core.net.TronNetService; import org.tron.core.net.message.adv.TransactionMessage; -import org.tron.core.store.AccountIdIndexStore; -import org.tron.core.store.AccountStore; -import org.tron.core.store.AccountTraceStore; -import org.tron.core.store.BalanceTraceStore; -import org.tron.core.store.ContractStore; -import org.tron.core.store.DynamicPropertiesStore; -import org.tron.core.store.MarketOrderStore; -import org.tron.core.store.MarketPairPriceToOrderStore; -import org.tron.core.store.MarketPairToPriceStore; -import org.tron.core.store.StoreFactory; +import org.tron.core.store.*; import org.tron.core.utils.TransactionUtil; import org.tron.core.vm.program.Program; import org.tron.core.zen.ShieldedTRC20ParametersBuilder; @@ -260,6 +219,7 @@ public class Wallet { private static final String SHIELDED_TRANSACTION_SCAN_RANGE = "request requires start_block_index >= 0 && end_block_index > " + "start_block_index && end_block_index - start_block_index <= 1000"; + private static final String PAGED_WITNESS_LIMIT_RANGE = "request requires witness limit <= 1000"; private static String addressPreFixString = Constant.ADD_PRE_FIX_STRING_MAINNET;//default testnet private static final byte[] SHIELDED_TRC20_LOG_TOPICS_MINT = Hash.sha3(ByteArray.fromString( "MintNewLeaf(uint256,bytes32,bytes32,bytes32,bytes32[21])")); @@ -764,6 +724,71 @@ public WitnessList getWitnessList() { return builder.build(); } + public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws IllegalArgumentException { + if (limit <= 0 || offset < 0) { + return null; + } + if (limit > 1000) { + throw new IllegalArgumentException(PAGED_WITNESS_LIMIT_RANGE); + } + + VotesStore votesStore = chainBaseManager.getVotesStore(); + Map countWitness = countVote(votesStore); + List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); + + witnessCapsuleList.forEach((witnessCapsule) -> { + // Iterate through the witness list and add the vote count, it may be negative. + long voteCount = countWitness.get(witnessCapsule.getAddress()); + witnessCapsule.setVoteCount(witnessCapsule.getVoteCount() + voteCount); + }); + + // Apply sorting, pagination + List sortedWitnessList = witnessCapsuleList.stream() + .sorted(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed()) + .skip(offset) + .limit(limit) + .collect(Collectors.toList()); + + // Add sorted witnesses to builder + WitnessList.Builder builder = WitnessList.newBuilder(); + sortedWitnessList.forEach(witnessCapsule -> + builder.addWitnesses(witnessCapsule.getInstance())); + + return builder.build(); + } + + private Map countVote(VotesStore votesStore) { + final Map countWitness = Maps.newHashMap(); + Iterator> dbIterator = votesStore.iterator(); + long sizeCount = 0; + while (dbIterator.hasNext()) { + Entry next = dbIterator.next(); + VotesCapsule votes = next.getValue(); + votes.getOldVotes().forEach(vote -> { + ByteString voteAddress = vote.getVoteAddress(); + long voteCount = vote.getVoteCount(); + if (countWitness.containsKey(voteAddress)) { + countWitness.put(voteAddress, countWitness.get(voteAddress) - voteCount); + } else { + countWitness.put(voteAddress, -voteCount); + } + }); + votes.getNewVotes().forEach(vote -> { + ByteString voteAddress = vote.getVoteAddress(); + long voteCount = vote.getVoteCount(); + if (countWitness.containsKey(voteAddress)) { + countWitness.put(voteAddress, countWitness.get(voteAddress) + voteCount); + } else { + countWitness.put(voteAddress, voteCount); + } + }); + sizeCount++; + votesStore.delete(next.getKey()); + } + logger.info("There is {} new votes in this epoch", sizeCount); + return countWitness; + } + public ProposalList getProposalList() { ProposalList.Builder builder = ProposalList.newBuilder(); List proposalCapsuleList = diff --git a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java index 76785218096..1076931a218 100644 --- a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java @@ -86,6 +86,8 @@ public class FullNodeHttpApiService extends HttpService { @Autowired private ListWitnessesServlet listWitnessesServlet; @Autowired + private GetPaginatedNowWitnessListServlet getPaginatedNowWitnessListServlet; + @Autowired private GetAssetIssueListServlet getAssetIssueListServlet; @Autowired private GetPaginatedAssetIssueListServlet getPaginatedAssetIssueListServlet; @@ -342,7 +344,10 @@ protected void addServlet(ServletContextHandler context) { context.addServlet( new ServletHolder(getTransactionCountByBlockNumServlet), "/wallet/gettransactioncountbyblocknum"); + // Get the list of witnesses info with contains vote counts for last epoch/maintenance context.addServlet(new ServletHolder(listWitnessesServlet), "/wallet/listwitnesses"); + // Get the paged list of witnesses info with realtime vote counts + context.addServlet(new ServletHolder(getPaginatedNowWitnessListServlet), "/wallet/getpaginatednowwitnesslist"); context.addServlet(new ServletHolder(getAssetIssueListServlet), "/wallet/getassetissuelist"); context.addServlet( new ServletHolder(getPaginatedAssetIssueListServlet), diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java new file mode 100644 index 00000000000..f8c52ed2a8a --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -0,0 +1,53 @@ +package org.tron.core.services.http; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.api.GrpcAPI; +import org.tron.api.GrpcAPI.WitnessList; +import org.tron.core.Wallet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +// Get the paged list of witnesses info with realtime vote counts +@Component +@Slf4j(topic = "API") +public class GetPaginatedNowWitnessListServlet extends RateLimiterServlet { + + @Autowired + private Wallet wallet; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + try { + boolean visible = Util.getVisible(request); + long offset = Long.parseLong(request.getParameter("offset")); + long limit = Long.parseLong(request.getParameter("limit")); + fillResponse(offset, limit, visible, response); + } catch (Exception e) { + Util.processError(e, response); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + try { + PostParams params = PostParams.getPostParams(request); + GrpcAPI.PaginatedMessage.Builder build = GrpcAPI.PaginatedMessage.newBuilder(); + JsonFormat.merge(params.getParams(), build, params.isVisible()); + fillResponse(build.getOffset(), build.getLimit(), params.isVisible(), response); + } catch (Exception e) { + Util.processError(e, response); + } + } + + private void fillResponse(long offset, long limit, boolean visible, HttpServletResponse response) + throws IOException, IllegalArgumentException { + GrpcAPI.WitnessList reply = wallet.getPaginatedNowWitnessList(offset, limit); + if (reply != null) { + response.getWriter().println(JsonFormat.printToString(reply, visible)); + } else { + response.getWriter().println("{}"); + } + } +} From a6179b1771754915e94a4b8a1c5d515da00bcd68 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Fri, 20 Jun 2025 17:39:36 +0800 Subject: [PATCH 02/22] update logic --- .../src/main/java/org/tron/core/Wallet.java | 38 ++++-- .../org/tron/core/services/RpcApiService.java | 12 ++ .../GetPaginatedNowWitnessListServlet.java | 2 +- .../RpcApiServiceOnSolidity.java | 6 + ...inatedNowWitnessListOnSolidityServlet.java | 25 ++++ .../solidity/HttpApiOnSolidityService.java | 51 +------- .../test/java/org/tron/core/WalletTest.java | 113 ++++++++++++++++-- protocol/src/main/protos/api/api.proto | 6 + 8 files changed, 185 insertions(+), 68 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index b0758a9d3d0..48369b25af7 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -724,32 +724,38 @@ public WitnessList getWitnessList() { return builder.build(); } - public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws IllegalArgumentException { + public WitnessList getPaginatedNowWitnessList(long offset, long limit) { if (limit <= 0 || offset < 0) { return null; } + // To control the maximum response size less than 40KB. if (limit > 1000) { - throw new IllegalArgumentException(PAGED_WITNESS_LIMIT_RANGE); + limit = 1000; } VotesStore votesStore = chainBaseManager.getVotesStore(); + // Count the vote changes for each witness in the current epoch, it is maybe negative. Map countWitness = countVote(votesStore); + + // Get all witnesses from the store, it contains the final vote count at the end of the last epoch. List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); + // Iterate through the witness list and apply the vote changes, it will be the realtime vote count. witnessCapsuleList.forEach((witnessCapsule) -> { - // Iterate through the witness list and add the vote count, it may be negative. - long voteCount = countWitness.get(witnessCapsule.getAddress()); + long voteCount = countWitness.getOrDefault(witnessCapsule.getAddress(), 0L); + // since the above chainBaseManager.getWitnessStore().getAllWitnesses() + // each time return a copy of the witness list, thus this update will not affect the original database list. witnessCapsule.setVoteCount(witnessCapsule.getVoteCount() + voteCount); }); - // Apply sorting, pagination + // Return the witness with the highest vote counts at first and skip the offset with limit List sortedWitnessList = witnessCapsuleList.stream() .sorted(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed()) .skip(offset) .limit(limit) .collect(Collectors.toList()); - // Add sorted witnesses to builder + // Pack the sorted WitnessCapsule list into a WitnessList object WitnessList.Builder builder = WitnessList.newBuilder(); sortedWitnessList.forEach(witnessCapsule -> builder.addWitnesses(witnessCapsule.getInstance())); @@ -757,13 +763,28 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws Il return builder.build(); } + /* + countVote(VotesStore votesStore): Counts the vote count changes for witnesses in the current epoch, + If the voters change their votes in the current epoch, + the vote count for last epoch witness will be negative, while the new vote count will be positive. + For example: + Account A votes for witness W1 in the last epoch with 100 votes, + in the current epoch, A change the votes for W2 with 60 votes W3 with 80 votes, + then the vote count for W1 will be -100, while the vote count for W2 will be 60, W3 will be 80. + */ private Map countVote(VotesStore votesStore) { - final Map countWitness = Maps.newHashMap(); + // Initialize a result map to store vote changes for each witness + Map countWitness = Maps.newHashMap(); + + // VotesStore is a key-value store, where the key is the address of the voter Iterator> dbIterator = votesStore.iterator(); + long sizeCount = 0; while (dbIterator.hasNext()) { Entry next = dbIterator.next(); VotesCapsule votes = next.getValue(); + + // For each voter, we iterate the old votes and new votes to calculate the vote count changes votes.getOldVotes().forEach(vote -> { ByteString voteAddress = vote.getVoteAddress(); long voteCount = vote.getVoteCount(); @@ -783,9 +804,8 @@ private Map countVote(VotesStore votesStore) { } }); sizeCount++; - votesStore.delete(next.getKey()); } - logger.info("There is {} new votes in this epoch", sizeCount); + logger.info("There is {} votes in this request", sizeCount); return countWitness; } diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index 8f9c6b15bb7..fffbed5e091 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -396,6 +396,12 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp responseObserver.onCompleted(); } + @Override + public void getPaginatedNowWitnessList(PaginatedMessage request, StreamObserver responseObserver) { + responseObserver.onNext(wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + responseObserver.onCompleted(); + } + @Override public void getAssetIssueList(EmptyMessage request, StreamObserver responseObserver) { @@ -1872,6 +1878,12 @@ public void listWitnesses(EmptyMessage request, responseObserver.onCompleted(); } + @Override + public void getPaginatedNowWitnessList(PaginatedMessage request, StreamObserver responseObserver) { + responseObserver.onNext(wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + responseObserver.onCompleted(); + } + @Override public void listProposals(EmptyMessage request, StreamObserver responseObserver) { diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java index f8c52ed2a8a..b1ac1febd26 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -42,7 +42,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } private void fillResponse(long offset, long limit, boolean visible, HttpServletResponse response) - throws IOException, IllegalArgumentException { + throws IOException { GrpcAPI.WitnessList reply = wallet.getPaginatedNowWitnessList(offset, limit); if (reply != null) { response.getWriter().println(JsonFormat.printToString(reply, visible)); diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java index aa566f56042..c0333b52025 100755 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java @@ -162,6 +162,12 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp () -> rpcApiService.getWalletSolidityApi().listWitnesses(request, responseObserver)); } + public void getPaginatedNowWitnessList(PaginatedMessage request, + StreamObserver responseObserver) { + walletOnSolidity.futureGet( + () -> rpcApiService.getWalletSolidityApi().getPaginatedNowWitnessList(request, responseObserver)); + } + @Override public void getAssetIssueById(BytesMessage request, StreamObserver responseObserver) { diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java new file mode 100644 index 00000000000..4ba859aee0f --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java @@ -0,0 +1,25 @@ +package org.tron.core.services.interfaceOnSolidity.http; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.core.services.http.GetPaginatedNowWitnessListServlet; +import org.tron.core.services.interfaceOnSolidity.WalletOnSolidity; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +@Slf4j(topic = "API") +public class GetPaginatedNowWitnessListOnSolidityServlet extends GetPaginatedNowWitnessListServlet { + @Autowired + private WalletOnSolidity walletOnSolidity; + + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + walletOnSolidity.futureGet(() -> super.doGet(request, response)); + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + walletOnSolidity.futureGet(() -> super.doPost(request, response)); + } +} diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java index b1d940ce2cd..cd74ec3f010 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java @@ -13,52 +13,7 @@ import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpApiAccessFilter; import org.tron.core.services.filter.LiteFnQueryHttpFilter; -import org.tron.core.services.interfaceOnSolidity.http.EstimateEnergyOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAccountByIdOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAccountOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueByIdOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueByNameOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueListByNameOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueListOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetAvailableUnfreezeCountOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBandwidthPricesOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBlockByIdOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBlockByLatestNumOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBlockByLimitNextOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBlockByNumOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBlockOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBrokerageOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetBurnTrxOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetCanDelegatedMaxSizeOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetCanWithdrawUnfreezeAmountOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceAccountIndexOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceAccountIndexV2OnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceV2OnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetEnergyPricesOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetExchangeByIdOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetMarketOrderByAccountOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetMarketOrderByIdOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetMarketOrderListByPairOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetMarketPairListOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetMarketPriceByPairOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetMerkleTreeVoucherInfoOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetNodeInfoOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetNowBlockOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedAssetIssueListOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetRewardOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetTransactionCountByBlockNumOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetTransactionInfoByBlockNumOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.IsShieldedTRC20ContractNoteSpentOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.IsSpendOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ListExchangesOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ListWitnessesOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ScanAndMarkNoteByIvkOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ScanNoteByIvkOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ScanNoteByOvkOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByIvkOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByOvkOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.TriggerConstantContractOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.*; @Slf4j(topic = "API") @@ -74,6 +29,8 @@ public class HttpApiOnSolidityService extends HttpService { @Autowired private ListWitnessesOnSolidityServlet listWitnessesOnSolidityServlet; @Autowired + private GetPaginatedNowWitnessListOnSolidityServlet getPaginatedNowWitnessListOnSolidityServlet; + @Autowired private GetAssetIssueListOnSolidityServlet getAssetIssueListOnSolidityServlet; @Autowired private GetPaginatedAssetIssueListOnSolidityServlet getPaginatedAssetIssueListOnSolidityServlet; @@ -189,6 +146,8 @@ protected void addServlet(ServletContextHandler context) { context.addServlet(new ServletHolder(accountOnSolidityServlet), "/walletsolidity/getaccount"); context.addServlet(new ServletHolder(listWitnessesOnSolidityServlet), "/walletsolidity/listwitnesses"); + context.addServlet(new ServletHolder(getPaginatedNowWitnessListOnSolidityServlet), + "/walletsolidity/getpaginatednowwitnesslist"); context.addServlet(new ServletHolder(getAssetIssueListOnSolidityServlet), "/walletsolidity/getassetissuelist"); context.addServlet(new ServletHolder(getPaginatedAssetIssueListOnSolidityServlet), diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 831490fdca1..51ac0f86956 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -29,7 +29,10 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; + +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.annotation.Resource; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -54,18 +57,7 @@ import org.tron.core.actuator.DelegateResourceActuator; import org.tron.core.actuator.FreezeBalanceActuator; import org.tron.core.actuator.UnfreezeBalanceV2Actuator; -import org.tron.core.capsule.AccountCapsule; -import org.tron.core.capsule.AssetIssueCapsule; -import org.tron.core.capsule.BlockCapsule; -import org.tron.core.capsule.BytesCapsule; -import org.tron.core.capsule.CodeCapsule; -import org.tron.core.capsule.ContractCapsule; -import org.tron.core.capsule.DelegatedResourceCapsule; -import org.tron.core.capsule.ExchangeCapsule; -import org.tron.core.capsule.ProposalCapsule; -import org.tron.core.capsule.TransactionCapsule; -import org.tron.core.capsule.TransactionInfoCapsule; -import org.tron.core.capsule.TransactionResultCapsule; +import org.tron.core.capsule.*; import org.tron.core.config.args.Args; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; @@ -851,6 +843,103 @@ public void testGetDelegatedResourceV2() { } } + @Test + public void testGetPaginatedNowWitnessList_CornerCase() { + GrpcAPI.WitnessList witnessList = wallet.getPaginatedNowWitnessList(-100, 0); + Assert.assertTrue("Should return an empty witness list when offset is negative", + witnessList == null); + + witnessList = wallet.getPaginatedNowWitnessList(100, 0); + Assert.assertTrue("Should return an empty witness list when limit is 0", + witnessList == null); + + String fakeWitnessAddressPrefix = "fake_witness"; + int fakeNumberOfWitnesses = 1000 + 10; + // Mock additional witnesses with vote counts greater than 1000 + for(int i = 0; i < fakeNumberOfWitnesses; i++) { + saveWitnessWith(fakeWitnessAddressPrefix + i, 200); + } + + witnessList = wallet.getPaginatedNowWitnessList(0, 1000000); + // Check the returned witness list should contain 1000 witnesses with descending vote count + Assert.assertTrue("Witness list should contain 1000 witnesses", + witnessList.getWitnessesCount() == 1000); + + // clean up, delete the fake witnesses + for(int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore().delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + } + } + + @Test + public void testGetPaginatedNowWitnessList() { + GrpcAPI.WitnessList witnessList = wallet.getWitnessList(); + logger.info(witnessList.toString()); + + // iterate through the witness list and find the existing maximum vote count + long maxVoteCount = 0L; + for (Protocol.Witness witness : witnessList.getWitnessesList()) { + if (witness.getVoteCount() > maxVoteCount) { + maxVoteCount = witness.getVoteCount(); + } + } + String fakeWitnessAddressPrefix = "fake_witness_address_for_paged_now_witness_list"; + int fakeNumberOfWitnesses = 10; + // Mock additional witnesses with vote counts greater than the maximum + for(int i = 0; i < fakeNumberOfWitnesses; i++) { + saveWitnessWith(fakeWitnessAddressPrefix + i, maxVoteCount + 1000000L); + } + + // Create a VotesCapsule to simulate the votes for the fake witnesses + VotesCapsule votesCapsule = new VotesCapsule(ByteString.copyFromUtf8(ACCOUNT_ADDRESS_ONE), new ArrayList()); + votesCapsule.addOldVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 0), 100L); + votesCapsule.addOldVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 1), 50L); + votesCapsule.addNewVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 2), 200L); + votesCapsule.addNewVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 3), 300L); + chainBaseManager.getVotesStore().put(votesCapsule.createDbKey(), votesCapsule); + + logger.info("now request paginated witness list with 0 offset and 10 limit:"); + GrpcAPI.WitnessList witnessList2 = wallet.getPaginatedNowWitnessList(0, 10); + // Check the returned witness list should contain 10 witnesses with descending vote count + Assert.assertTrue("Witness list should contain 10 witnesses", + witnessList2.getWitnessesCount() == 10); + // Check the first witness should have the maximum vote count + Assert.assertEquals("The first witness should have the maximum vote count",fakeWitnessAddressPrefix + 3, witnessList2.getWitnesses(0).getAddress().toStringUtf8()); + Assert.assertEquals( maxVoteCount + 1000300L, witnessList2.getWitnesses(0).getVoteCount()); + // Check the second witness should have the second maximum vote count + Assert.assertEquals("The second witness", fakeWitnessAddressPrefix + 2, witnessList2.getWitnesses(1).getAddress().toStringUtf8()); + Assert.assertEquals( maxVoteCount + 1000200L, witnessList2.getWitnesses(1).getVoteCount()); + // Check the last witness should have the least vote count + Assert.assertEquals("The tenth witness", fakeWitnessAddressPrefix + 0, witnessList2.getWitnesses(9).getAddress().toStringUtf8()); + Assert.assertEquals( maxVoteCount + 1000000L - 100L, witnessList2.getWitnesses(9).getVoteCount()); + + + logger.info("after paged"); + GrpcAPI.WitnessList witnessList3 = wallet.getWitnessList(); + // Check the witness list should remain unchanged after paged request + for (Protocol.Witness witness : witnessList3.getWitnessesList()) { + if (witness.getVoteCount() > maxVoteCount) { + Assert.assertTrue("Check the witness list should remain unchanged after paged request", witness.getVoteCount() == maxVoteCount + 1000000L); + } + } + + // clean up, delete the fake witnesses + for(int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore().delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + } + chainBaseManager.getVotesStore().delete(votesCapsule.createDbKey()); + Assert.assertTrue("Clean up the mocked witness data",wallet.getWitnessList().getWitnessesCount() == witnessList.getWitnessesCount()); + + } + + public void saveWitnessWith(String witnessAddress, long voteCount) { + WitnessCapsule witness = new WitnessCapsule( + Protocol.Witness.newBuilder() + .setAddress(ByteString.copyFromUtf8(witnessAddress)) // Convert String to ByteString + .setVoteCount(voteCount).build()); + chainBaseManager.getWitnessStore().put(witness.getAddress().toByteArray(), witness); + } + @Test public void testGetDelegatedResourceAccountIndexV2() { dbManager.getDynamicPropertiesStore().saveUnfreezeDelayDays(1L); diff --git a/protocol/src/main/protos/api/api.proto b/protocol/src/main/protos/api/api.proto index 2505fa48d6f..a67113cb606 100644 --- a/protocol/src/main/protos/api/api.proto +++ b/protocol/src/main/protos/api/api.proto @@ -499,6 +499,8 @@ service Wallet { }; }; + rpc GetPaginatedNowWitnessList (PaginatedMessage) returns (WitnessList) { + }; rpc GetDelegatedResource (DelegatedResourceMessage) returns (DelegatedResourceList) { }; @@ -808,6 +810,10 @@ service WalletSolidity { } }; }; + + rpc GetPaginatedNowWitnessList (PaginatedMessage) returns (WitnessList) { + }; + rpc GetAssetIssueList (EmptyMessage) returns (AssetIssueList) { option (google.api.http) = { post: "/walletsolidity/getassetissuelist" From 8004c64561c27f6586f5dd1027f9281aec83d68f Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Mon, 23 Jun 2025 10:41:28 +0800 Subject: [PATCH 03/22] remove unused logic --- framework/src/main/java/org/tron/core/Wallet.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 48369b25af7..cce487f3dfd 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -219,7 +219,6 @@ public class Wallet { private static final String SHIELDED_TRANSACTION_SCAN_RANGE = "request requires start_block_index >= 0 && end_block_index > " + "start_block_index && end_block_index - start_block_index <= 1000"; - private static final String PAGED_WITNESS_LIMIT_RANGE = "request requires witness limit <= 1000"; private static String addressPreFixString = Constant.ADD_PRE_FIX_STRING_MAINNET;//default testnet private static final byte[] SHIELDED_TRC20_LOG_TOPICS_MINT = Hash.sha3(ByteArray.fromString( "MintNewLeaf(uint256,bytes32,bytes32,bytes32,bytes32[21])")); From 3bb27a852457e1ae490fda65111b6000f54e8d5d Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Wed, 25 Jun 2025 17:13:43 +0800 Subject: [PATCH 04/22] revert merge import folders --- .../solidity/HttpApiOnSolidityService.java | 49 ++++++++++++++++++- .../test/java/org/tron/core/WalletTest.java | 15 +++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java index cd74ec3f010..5586ceea5af 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java @@ -13,8 +13,53 @@ import org.tron.core.config.args.Args; import org.tron.core.services.filter.HttpApiAccessFilter; import org.tron.core.services.filter.LiteFnQueryHttpFilter; -import org.tron.core.services.interfaceOnSolidity.http.*; - +import org.tron.core.services.interfaceOnSolidity.http.EstimateEnergyOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAccountByIdOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAccountOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueByIdOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueByNameOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueListByNameOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAssetIssueListOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetAvailableUnfreezeCountOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBandwidthPricesOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBlockByIdOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBlockByLatestNumOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBlockByLimitNextOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBlockByNumOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBlockOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBrokerageOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetBurnTrxOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetCanDelegatedMaxSizeOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetCanWithdrawUnfreezeAmountOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceAccountIndexOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceAccountIndexV2OnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetDelegatedResourceV2OnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetEnergyPricesOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetExchangeByIdOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetMarketOrderByAccountOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetMarketOrderByIdOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetMarketOrderListByPairOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetMarketPairListOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetMarketPriceByPairOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetMerkleTreeVoucherInfoOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetNodeInfoOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetNowBlockOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedAssetIssueListOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetRewardOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetTransactionCountByBlockNumOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetTransactionInfoByBlockNumOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.IsShieldedTRC20ContractNoteSpentOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.IsSpendOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ListExchangesOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ListWitnessesOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ScanAndMarkNoteByIvkOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ScanNoteByIvkOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ScanNoteByOvkOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByIvkOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByOvkOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.TriggerConstantContractOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedNowWitnessListOnSolidityServlet; @Slf4j(topic = "API") public class HttpApiOnSolidityService extends HttpService { diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 51ac0f86956..5eccfb17dc1 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -57,7 +57,20 @@ import org.tron.core.actuator.DelegateResourceActuator; import org.tron.core.actuator.FreezeBalanceActuator; import org.tron.core.actuator.UnfreezeBalanceV2Actuator; -import org.tron.core.capsule.*; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.AssetIssueCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.BytesCapsule; +import org.tron.core.capsule.CodeCapsule; +import org.tron.core.capsule.ContractCapsule; +import org.tron.core.capsule.DelegatedResourceCapsule; +import org.tron.core.capsule.ExchangeCapsule; +import org.tron.core.capsule.ProposalCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionInfoCapsule; +import org.tron.core.capsule.TransactionResultCapsule; +import org.tron.core.capsule.VotesCapsule; +import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.args.Args; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; From 451fea4e388495b50632a3b48ecca744eff15e80 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Wed, 25 Jun 2025 17:22:54 +0800 Subject: [PATCH 05/22] fix merge import --- .../src/main/java/org/tron/core/Wallet.java | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index cce487f3dfd..924c19128b0 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -40,16 +40,28 @@ import static org.tron.core.vm.utils.FreezeV2Util.getV2NetUsage; import static org.tron.protos.contract.Common.ResourceCode; -import com.google.common.collect.*; +import com.google.common.collect.ContiguousSet; +import com.google.common.collect.DiscreteDomain; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Range; +import com.google.common.collect.Maps; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolStringList; import java.math.BigInteger; import java.security.SignatureException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; - +import java.util.Comparator; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; @@ -128,9 +140,33 @@ import org.tron.core.actuator.ActuatorFactory; import org.tron.core.actuator.UnfreezeBalanceV2Actuator; import org.tron.core.actuator.VMActuator; -import org.tron.core.capsule.*; +import org.tron.core.capsule.AbiCapsule; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.AssetIssueCapsule; +import org.tron.core.capsule.BlockBalanceTraceCapsule; +import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.BlockCapsule.BlockId; +import org.tron.core.capsule.BytesCapsule; +import org.tron.core.capsule.CodeCapsule; +import org.tron.core.capsule.ContractCapsule; +import org.tron.core.capsule.ContractStateCapsule; +import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule; +import org.tron.core.capsule.DelegatedResourceCapsule; +import org.tron.core.capsule.ExchangeCapsule; +import org.tron.core.capsule.IncrementalMerkleTreeCapsule; +import org.tron.core.capsule.IncrementalMerkleVoucherCapsule; +import org.tron.core.capsule.MarketAccountOrderCapsule; +import org.tron.core.capsule.MarketOrderCapsule; +import org.tron.core.capsule.MarketOrderIdListCapsule; +import org.tron.core.capsule.PedersenHashCapsule; +import org.tron.core.capsule.ProposalCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionInfoCapsule; +import org.tron.core.capsule.TransactionResultCapsule; +import org.tron.core.capsule.TransactionRetCapsule; +import org.tron.core.capsule.WitnessCapsule; import org.tron.core.capsule.utils.MarketUtils; +import org.tron.core.capsule.VotesCapsule; import org.tron.core.config.args.Args; import org.tron.core.db.BandwidthProcessor; import org.tron.core.db.BlockIndexStore; @@ -159,7 +195,17 @@ import org.tron.core.net.TronNetDelegate; import org.tron.core.net.TronNetService; import org.tron.core.net.message.adv.TransactionMessage; -import org.tron.core.store.*; +import org.tron.core.store.AccountIdIndexStore; +import org.tron.core.store.AccountStore; +import org.tron.core.store.AccountTraceStore; +import org.tron.core.store.BalanceTraceStore; +import org.tron.core.store.ContractStore; +import org.tron.core.store.DynamicPropertiesStore; +import org.tron.core.store.MarketOrderStore; +import org.tron.core.store.MarketPairPriceToOrderStore; +import org.tron.core.store.MarketPairToPriceStore; +import org.tron.core.store.StoreFactory; +import org.tron.core.store.VotesStore; import org.tron.core.utils.TransactionUtil; import org.tron.core.vm.program.Program; import org.tron.core.zen.ShieldedTRC20ParametersBuilder; From 833be111ec2c95d2e7feaec775623cb99960a0cf Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Thu, 26 Jun 2025 17:29:46 +0800 Subject: [PATCH 06/22] fix review --- .../src/main/java/org/tron/core/Wallet.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 924c19128b0..d4ddc4bb241 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -295,6 +295,7 @@ public class Wallet { public static final String CONTRACT_VALIDATE_EXCEPTION = "ContractValidateException: {}"; public static final String CONTRACT_VALIDATE_ERROR = "Contract validate error : "; + private static int GET_WITNESS_LIST_MAX_LIMIT = 1000; /** * Creates a new Wallet with a random ECKey. */ @@ -774,17 +775,20 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { return null; } // To control the maximum response size less than 40KB. - if (limit > 1000) { - limit = 1000; + if (limit > GET_WITNESS_LIST_MAX_LIMIT) { + limit = GET_WITNESS_LIST_MAX_LIMIT; + } + + // Get all witnesses from the store, it contains the final vote count at the end of the last epoch. + List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); + if (offset >= witnessCapsuleList.size()) { + return null; } VotesStore votesStore = chainBaseManager.getVotesStore(); // Count the vote changes for each witness in the current epoch, it is maybe negative. Map countWitness = countVote(votesStore); - // Get all witnesses from the store, it contains the final vote count at the end of the last epoch. - List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); - // Iterate through the witness list and apply the vote changes, it will be the realtime vote count. witnessCapsuleList.forEach((witnessCapsule) -> { long voteCount = countWitness.getOrDefault(witnessCapsule.getAddress(), 0L); @@ -824,7 +828,6 @@ private Map countVote(VotesStore votesStore) { // VotesStore is a key-value store, where the key is the address of the voter Iterator> dbIterator = votesStore.iterator(); - long sizeCount = 0; while (dbIterator.hasNext()) { Entry next = dbIterator.next(); VotesCapsule votes = next.getValue(); @@ -848,9 +851,7 @@ private Map countVote(VotesStore votesStore) { countWitness.put(voteAddress, voteCount); } }); - sizeCount++; } - logger.info("There is {} votes in this request", sizeCount); return countWitness; } From 55bbebe6b21c112930432002e951a3d502de40f9 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Thu, 26 Jun 2025 18:11:41 +0800 Subject: [PATCH 07/22] fix format --- .../java/org/tron/core/config/Parameter.java | 1 + .../src/main/java/org/tron/core/Wallet.java | 30 +++++-------- .../org/tron/core/services/RpcApiService.java | 12 +++-- .../services/http/FullNodeHttpApiService.java | 3 +- .../GetPaginatedNowWitnessListServlet.java | 2 +- .../RpcApiServiceOnSolidity.java | 5 ++- ...inatedNowWitnessListOnSolidityServlet.java | 16 +++---- .../test/java/org/tron/core/WalletTest.java | 45 +++++++++++-------- 8 files changed, 62 insertions(+), 52 deletions(-) diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index a71dc58e8bd..18867347efd 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -107,6 +107,7 @@ public class DatabaseConstants { public static final int PROPOSAL_COUNT_LIMIT_MAX = 1000; public static final int EXCHANGE_COUNT_LIMIT_MAX = 1000; public static final int MARKET_COUNT_LIMIT_MAX = 1000; + public static final int WITNESS_LIST_LIMIT_MAX = 1000; } public class AdaptiveResourceLimitConstants { diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index d4ddc4bb241..d278cba1282 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -30,6 +30,7 @@ import static org.tron.core.config.Parameter.DatabaseConstants.EXCHANGE_COUNT_LIMIT_MAX; import static org.tron.core.config.Parameter.DatabaseConstants.MARKET_COUNT_LIMIT_MAX; import static org.tron.core.config.Parameter.DatabaseConstants.PROPOSAL_COUNT_LIMIT_MAX; +import static org.tron.core.config.Parameter.DatabaseConstants.WITNESS_LIST_LIMIT_MAX; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseEnergyFee; import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.EARLIEST_STR; import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.FINALIZED_STR; @@ -295,7 +296,6 @@ public class Wallet { public static final String CONTRACT_VALIDATE_EXCEPTION = "ContractValidateException: {}"; public static final String CONTRACT_VALIDATE_ERROR = "Contract validate error : "; - private static int GET_WITNESS_LIST_MAX_LIMIT = 1000; /** * Creates a new Wallet with a random ECKey. */ @@ -775,8 +775,8 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { return null; } // To control the maximum response size less than 40KB. - if (limit > GET_WITNESS_LIST_MAX_LIMIT) { - limit = GET_WITNESS_LIST_MAX_LIMIT; + if (limit > WITNESS_LIST_LIMIT_MAX) { + limit = WITNESS_LIST_LIMIT_MAX; } // Get all witnesses from the store, it contains the final vote count at the end of the last epoch. @@ -799,15 +799,15 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { // Return the witness with the highest vote counts at first and skip the offset with limit List sortedWitnessList = witnessCapsuleList.stream() - .sorted(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed()) - .skip(offset) - .limit(limit) - .collect(Collectors.toList()); + .sorted(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed()) + .skip(offset) + .limit(limit) + .collect(Collectors.toList()); // Pack the sorted WitnessCapsule list into a WitnessList object WitnessList.Builder builder = WitnessList.newBuilder(); sortedWitnessList.forEach(witnessCapsule -> - builder.addWitnesses(witnessCapsule.getInstance())); + builder.addWitnesses(witnessCapsule.getInstance())); return builder.build(); } @@ -836,20 +836,14 @@ private Map countVote(VotesStore votesStore) { votes.getOldVotes().forEach(vote -> { ByteString voteAddress = vote.getVoteAddress(); long voteCount = vote.getVoteCount(); - if (countWitness.containsKey(voteAddress)) { - countWitness.put(voteAddress, countWitness.get(voteAddress) - voteCount); - } else { - countWitness.put(voteAddress, -voteCount); - } + countWitness.put(voteAddress, + countWitness.containsKey(voteAddress) ? countWitness.get(voteAddress) : 0 - voteCount); }); votes.getNewVotes().forEach(vote -> { ByteString voteAddress = vote.getVoteAddress(); long voteCount = vote.getVoteCount(); - if (countWitness.containsKey(voteAddress)) { - countWitness.put(voteAddress, countWitness.get(voteAddress) + voteCount); - } else { - countWitness.put(voteAddress, voteCount); - } + countWitness.put(voteAddress, + countWitness.containsKey(voteAddress) ? countWitness.get(voteAddress) : 0 + voteCount); }); } return countWitness; diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index fffbed5e091..7b768f79ac2 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -397,8 +397,10 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp } @Override - public void getPaginatedNowWitnessList(PaginatedMessage request, StreamObserver responseObserver) { - responseObserver.onNext(wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + public void getPaginatedNowWitnessList(PaginatedMessage request, + StreamObserver responseObserver) { + responseObserver.onNext( + wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); responseObserver.onCompleted(); } @@ -1879,8 +1881,10 @@ public void listWitnesses(EmptyMessage request, } @Override - public void getPaginatedNowWitnessList(PaginatedMessage request, StreamObserver responseObserver) { - responseObserver.onNext(wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + public void getPaginatedNowWitnessList(PaginatedMessage request, + StreamObserver responseObserver) { + responseObserver.onNext( + wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); responseObserver.onCompleted(); } diff --git a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java index 1076931a218..3ad4ace62fc 100644 --- a/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java +++ b/framework/src/main/java/org/tron/core/services/http/FullNodeHttpApiService.java @@ -347,7 +347,8 @@ protected void addServlet(ServletContextHandler context) { // Get the list of witnesses info with contains vote counts for last epoch/maintenance context.addServlet(new ServletHolder(listWitnessesServlet), "/wallet/listwitnesses"); // Get the paged list of witnesses info with realtime vote counts - context.addServlet(new ServletHolder(getPaginatedNowWitnessListServlet), "/wallet/getpaginatednowwitnesslist"); + context.addServlet(new ServletHolder(getPaginatedNowWitnessListServlet), + "/wallet/getpaginatednowwitnesslist"); context.addServlet(new ServletHolder(getAssetIssueListServlet), "/wallet/getassetissuelist"); context.addServlet( new ServletHolder(getPaginatedAssetIssueListServlet), diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java index b1ac1febd26..bde4acf2a03 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -42,7 +42,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } private void fillResponse(long offset, long limit, boolean visible, HttpServletResponse response) - throws IOException { + throws IOException { GrpcAPI.WitnessList reply = wallet.getPaginatedNowWitnessList(offset, limit); if (reply != null) { response.getWriter().println(JsonFormat.printToString(reply, visible)); diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java index c0333b52025..315d70df8d6 100755 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/RpcApiServiceOnSolidity.java @@ -163,9 +163,10 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp } public void getPaginatedNowWitnessList(PaginatedMessage request, - StreamObserver responseObserver) { + StreamObserver responseObserver) { walletOnSolidity.futureGet( - () -> rpcApiService.getWalletSolidityApi().getPaginatedNowWitnessList(request, responseObserver)); + () -> rpcApiService.getWalletSolidityApi() + .getPaginatedNowWitnessList(request, responseObserver)); } @Override diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java index 4ba859aee0f..8d5e0715ecf 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java @@ -12,14 +12,14 @@ @Component @Slf4j(topic = "API") public class GetPaginatedNowWitnessListOnSolidityServlet extends GetPaginatedNowWitnessListServlet { - @Autowired - private WalletOnSolidity walletOnSolidity; + @Autowired + private WalletOnSolidity walletOnSolidity; - protected void doGet(HttpServletRequest request, HttpServletResponse response) { - walletOnSolidity.futureGet(() -> super.doGet(request, response)); - } + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + walletOnSolidity.futureGet(() -> super.doGet(request, response)); + } - protected void doPost(HttpServletRequest request, HttpServletResponse response) { - walletOnSolidity.futureGet(() -> super.doPost(request, response)); - } + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + walletOnSolidity.futureGet(() -> super.doPost(request, response)); + } } diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 5eccfb17dc1..7e570865787 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -860,16 +860,16 @@ public void testGetDelegatedResourceV2() { public void testGetPaginatedNowWitnessList_CornerCase() { GrpcAPI.WitnessList witnessList = wallet.getPaginatedNowWitnessList(-100, 0); Assert.assertTrue("Should return an empty witness list when offset is negative", - witnessList == null); + witnessList == null); witnessList = wallet.getPaginatedNowWitnessList(100, 0); Assert.assertTrue("Should return an empty witness list when limit is 0", - witnessList == null); + witnessList == null); String fakeWitnessAddressPrefix = "fake_witness"; int fakeNumberOfWitnesses = 1000 + 10; // Mock additional witnesses with vote counts greater than 1000 - for(int i = 0; i < fakeNumberOfWitnesses; i++) { + for (int i = 0; i < fakeNumberOfWitnesses; i++) { saveWitnessWith(fakeWitnessAddressPrefix + i, 200); } @@ -879,8 +879,9 @@ public void testGetPaginatedNowWitnessList_CornerCase() { witnessList.getWitnessesCount() == 1000); // clean up, delete the fake witnesses - for(int i = 0; i < fakeNumberOfWitnesses; i++) { - chainBaseManager.getWitnessStore().delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore() + .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); } } @@ -899,12 +900,13 @@ public void testGetPaginatedNowWitnessList() { String fakeWitnessAddressPrefix = "fake_witness_address_for_paged_now_witness_list"; int fakeNumberOfWitnesses = 10; // Mock additional witnesses with vote counts greater than the maximum - for(int i = 0; i < fakeNumberOfWitnesses; i++) { + for (int i = 0; i < fakeNumberOfWitnesses; i++) { saveWitnessWith(fakeWitnessAddressPrefix + i, maxVoteCount + 1000000L); } // Create a VotesCapsule to simulate the votes for the fake witnesses - VotesCapsule votesCapsule = new VotesCapsule(ByteString.copyFromUtf8(ACCOUNT_ADDRESS_ONE), new ArrayList()); + VotesCapsule votesCapsule = new VotesCapsule(ByteString.copyFromUtf8(ACCOUNT_ADDRESS_ONE), + new ArrayList()); votesCapsule.addOldVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 0), 100L); votesCapsule.addOldVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 1), 50L); votesCapsule.addNewVotes(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + 2), 200L); @@ -917,14 +919,18 @@ public void testGetPaginatedNowWitnessList() { Assert.assertTrue("Witness list should contain 10 witnesses", witnessList2.getWitnessesCount() == 10); // Check the first witness should have the maximum vote count - Assert.assertEquals("The first witness should have the maximum vote count",fakeWitnessAddressPrefix + 3, witnessList2.getWitnesses(0).getAddress().toStringUtf8()); - Assert.assertEquals( maxVoteCount + 1000300L, witnessList2.getWitnesses(0).getVoteCount()); + Assert.assertEquals("The first witness should have the maximum vote count", + fakeWitnessAddressPrefix + 3, witnessList2.getWitnesses(0).getAddress().toStringUtf8()); + Assert.assertEquals(maxVoteCount + 1000300L, witnessList2.getWitnesses(0).getVoteCount()); // Check the second witness should have the second maximum vote count - Assert.assertEquals("The second witness", fakeWitnessAddressPrefix + 2, witnessList2.getWitnesses(1).getAddress().toStringUtf8()); - Assert.assertEquals( maxVoteCount + 1000200L, witnessList2.getWitnesses(1).getVoteCount()); + Assert.assertEquals("The second witness", fakeWitnessAddressPrefix + 2, + witnessList2.getWitnesses(1).getAddress().toStringUtf8()); + Assert.assertEquals(maxVoteCount + 1000200L, witnessList2.getWitnesses(1).getVoteCount()); // Check the last witness should have the least vote count - Assert.assertEquals("The tenth witness", fakeWitnessAddressPrefix + 0, witnessList2.getWitnesses(9).getAddress().toStringUtf8()); - Assert.assertEquals( maxVoteCount + 1000000L - 100L, witnessList2.getWitnesses(9).getVoteCount()); + Assert.assertEquals("The tenth witness", fakeWitnessAddressPrefix + 0, + witnessList2.getWitnesses(9).getAddress().toStringUtf8()); + Assert.assertEquals(maxVoteCount + 1000000L - 100L, + witnessList2.getWitnesses(9).getVoteCount()); logger.info("after paged"); @@ -932,23 +938,26 @@ public void testGetPaginatedNowWitnessList() { // Check the witness list should remain unchanged after paged request for (Protocol.Witness witness : witnessList3.getWitnessesList()) { if (witness.getVoteCount() > maxVoteCount) { - Assert.assertTrue("Check the witness list should remain unchanged after paged request", witness.getVoteCount() == maxVoteCount + 1000000L); + Assert.assertTrue("Check the witness list should remain unchanged after paged request", + witness.getVoteCount() == maxVoteCount + 1000000L); } } // clean up, delete the fake witnesses - for(int i = 0; i < fakeNumberOfWitnesses; i++) { - chainBaseManager.getWitnessStore().delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore() + .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); } chainBaseManager.getVotesStore().delete(votesCapsule.createDbKey()); - Assert.assertTrue("Clean up the mocked witness data",wallet.getWitnessList().getWitnessesCount() == witnessList.getWitnessesCount()); + Assert.assertTrue("Clean up the mocked witness data", + wallet.getWitnessList().getWitnessesCount() == witnessList.getWitnessesCount()); } public void saveWitnessWith(String witnessAddress, long voteCount) { WitnessCapsule witness = new WitnessCapsule( Protocol.Witness.newBuilder() - .setAddress(ByteString.copyFromUtf8(witnessAddress)) // Convert String to ByteString + .setAddress(ByteString.copyFromUtf8(witnessAddress)) // Convert String to ByteString .setVoteCount(voteCount).build()); chainBaseManager.getWitnessStore().put(witness.getAddress().toByteArray(), witness); } From 5f9162816f1734f6be25053c17cd7a5b8838fd72 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Mon, 30 Jun 2025 16:19:16 +0800 Subject: [PATCH 08/22] sort witness using the same logic as in maintence --- .../main/java/org/tron/core/store/WitnessStore.java | 2 +- framework/src/main/java/org/tron/core/Wallet.java | 10 +++++++--- .../test/java/org/tron/core/db/WitnessStoreTest.java | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/store/WitnessStore.java b/chainbase/src/main/java/org/tron/core/store/WitnessStore.java index 9f444d3333d..9c30df195af 100644 --- a/chainbase/src/main/java/org/tron/core/store/WitnessStore.java +++ b/chainbase/src/main/java/org/tron/core/store/WitnessStore.java @@ -55,7 +55,7 @@ public List getWitnessStandby(boolean isSortOpt) { return ret; } - public void sortWitnesses(List witnesses, boolean isSortOpt) { + public static void sortWitnesses(List witnesses, boolean isSortOpt) { witnesses.sort(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed() .thenComparing(isSortOpt ? Comparator.comparing(WitnessCapsule::createReadableString).reversed() diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index d278cba1282..f5d3d242a53 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -207,6 +207,7 @@ import org.tron.core.store.MarketPairToPriceStore; import org.tron.core.store.StoreFactory; import org.tron.core.store.VotesStore; +import org.tron.core.store.WitnessStore; import org.tron.core.utils.TransactionUtil; import org.tron.core.vm.program.Program; import org.tron.core.zen.ShieldedTRC20ParametersBuilder; @@ -797,9 +798,12 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { witnessCapsule.setVoteCount(witnessCapsule.getVoteCount() + voteCount); }); + // Use the same logic as in the Maintenance period + WitnessStore.sortWitnesses(witnessCapsuleList, + chainBaseManager.getDynamicPropertiesStore().allowWitnessSortOptimization()); + // Return the witness with the highest vote counts at first and skip the offset with limit List sortedWitnessList = witnessCapsuleList.stream() - .sorted(Comparator.comparingLong(WitnessCapsule::getVoteCount).reversed()) .skip(offset) .limit(limit) .collect(Collectors.toList()); @@ -837,13 +841,13 @@ private Map countVote(VotesStore votesStore) { ByteString voteAddress = vote.getVoteAddress(); long voteCount = vote.getVoteCount(); countWitness.put(voteAddress, - countWitness.containsKey(voteAddress) ? countWitness.get(voteAddress) : 0 - voteCount); + countWitness.getOrDefault(voteAddress, 0L) - voteCount); }); votes.getNewVotes().forEach(vote -> { ByteString voteAddress = vote.getVoteAddress(); long voteCount = vote.getVoteCount(); countWitness.put(voteAddress, - countWitness.containsKey(voteAddress) ? countWitness.get(voteAddress) : 0 + voteCount); + countWitness.getOrDefault(voteAddress, 0L) + voteCount); }); } return countWitness; diff --git a/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java b/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java index d141a5fd790..521d048f23e 100755 --- a/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/WitnessStoreTest.java @@ -62,12 +62,12 @@ public void testSortWitness() { List witnessAddress = allWitnesses.stream().map(WitnessCapsule::getAddress) .collect(Collectors.toList()); this.witnessStore.sortWitness(witnessAddress, false); - this.witnessStore.sortWitnesses(allWitnesses, false); + WitnessStore.sortWitnesses(allWitnesses, false); Assert.assertEquals(witnessAddress, allWitnesses.stream().map(WitnessCapsule::getAddress) .collect(Collectors.toList())); List pre = new ArrayList<>(witnessAddress); this.witnessStore.sortWitness(witnessAddress, true); - this.witnessStore.sortWitnesses(allWitnesses, true); + WitnessStore.sortWitnesses(allWitnesses, true); Assert.assertEquals(witnessAddress, allWitnesses.stream().map(WitnessCapsule::getAddress) .collect(Collectors.toList())); Assert.assertNotEquals(pre, witnessAddress); From 5b3c1178b18b41ffbade2752f3ab20c64d9db6ab Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Tue, 1 Jul 2025 12:17:14 +0800 Subject: [PATCH 09/22] add rpc, http test --- .../core/services/RpcApiServicesTest.java | 8 +++ .../org/tron/core/services/WalletApiTest.java | 63 ++++++++++++++++++- .../core/services/http/HttpServletTest.java | 4 ++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index 7179215393f..94bec4659ad 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -232,6 +232,14 @@ public void testListWitnesses() { assertNotNull(blockingStubPBFT.listWitnesses(message)); } + @Test + public void testGetPaginatedNowWitnessList() { + PaginatedMessage paginatedMessage = PaginatedMessage.newBuilder() + .setOffset(0).setLimit(5).build(); + assertNotNull(blockingStubFull.getPaginatedNowWitnessList(paginatedMessage)); + assertNotNull(blockingStubSolidity.getPaginatedNowWitnessList(paginatedMessage)); + } + @Test public void testGetAssetIssueList() { EmptyMessage message = EmptyMessage.newBuilder().build(); diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index 8890d4bfd9e..d56a02dc894 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -14,6 +14,14 @@ import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.stream.Collectors; +import org.tron.common.utils.JsonUtilTest; import org.tron.common.utils.PublicMethod; import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; @@ -34,8 +42,18 @@ public class WalletApiTest { public static void init() throws IOException { Args.setParam(new String[]{ "-d", temporaryFolder.newFolder().toString(), "--p2p-disable", "true"}, Constant.TEST_CONF); - Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort()); + int rpcPort = PublicMethod.chooseRandomPort(); + Args.getInstance().setRpcPort(rpcPort); Args.getInstance().setRpcEnable(true); + + // http request for /wallet/xx on FullNode + Args.getInstance().setFullNodeHttpPort(rpcPort + 1); + Args.getInstance().setFullNodeHttpEnable(true); + + // http request for /walletsolidity/xx on FullNode + Args.getInstance().setSolidityHttpPort(rpcPort + 2); + Args.getInstance().setSolidityNodeHttpEnable(true); + context = new TronApplicationContext(DefaultConfig.class); appT = ApplicationFactory.create(context); appT.startup(); @@ -53,6 +71,49 @@ public void listNodesTest() { .getNodesList().isEmpty()); } + @Test + public void getPaginatedNowWitnessListTest() { + try { + String url = "http://127.0.0.1:" + Args.getInstance().getFullNodeHttpPort() + + "/wallet/getpaginatednowwitnesslist"; + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + + // Create JSON payload + String jsonPayload = "{\"offset\": 0, \"limit\": 1000, \"visible\": true}"; + connection.getOutputStream().write(jsonPayload.getBytes()); + connection.getOutputStream().flush(); + + Assert.assertEquals("HTTP response code should be 200", 200, connection.getResponseCode()); + } catch (Exception e) { + Assert.fail("HTTP request failed: " + e.getMessage()); + } + } + + @Test + public void getPaginatedNowWitnessListSolidityTest() { + try { + String url = "http://127.0.0.1:" + Args.getInstance().getSolidityHttpPort() + + "/walletsolidity/getpaginatednowwitnesslist"; + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + + // Create JSON payload + String jsonPayload = "{\"offset\": 0, \"limit\": 1000, \"visible\": true}"; + connection.getOutputStream().write(jsonPayload.getBytes()); + connection.getOutputStream().flush(); + + Assert.assertEquals("HTTP response code should be 200", 200, connection.getResponseCode()); + } catch (Exception e) { + Assert.fail("HTTP solidity request failed: " + e.getMessage()); + } + } + + @After public void destroy() { Args.clearParam(); diff --git a/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java b/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java index dfd4c569e3e..03cf11f39a1 100644 --- a/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/HttpServletTest.java @@ -107,6 +107,7 @@ public class HttpServletTest { private ListNodesServlet listNodesServlet; private ListProposalsServlet listProposalsServlet; private ListWitnessesServlet listWitnessesServlet; + private GetPaginatedNowWitnessListServlet getPaginatedNowWitnessListServlet; private MarketCancelOrderServlet marketCancelOrderServlet; private MarketSellAssetServlet marketSellAssetServlet; private MetricsServlet metricsServlet; @@ -244,6 +245,7 @@ public void setUp() { listNodesServlet = new ListNodesServlet(); listProposalsServlet = new ListProposalsServlet(); listWitnessesServlet = new ListWitnessesServlet(); + getPaginatedNowWitnessListServlet = new GetPaginatedNowWitnessListServlet(); marketCancelOrderServlet = new MarketCancelOrderServlet(); marketSellAssetServlet = new MarketSellAssetServlet(); metricsServlet = new MetricsServlet(); @@ -367,6 +369,7 @@ public void doGetTest() { listNodesServlet.doGet(request, response); listProposalsServlet.doGet(request, response); listWitnessesServlet.doGet(request, response); + getPaginatedNowWitnessListServlet.doGet(request, response); marketCancelOrderServlet.doGet(request, response); marketSellAssetServlet.doGet(request, response); metricsServlet.doGet(request, response); @@ -499,6 +502,7 @@ public void doPostTest() { listNodesServlet.doPost(request, response); listProposalsServlet.doPost(request, response); listWitnessesServlet.doPost(request, response); + getPaginatedNowWitnessListServlet.doPost(request, response); marketCancelOrderServlet.doPost(request, response); marketSellAssetServlet.doPost(request, response); participateAssetIssueServlet.doPost(request, response); From 9f24507d2057e71c23263a2d4bd7eff8efe9284c Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Tue, 1 Jul 2025 12:43:50 +0800 Subject: [PATCH 10/22] update comments --- .../src/main/java/org/tron/core/Wallet.java | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index f5d3d242a53..4090dfcc360 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -775,7 +775,6 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { if (limit <= 0 || offset < 0) { return null; } - // To control the maximum response size less than 40KB. if (limit > WITNESS_LIST_LIMIT_MAX) { limit = WITNESS_LIST_LIMIT_MAX; } @@ -790,25 +789,21 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { // Count the vote changes for each witness in the current epoch, it is maybe negative. Map countWitness = countVote(votesStore); - // Iterate through the witness list and apply the vote changes, it will be the realtime vote count. + // Iterate through the witness list to apply vote changes and calculate the real-time vote count witnessCapsuleList.forEach((witnessCapsule) -> { long voteCount = countWitness.getOrDefault(witnessCapsule.getAddress(), 0L); - // since the above chainBaseManager.getWitnessStore().getAllWitnesses() - // each time return a copy of the witness list, thus this update will not affect the original database list. witnessCapsule.setVoteCount(witnessCapsule.getVoteCount() + voteCount); }); - // Use the same logic as in the Maintenance period + // Use the same sorting logic as in the Maintenance period WitnessStore.sortWitnesses(witnessCapsuleList, chainBaseManager.getDynamicPropertiesStore().allowWitnessSortOptimization()); - // Return the witness with the highest vote counts at first and skip the offset with limit List sortedWitnessList = witnessCapsuleList.stream() .skip(offset) .limit(limit) .collect(Collectors.toList()); - // Pack the sorted WitnessCapsule list into a WitnessList object WitnessList.Builder builder = WitnessList.newBuilder(); sortedWitnessList.forEach(witnessCapsule -> builder.addWitnesses(witnessCapsule.getInstance())); @@ -816,15 +811,24 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { return builder.build(); } - /* - countVote(VotesStore votesStore): Counts the vote count changes for witnesses in the current epoch, - If the voters change their votes in the current epoch, - the vote count for last epoch witness will be negative, while the new vote count will be positive. - For example: - Account A votes for witness W1 in the last epoch with 100 votes, - in the current epoch, A change the votes for W2 with 60 votes W3 with 80 votes, - then the vote count for W1 will be -100, while the vote count for W2 will be 60, W3 will be 80. - */ + /** + * Counts vote changes for witnesses in the current epoch. + * + * Vote count changes are tracked as follows: + * - Negative values for votes removed from previous witness in the last epoch + * - Positive values for votes added to new witness in the current epoch + * + * Example: + * an Account X had 100 votes for witness W1 in the previous epoch. + * In the current epoch, X changes votes to: + * - W2: 60 votes + * - W3: 80 votes + * + * Resulting vote changes: + * - W1: -100 (votes removed) + * - W2: +60 (new votes) + * - W3: +80 (new votes) + */ private Map countVote(VotesStore votesStore) { // Initialize a result map to store vote changes for each witness Map countWitness = Maps.newHashMap(); @@ -836,7 +840,11 @@ private Map countVote(VotesStore votesStore) { Entry next = dbIterator.next(); VotesCapsule votes = next.getValue(); - // For each voter, we iterate the old votes and new votes to calculate the vote count changes + /** + * VotesCapsule contains two lists: + * - Old votes: Last votes from the previous epoch, updated in maintenance period + * - New votes: Latest votes in current epoch, updated after each vote transaction + */ votes.getOldVotes().forEach(vote -> { ByteString voteAddress = vote.getVoteAddress(); long voteCount = vote.getVoteCount(); From e9795877a91cf58d8b976b28924013ffaa35bd66 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Tue, 1 Jul 2025 15:11:52 +0800 Subject: [PATCH 11/22] delete unused code --- .../org/tron/core/services/WalletApiTest.java | 63 +------------------ 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index d56a02dc894..8890d4bfd9e 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -14,14 +14,6 @@ import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.stream.Collectors; -import org.tron.common.utils.JsonUtilTest; import org.tron.common.utils.PublicMethod; import org.tron.core.Constant; import org.tron.core.config.DefaultConfig; @@ -42,18 +34,8 @@ public class WalletApiTest { public static void init() throws IOException { Args.setParam(new String[]{ "-d", temporaryFolder.newFolder().toString(), "--p2p-disable", "true"}, Constant.TEST_CONF); - int rpcPort = PublicMethod.chooseRandomPort(); - Args.getInstance().setRpcPort(rpcPort); + Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort()); Args.getInstance().setRpcEnable(true); - - // http request for /wallet/xx on FullNode - Args.getInstance().setFullNodeHttpPort(rpcPort + 1); - Args.getInstance().setFullNodeHttpEnable(true); - - // http request for /walletsolidity/xx on FullNode - Args.getInstance().setSolidityHttpPort(rpcPort + 2); - Args.getInstance().setSolidityNodeHttpEnable(true); - context = new TronApplicationContext(DefaultConfig.class); appT = ApplicationFactory.create(context); appT.startup(); @@ -71,49 +53,6 @@ public void listNodesTest() { .getNodesList().isEmpty()); } - @Test - public void getPaginatedNowWitnessListTest() { - try { - String url = "http://127.0.0.1:" + Args.getInstance().getFullNodeHttpPort() + - "/wallet/getpaginatednowwitnesslist"; - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setDoOutput(true); - - // Create JSON payload - String jsonPayload = "{\"offset\": 0, \"limit\": 1000, \"visible\": true}"; - connection.getOutputStream().write(jsonPayload.getBytes()); - connection.getOutputStream().flush(); - - Assert.assertEquals("HTTP response code should be 200", 200, connection.getResponseCode()); - } catch (Exception e) { - Assert.fail("HTTP request failed: " + e.getMessage()); - } - } - - @Test - public void getPaginatedNowWitnessListSolidityTest() { - try { - String url = "http://127.0.0.1:" + Args.getInstance().getSolidityHttpPort() + - "/walletsolidity/getpaginatednowwitnesslist"; - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setDoOutput(true); - - // Create JSON payload - String jsonPayload = "{\"offset\": 0, \"limit\": 1000, \"visible\": true}"; - connection.getOutputStream().write(jsonPayload.getBytes()); - connection.getOutputStream().flush(); - - Assert.assertEquals("HTTP response code should be 200", 200, connection.getResponseCode()); - } catch (Exception e) { - Assert.fail("HTTP solidity request failed: " + e.getMessage()); - } - } - - @After public void destroy() { Args.clearParam(); From 2bad6bd156c7f18c1000abe902a636356b28f59d Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Thu, 3 Jul 2025 16:55:17 +0800 Subject: [PATCH 12/22] fix review --- common/src/main/java/org/tron/core/config/Parameter.java | 2 +- framework/src/main/java/org/tron/core/Wallet.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/org/tron/core/config/Parameter.java b/common/src/main/java/org/tron/core/config/Parameter.java index 18867347efd..ede67abe4dc 100644 --- a/common/src/main/java/org/tron/core/config/Parameter.java +++ b/common/src/main/java/org/tron/core/config/Parameter.java @@ -107,7 +107,7 @@ public class DatabaseConstants { public static final int PROPOSAL_COUNT_LIMIT_MAX = 1000; public static final int EXCHANGE_COUNT_LIMIT_MAX = 1000; public static final int MARKET_COUNT_LIMIT_MAX = 1000; - public static final int WITNESS_LIST_LIMIT_MAX = 1000; + public static final int WITNESS_COUNT_LIMIT_MAX = 1000; } public class AdaptiveResourceLimitConstants { diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 4090dfcc360..18b49aa3884 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -30,7 +30,7 @@ import static org.tron.core.config.Parameter.DatabaseConstants.EXCHANGE_COUNT_LIMIT_MAX; import static org.tron.core.config.Parameter.DatabaseConstants.MARKET_COUNT_LIMIT_MAX; import static org.tron.core.config.Parameter.DatabaseConstants.PROPOSAL_COUNT_LIMIT_MAX; -import static org.tron.core.config.Parameter.DatabaseConstants.WITNESS_LIST_LIMIT_MAX; +import static org.tron.core.config.Parameter.DatabaseConstants.WITNESS_COUNT_LIMIT_MAX; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseEnergyFee; import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.EARLIEST_STR; import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.FINALIZED_STR; @@ -62,7 +62,6 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import java.util.Comparator; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; @@ -775,8 +774,8 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { if (limit <= 0 || offset < 0) { return null; } - if (limit > WITNESS_LIST_LIMIT_MAX) { - limit = WITNESS_LIST_LIMIT_MAX; + if (limit > WITNESS_COUNT_LIMIT_MAX) { + limit = WITNESS_COUNT_LIMIT_MAX; } // Get all witnesses from the store, it contains the final vote count at the end of the last epoch. From 9d81cea806defa6614108db86e079474740fc3da Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Thu, 10 Jul 2025 17:30:58 +0800 Subject: [PATCH 13/22] fix style --- framework/src/main/java/org/tron/core/Wallet.java | 6 +++--- .../services/http/GetPaginatedNowWitnessListServlet.java | 8 +++----- .../http/solidity/HttpApiOnSolidityService.java | 4 +--- .../java/org/tron/core/zksnark/ShieldedReceiveTest.java | 3 ++- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 229615a8ba2..75627373d57 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -45,8 +45,8 @@ import com.google.common.collect.DiscreteDomain; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.google.common.collect.Range; import com.google.common.collect.Maps; +import com.google.common.collect.Range; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolStringList; @@ -164,9 +164,9 @@ import org.tron.core.capsule.TransactionInfoCapsule; import org.tron.core.capsule.TransactionResultCapsule; import org.tron.core.capsule.TransactionRetCapsule; +import org.tron.core.capsule.VotesCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.capsule.utils.MarketUtils; -import org.tron.core.capsule.VotesCapsule; import org.tron.core.config.args.Args; import org.tron.core.db.BandwidthProcessor; import org.tron.core.db.BlockIndexStore; @@ -778,7 +778,7 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { limit = WITNESS_COUNT_LIMIT_MAX; } - // Get all witnesses from the store, it contains the final vote count at the end of the last epoch. + // It contains the final vote count at the end of the last epoch. List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); if (offset >= witnessCapsuleList.size()) { return null; diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java index bde4acf2a03..40f0508e4e7 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -1,15 +1,13 @@ package org.tron.core.services.http; +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI; -import org.tron.api.GrpcAPI.WitnessList; import org.tron.core.Wallet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - // Get the paged list of witnesses info with realtime vote counts @Component diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java index 5586ceea5af..f69597959f8 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/solidity/HttpApiOnSolidityService.java @@ -3,8 +3,6 @@ import java.util.EnumSet; import javax.servlet.DispatcherType; import lombok.extern.slf4j.Slf4j; -import org.eclipse.jetty.server.ConnectionLimit; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -46,6 +44,7 @@ import org.tron.core.services.interfaceOnSolidity.http.GetNodeInfoOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetNowBlockOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedAssetIssueListOnSolidityServlet; +import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedNowWitnessListOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetRewardOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetTransactionCountByBlockNumOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.GetTransactionInfoByBlockNumOnSolidityServlet; @@ -59,7 +58,6 @@ import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByIvkOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.ScanShieldedTRC20NotesByOvkOnSolidityServlet; import org.tron.core.services.interfaceOnSolidity.http.TriggerConstantContractOnSolidityServlet; -import org.tron.core.services.interfaceOnSolidity.http.GetPaginatedNowWitnessListOnSolidityServlet; @Slf4j(topic = "API") public class HttpApiOnSolidityService extends HttpService { diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index 2a7545f7a9b..259f72067d0 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -320,7 +320,8 @@ public void testBroadcastBeforeAllowZksnark() } /* - * generate spendproof, dataToBeSigned, outputproof example dynamically according to the params file + * generate spendproof, dataToBeSigned, outputproof + * example dynamically according to the params file */ public String[] generateSpendAndOutputParams() throws ZksnarkException, BadItemException { librustzcashInitZksnarkParams(); From 4307843bff1ba82ba52fe18492b38b9a87beca97 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Fri, 11 Jul 2025 09:15:44 +0800 Subject: [PATCH 14/22] fix style --- .../http/GetPaginatedNowWitnessListOnSolidityServlet.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java index 8d5e0715ecf..4578393ec76 100644 --- a/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java +++ b/framework/src/main/java/org/tron/core/services/interfaceOnSolidity/http/GetPaginatedNowWitnessListOnSolidityServlet.java @@ -1,14 +1,13 @@ package org.tron.core.services.interfaceOnSolidity.http; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.services.http.GetPaginatedNowWitnessListServlet; import org.tron.core.services.interfaceOnSolidity.WalletOnSolidity; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - @Component @Slf4j(topic = "API") public class GetPaginatedNowWitnessListOnSolidityServlet extends GetPaginatedNowWitnessListServlet { From b2766041398cda4ce40d7575442f3d120cc6ce7d Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Tue, 15 Jul 2025 10:16:25 +0800 Subject: [PATCH 15/22] fix check style --- framework/src/main/java/org/tron/core/Wallet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 75627373d57..9062a2647ff 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -789,7 +789,7 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) { Map countWitness = countVote(votesStore); // Iterate through the witness list to apply vote changes and calculate the real-time vote count - witnessCapsuleList.forEach((witnessCapsule) -> { + witnessCapsuleList.forEach(witnessCapsule -> { long voteCount = countWitness.getOrDefault(witnessCapsule.getAddress(), 0L); witnessCapsule.setVoteCount(witnessCapsule.getVoteCount() + voteCount); }); From 8e1629c6771d84e7b5b6bc79864502e3f4169c8e Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Fri, 8 Aug 2025 13:52:26 +0800 Subject: [PATCH 16/22] add check maintenance period and throw error --- .../MaintenanceClearingException.java | 20 ++++++ .../src/main/java/org/tron/core/Wallet.java | 14 ++++- .../org/tron/core/services/RpcApiService.java | 17 ++++-- .../GetPaginatedNowWitnessListServlet.java | 3 +- .../test/java/org/tron/core/WalletTest.java | 61 ++++++++++++------- 5 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java diff --git a/common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java b/common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java new file mode 100644 index 00000000000..dd439878d00 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java @@ -0,0 +1,20 @@ +package org.tron.core.exception; + +/** + * Maintenance clearing exception - thrown when system is in maintenance clearing state + * Please try again later + */ +public class MaintenanceClearingException extends TronException { + + public MaintenanceClearingException() { + super(); + } + + public MaintenanceClearingException(String message) { + super(message); + } + + public MaintenanceClearingException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 9062a2647ff..1570a075376 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -182,6 +182,7 @@ import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.ItemNotFoundException; import org.tron.core.exception.JsonRpcInvalidParamsException; +import org.tron.core.exception.MaintenanceClearingException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.PermissionException; import org.tron.core.exception.SignatureFormatException; @@ -770,14 +771,23 @@ public WitnessList getWitnessList() { return builder.build(); } - public WitnessList getPaginatedNowWitnessList(long offset, long limit) { + public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws MaintenanceClearingException { if (limit <= 0 || offset < 0) { return null; } if (limit > WITNESS_COUNT_LIMIT_MAX) { limit = WITNESS_COUNT_LIMIT_MAX; } - + + /* + In the maintenance period, the VoteStores will be cleared. + To avoid the race condition of VoteStores deleted but Witness vote counts not updated, return retry error. + */ + if (chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1) { + long maintenanceLogicTime = chainBaseManager.getDynamicPropertiesStore().getMaintenanceSkipSlots() * BLOCK_PRODUCED_INTERVAL / 1000; + String message = "Maintenance clearing, please try again later after " + maintenanceLogicTime + " seconds."; + throw new MaintenanceClearingException(message); + } // It contains the final vote count at the end of the last epoch. List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); if (offset >= witnessCapsuleList.size()) { diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index 7b768f79ac2..c253736a3cd 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -89,6 +89,7 @@ import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.ItemNotFoundException; +import org.tron.core.exception.MaintenanceClearingException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.StoreException; import org.tron.core.exception.VMIllegalException; @@ -399,8 +400,12 @@ public void listWitnesses(EmptyMessage request, StreamObserver resp @Override public void getPaginatedNowWitnessList(PaginatedMessage request, StreamObserver responseObserver) { - responseObserver.onNext( - wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + try { + responseObserver.onNext( + wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + } catch (MaintenanceClearingException e) { + responseObserver.onError(getRunTimeException(e)); + } responseObserver.onCompleted(); } @@ -1883,8 +1888,12 @@ public void listWitnesses(EmptyMessage request, @Override public void getPaginatedNowWitnessList(PaginatedMessage request, StreamObserver responseObserver) { - responseObserver.onNext( - wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + try { + responseObserver.onNext( + wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); + } catch (MaintenanceClearingException e) { + responseObserver.onError(getRunTimeException(e)); + } responseObserver.onCompleted(); } diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java index 40f0508e4e7..bc51605706b 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI; import org.tron.core.Wallet; +import org.tron.core.exception.MaintenanceClearingException; // Get the paged list of witnesses info with realtime vote counts @Component @@ -40,7 +41,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } private void fillResponse(long offset, long limit, boolean visible, HttpServletResponse response) - throws IOException { + throws IOException, MaintenanceClearingException { GrpcAPI.WitnessList reply = wallet.getPaginatedNowWitnessList(offset, limit); if (reply != null) { response.getWriter().println(JsonFormat.printToString(reply, visible)); diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 7e570865787..23914ef13e8 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -74,6 +74,7 @@ import org.tron.core.config.args.Args; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; +import org.tron.core.exception.MaintenanceClearingException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.utils.ProposalUtil.ProposalType; @@ -858,30 +859,37 @@ public void testGetDelegatedResourceV2() { @Test public void testGetPaginatedNowWitnessList_CornerCase() { - GrpcAPI.WitnessList witnessList = wallet.getPaginatedNowWitnessList(-100, 0); - Assert.assertTrue("Should return an empty witness list when offset is negative", - witnessList == null); - - witnessList = wallet.getPaginatedNowWitnessList(100, 0); - Assert.assertTrue("Should return an empty witness list when limit is 0", - witnessList == null); - - String fakeWitnessAddressPrefix = "fake_witness"; - int fakeNumberOfWitnesses = 1000 + 10; - // Mock additional witnesses with vote counts greater than 1000 - for (int i = 0; i < fakeNumberOfWitnesses; i++) { - saveWitnessWith(fakeWitnessAddressPrefix + i, 200); - } + try { + // To avoid throw MaintenanceClearingException + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); + + GrpcAPI.WitnessList witnessList = wallet.getPaginatedNowWitnessList(-100, 0); + Assert.assertTrue("Should return an empty witness list when offset is negative", + witnessList == null); + + witnessList = wallet.getPaginatedNowWitnessList(100, 0); + Assert.assertTrue("Should return an empty witness list when limit is 0", + witnessList == null); + + String fakeWitnessAddressPrefix = "fake_witness"; + int fakeNumberOfWitnesses = 1000 + 10; + // Mock additional witnesses with vote counts greater than 1000 + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + saveWitnessWith(fakeWitnessAddressPrefix + i, 200); + } - witnessList = wallet.getPaginatedNowWitnessList(0, 1000000); - // Check the returned witness list should contain 1000 witnesses with descending vote count - Assert.assertTrue("Witness list should contain 1000 witnesses", - witnessList.getWitnessesCount() == 1000); + witnessList = wallet.getPaginatedNowWitnessList(0, 1000000); + // Check the returned witness list should contain 1000 witnesses with descending vote count + Assert.assertTrue("Witness list should contain 1000 witnesses", + witnessList.getWitnessesCount() == 1000); - // clean up, delete the fake witnesses - for (int i = 0; i < fakeNumberOfWitnesses; i++) { - chainBaseManager.getWitnessStore() - .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + // clean up, delete the fake witnesses + for (int i = 0; i < fakeNumberOfWitnesses; i++) { + chainBaseManager.getWitnessStore() + .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); + } + } catch (MaintenanceClearingException e) { + Assert.fail(e.getMessage()); } } @@ -914,7 +922,14 @@ public void testGetPaginatedNowWitnessList() { chainBaseManager.getVotesStore().put(votesCapsule.createDbKey(), votesCapsule); logger.info("now request paginated witness list with 0 offset and 10 limit:"); - GrpcAPI.WitnessList witnessList2 = wallet.getPaginatedNowWitnessList(0, 10); + GrpcAPI.WitnessList witnessList2 = null; + try { + // To avoid throw MaintenanceClearingException + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); + witnessList2 = wallet.getPaginatedNowWitnessList(0, 10); + } catch (MaintenanceClearingException e) { + Assert.fail(e.getMessage()); + } // Check the returned witness list should contain 10 witnesses with descending vote count Assert.assertTrue("Witness list should contain 10 witnesses", witnessList2.getWitnessesCount() == 10); From 104042922c9e185db9c0f6c34fa8f6aa1338650d Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Fri, 8 Aug 2025 14:36:53 +0800 Subject: [PATCH 17/22] add test case for throw error --- .../src/test/java/org/tron/core/WalletTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 23914ef13e8..0a797612f5a 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -857,6 +857,20 @@ public void testGetDelegatedResourceV2() { } } + @Test + public void testGetPaginatedNowWitnessList_Error() { + try { + // To avoid throw MaintenanceClearingException + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(1); + wallet.getPaginatedNowWitnessList(0, 10); + Assert.fail("Should throw error when in maintenance period"); + } catch (Exception e) { + Assert.assertTrue("Should throw MaintenanceClearingException", + e instanceof MaintenanceClearingException); + } + dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); + } + @Test public void testGetPaginatedNowWitnessList_CornerCase() { try { From 109d71901214d3262ae6f9e5c9ab45671997eb41 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Thu, 14 Aug 2025 12:12:15 +0800 Subject: [PATCH 18/22] fix review --- .../MaintenanceClearingException.java | 20 ------------------- .../MaintenanceUnavailableException.java | 20 +++++++++++++++++++ .../src/main/java/org/tron/core/Wallet.java | 10 +++++----- .../org/tron/core/services/RpcApiService.java | 6 +++--- .../GetPaginatedNowWitnessListServlet.java | 4 ++-- .../test/java/org/tron/core/WalletTest.java | 9 ++++----- 6 files changed, 34 insertions(+), 35 deletions(-) delete mode 100644 common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java create mode 100644 common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java diff --git a/common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java b/common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java deleted file mode 100644 index dd439878d00..00000000000 --- a/common/src/main/java/org/tron/core/exception/MaintenanceClearingException.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.tron.core.exception; - -/** - * Maintenance clearing exception - thrown when system is in maintenance clearing state - * Please try again later - */ -public class MaintenanceClearingException extends TronException { - - public MaintenanceClearingException() { - super(); - } - - public MaintenanceClearingException(String message) { - super(message); - } - - public MaintenanceClearingException(String message, Throwable cause) { - super(message, cause); - } -} \ No newline at end of file diff --git a/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java b/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java new file mode 100644 index 00000000000..4abf4765709 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java @@ -0,0 +1,20 @@ +package org.tron.core.exception; + +/** + * Maintenance clearing exception - thrown when system is in maintenance clearing state + * Please try again later + */ +public class MaintenanceUnavailableException extends TronException { + + public MaintenanceUnavailableException() { + super(); + } + + public MaintenanceUnavailableException(String message) { + super(message); + } + + public MaintenanceUnavailableException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 1570a075376..b86b99aeb97 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -182,7 +182,7 @@ import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.ItemNotFoundException; import org.tron.core.exception.JsonRpcInvalidParamsException; -import org.tron.core.exception.MaintenanceClearingException; +import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.PermissionException; import org.tron.core.exception.SignatureFormatException; @@ -771,7 +771,8 @@ public WitnessList getWitnessList() { return builder.build(); } - public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws MaintenanceClearingException { + public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws + MaintenanceUnavailableException { if (limit <= 0 || offset < 0) { return null; } @@ -784,9 +785,8 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws Ma To avoid the race condition of VoteStores deleted but Witness vote counts not updated, return retry error. */ if (chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1) { - long maintenanceLogicTime = chainBaseManager.getDynamicPropertiesStore().getMaintenanceSkipSlots() * BLOCK_PRODUCED_INTERVAL / 1000; - String message = "Maintenance clearing, please try again later after " + maintenanceLogicTime + " seconds."; - throw new MaintenanceClearingException(message); + String message = "Service temporarily unavailable during maintenance period. Please try again later."; + throw new MaintenanceUnavailableException(message); } // It contains the final vote count at the end of the last epoch. List witnessCapsuleList = chainBaseManager.getWitnessStore().getAllWitnesses(); diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index c253736a3cd..8b12fdc428b 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -89,7 +89,7 @@ import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.MaintenanceClearingException; +import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.StoreException; import org.tron.core.exception.VMIllegalException; @@ -403,7 +403,7 @@ public void getPaginatedNowWitnessList(PaginatedMessage request, try { responseObserver.onNext( wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); - } catch (MaintenanceClearingException e) { + } catch (MaintenanceUnavailableException e) { responseObserver.onError(getRunTimeException(e)); } responseObserver.onCompleted(); @@ -1891,7 +1891,7 @@ public void getPaginatedNowWitnessList(PaginatedMessage request, try { responseObserver.onNext( wallet.getPaginatedNowWitnessList(request.getOffset(), request.getLimit())); - } catch (MaintenanceClearingException e) { + } catch (MaintenanceUnavailableException e) { responseObserver.onError(getRunTimeException(e)); } responseObserver.onCompleted(); diff --git a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java index bc51605706b..e53ab6610ec 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetPaginatedNowWitnessListServlet.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI; import org.tron.core.Wallet; -import org.tron.core.exception.MaintenanceClearingException; +import org.tron.core.exception.MaintenanceUnavailableException; // Get the paged list of witnesses info with realtime vote counts @Component @@ -41,7 +41,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } private void fillResponse(long offset, long limit, boolean visible, HttpServletResponse response) - throws IOException, MaintenanceClearingException { + throws IOException, MaintenanceUnavailableException { GrpcAPI.WitnessList reply = wallet.getPaginatedNowWitnessList(offset, limit); if (reply != null) { response.getWriter().println(JsonFormat.printToString(reply, visible)); diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 0a797612f5a..8937267d7f1 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import javax.annotation.Resource; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -74,7 +73,7 @@ import org.tron.core.config.args.Args; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; -import org.tron.core.exception.MaintenanceClearingException; +import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.utils.ProposalUtil.ProposalType; @@ -866,7 +865,7 @@ public void testGetPaginatedNowWitnessList_Error() { Assert.fail("Should throw error when in maintenance period"); } catch (Exception e) { Assert.assertTrue("Should throw MaintenanceClearingException", - e instanceof MaintenanceClearingException); + e instanceof MaintenanceUnavailableException); } dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); } @@ -902,7 +901,7 @@ public void testGetPaginatedNowWitnessList_CornerCase() { chainBaseManager.getWitnessStore() .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); } - } catch (MaintenanceClearingException e) { + } catch (MaintenanceUnavailableException e) { Assert.fail(e.getMessage()); } } @@ -941,7 +940,7 @@ public void testGetPaginatedNowWitnessList() { // To avoid throw MaintenanceClearingException dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); witnessList2 = wallet.getPaginatedNowWitnessList(0, 10); - } catch (MaintenanceClearingException e) { + } catch (MaintenanceUnavailableException e) { Assert.fail(e.getMessage()); } // Check the returned witness list should contain 10 witnesses with descending vote count From 41c3ba01e93e22966bcfc6c85079c1796392f472 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Wed, 20 Aug 2025 10:29:11 +0800 Subject: [PATCH 19/22] update comments --- .../tron/core/exception/MaintenanceUnavailableException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java b/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java index 4abf4765709..1911f84a616 100644 --- a/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java +++ b/common/src/main/java/org/tron/core/exception/MaintenanceUnavailableException.java @@ -1,7 +1,7 @@ package org.tron.core.exception; /** - * Maintenance clearing exception - thrown when system is in maintenance clearing state + * Maintenance unavailable exception - thrown when service is in maintenance state * Please try again later */ public class MaintenanceUnavailableException extends TronException { From 8578b90f189675fac24b8a4431456c03ebb7f936 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Wed, 20 Aug 2025 11:22:01 +0800 Subject: [PATCH 20/22] fix build --- framework/src/main/java/org/tron/core/Wallet.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index df6c65a93b8..2621c7df575 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -181,7 +181,6 @@ import org.tron.core.exception.DupTransactionException; import org.tron.core.exception.HeaderNotFound; import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.JsonRpcInvalidParamsException; import org.tron.core.exception.MaintenanceUnavailableException; import org.tron.core.exception.NonUniqueObjectException; import org.tron.core.exception.PermissionException; From 9831206d3d008db0dd4e35f870e67789cd0caf91 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Wed, 20 Aug 2025 13:41:54 +0800 Subject: [PATCH 21/22] fix checkstyle --- framework/src/main/java/org/tron/core/Wallet.java | 6 ++++-- framework/src/test/java/org/tron/core/WalletTest.java | 7 +++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 2621c7df575..25249bbf053 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -782,10 +782,12 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws /* In the maintenance period, the VoteStores will be cleared. - To avoid the race condition of VoteStores deleted but Witness vote counts not updated, return retry error. + To avoid the race condition of VoteStores deleted but Witness vote counts not updated, + return retry error. */ if (chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1) { - String message = "Service temporarily unavailable during maintenance period. Please try again later."; + String message = "Service temporarily unavailable during maintenance period. " + + "Please try again later."; throw new MaintenanceUnavailableException(message); } // It contains the final vote count at the end of the last epoch. diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 8937267d7f1..10b0aa178f6 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -881,8 +881,7 @@ public void testGetPaginatedNowWitnessList_CornerCase() { witnessList == null); witnessList = wallet.getPaginatedNowWitnessList(100, 0); - Assert.assertTrue("Should return an empty witness list when limit is 0", - witnessList == null); + Assert.assertTrue("Should return an empty witness list when limit is 0", witnessList == null); String fakeWitnessAddressPrefix = "fake_witness"; int fakeNumberOfWitnesses = 1000 + 10; @@ -902,7 +901,7 @@ public void testGetPaginatedNowWitnessList_CornerCase() { .delete(ByteString.copyFromUtf8(fakeWitnessAddressPrefix + i).toByteArray()); } } catch (MaintenanceUnavailableException e) { - Assert.fail(e.getMessage()); + Assert.fail(e.getMessage()); } } @@ -936,7 +935,7 @@ public void testGetPaginatedNowWitnessList() { logger.info("now request paginated witness list with 0 offset and 10 limit:"); GrpcAPI.WitnessList witnessList2 = null; - try { + try { // To avoid throw MaintenanceClearingException dbManager.getChainBaseManager().getDynamicPropertiesStore().saveStateFlag(0); witnessList2 = wallet.getPaginatedNowWitnessList(0, 10); From 8bb61f0aff5d2d0057dea11ae98a48ab636a6368 Mon Sep 17 00:00:00 2001 From: Sunny Jiao <> Date: Wed, 20 Aug 2025 14:13:49 +0800 Subject: [PATCH 22/22] fix style --- framework/src/main/java/org/tron/core/Wallet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 25249bbf053..b433352b471 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -786,8 +786,8 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws return retry error. */ if (chainBaseManager.getDynamicPropertiesStore().getStateFlag() == 1) { - String message = "Service temporarily unavailable during maintenance period. " + - "Please try again later."; + String message = + "Service temporarily unavailable during maintenance period. Please try again later."; throw new MaintenanceUnavailableException(message); } // It contains the final vote count at the end of the last epoch.