Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8152529
fix(api): reject a contract query for a malformed address
0xbigapple Aug 25, 2026
3a49b86
fix(api): bound json numbers before conversion
0xbigapple Aug 25, 2026
7bca8a9
fix(api): answer an unusable address identically on every endpoint
0xbigapple Aug 25, 2026
045f929
fix(shielded): bound the shielded TRC-20 description counts
0xbigapple Aug 25, 2026
8e90727
fix(shielded): report unusable shielded trigger input as a validation…
0xbigapple Aug 25, 2026
de5ae92
fix(api): bound Base58 rendering of oversized address fields
0xbigapple Sep 29, 2026
6142b03
fix(api): surface input-validation messages past the error sanitizer
0xbigapple Sep 29, 2026
ba54931
fix(api): simplify address and Permission_id error handling
0xbigapple Oct 9, 2026
d4d27d0
refactor(api): rename hex to isHex in getAddress
0xbigapple Oct 12, 2026
fd04799
fix(shielded): drop the mint receive description count check
0xbigapple Oct 12, 2026
d51dd27
Merge remote-tracking branch 'upstream/release_v4.8.3' into fix/api-i…
0xbigapple Oct 13, 2026
d986c51
fix(shielded): bound mint and burn receive description counts
0xbigapple Oct 13, 2026
8734a39
fix(http): keep Base58 rendering for genesis addresses
0xbigapple Oct 14, 2026
437200f
refactor(http): simplify genesis address comment
0xbigapple Oct 14, 2026
75939ea
refactor(shielded): spell out transfer count checks
0xbigapple Oct 14, 2026
f7a1fa3
Merge remote-tracking branch 'upstream/release_v4.8.3' into fix/api-i…
0xbigapple Oct 14, 2026
15a379f
Merge remote-tracking branch 'upstream/release_v4.8.3' into fix/api-i…
0xbigapple Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion chainbase/src/main/java/org/tron/common/utils/WalletUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import java.util.List;
import java.util.stream.Collectors;
import org.tron.common.crypto.Hash;
import org.tron.common.math.StrictMathWrapper;
import org.tron.common.parameter.CommonParameter;
import org.tron.core.Constant;
import org.tron.core.capsule.ContractCapsule;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.ContractValidateException;
Expand All @@ -24,6 +26,8 @@

public class WalletUtil {

private static final String INVALID_ADDRESS = "invalid-address";

public static boolean checkPermissionOperations(Permission permission, Contract contract)
throws PermissionException {
ByteString operations = permission.getOperations();
Expand Down Expand Up @@ -115,9 +119,22 @@ public static boolean isConstant(SmartContract.ABI abi, byte[] selector) {
return false;
}

public static String getAddressString(byte[] address) {
if (address == null) {
return INVALID_ADDRESS + "(null)";
}
if (address.length == Constant.STANDARD_ADDRESS_SIZE
|| address.length == Constant.TRON_ADDRESS_SIZE) {
return encode58Check(address);
}
int shown = StrictMathWrapper.min(address.length, Constant.TRON_ADDRESS_SIZE);
return INVALID_ADDRESS + "(" + ByteArray.toHexString(Arrays.copyOf(address, shown))
+ (shown < address.length ? "..." : "") + ",len=" + address.length + ")";
}

public static List<String> getAddressStringList(Collection<ByteString> collection) {
return collection.stream()
.map(bytes -> encode58Check(bytes.toByteArray()))
.map(address -> getAddressString(address.toByteArray()))
.collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.ForkController;
import org.tron.common.utils.Sha256Hash;
import org.tron.common.utils.StringUtil;
import org.tron.common.utils.WalletUtil;
import org.tron.core.Constant;
import org.tron.core.capsule.AccountCapsule;
Expand Down Expand Up @@ -141,7 +140,7 @@ public void checkIsConstant() throws ContractValidateException, VMIllegalExcepti
.get(triggerContractFromTransaction.getContractAddress().toByteArray());
if (contract == null) {
throw new ContractValidateException(String.format("contract: %s is not in contract store",
StringUtil.encode58Check(triggerContractFromTransaction
WalletUtil.getAddressString(triggerContractFromTransaction
.getContractAddress().toByteArray())));

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.tron.core.exception;

import java.security.InvalidParameterException;

public class InvalidHttpParameterException extends InvalidParameterException {

public InvalidHttpParameterException(String message) {
super(message);
}

public InvalidHttpParameterException(String message, Throwable cause) {
super(message);
initCause(cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
import java.security.SignatureException;
import java.util.stream.Collectors;
import org.bouncycastle.util.encoders.Hex;
import org.tron.common.crypto.ECKey;
import org.tron.common.overlay.message.Message;
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.Sha256Hash;
import org.tron.common.utils.StringUtil;
import org.tron.common.utils.WalletUtil;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.P2pException;
import org.tron.protos.Protocol.PBFTMessage;
Expand Down Expand Up @@ -119,8 +118,7 @@ public String getDataString() {
private String decode() {
try {
SRL srList = SRL.parseFrom(pbftMessage.getRawData().getData().toByteArray());
return "sr list = " + srList.getSrAddressList().stream().map(
bytes -> StringUtil.encode58Check(bytes.toByteArray())).collect(Collectors.toList());
return "sr list = " + WalletUtil.getAddressStringList(srList.getSrAddressList());
} catch (InvalidProtocolBufferException e) {
}
return "decode error";
Expand Down
31 changes: 24 additions & 7 deletions framework/src/main/java/org/tron/core/Wallet.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.Sha256Hash;
import org.tron.common.utils.StringUtil;
import org.tron.common.utils.Utils;
import org.tron.common.utils.WalletUtil;
import org.tron.common.zksnark.IncrementalMerkleTreeContainer;
Expand Down Expand Up @@ -261,6 +260,9 @@ public class Wallet {
"Shielded transaction API is disabled; "
+ "set node.allowShieldedTransactionApi=true to enable.";
private static final String PAYMENT_ADDRESS_FORMAT_WRONG = "paymentAddress format is wrong";
// the authoritative bound is checkBigIntegerRange: uint256 max is 78 decimal digits,
// this only keeps the string short enough to convert cheaply
private static final int MAX_SHIELDED_AMOUNT_LENGTH = 80;
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";
Expand Down Expand Up @@ -3221,7 +3223,7 @@ public SmartContract getContract(GrpcAPI.BytesMessage bytesMessage) {
if (accountCapsule == null) {
logger.warn(
"Get contract failed, the account {} does not exist or the account "
+ "does not have a code hash!", StringUtil.encode58Check(address));
+ "does not have a code hash!", WalletUtil.getAddressString(address));
return null;
}

Expand Down Expand Up @@ -3250,7 +3252,7 @@ public SmartContractDataWrapper getContractInfo(GrpcAPI.BytesMessage bytesMessag
if (accountCapsule == null) {
logger.warn(
"Get contract failed, the account {} does not exist or the account does not have a code "
+ "hash!", StringUtil.encode58Check(address));
+ "hash!", WalletUtil.getAddressString(address));
return null;
}

Expand Down Expand Up @@ -4226,6 +4228,9 @@ private BigInteger getBigIntegerFromString(String in) {
if (trimmedIn.length() == 0) {
return BigInteger.ZERO;
}
if (trimmedIn.length() > MAX_SHIELDED_AMOUNT_LENGTH) {
throw new IllegalArgumentException("invalid shielded amount");
}
return new BigInteger(trimmedIn, 10);
}

Expand Down Expand Up @@ -4337,7 +4342,12 @@ public BytesMessage getTriggerInputForShieldedTRC20Contract(

ShieldedTRC20Parameters shieldedTRC20Parameters = request.getShieldedTRC20Parameters();
List<BytesMessage> spendAuthoritySignature = request.getSpendAuthoritySignatureList();
BigInteger value = getBigIntegerFromString(request.getAmount());
BigInteger value;
try {
value = getBigIntegerFromString(request.getAmount());
} catch (IllegalArgumentException e) {
throw new ContractValidateException("invalid amount", e);
}
checkBigIntegerRange(value);
byte[] transparentToAddress = request.getTransparentToAddress().toByteArray();
byte[] transparentToAddressTvm = new byte[20];
Expand Down Expand Up @@ -4402,9 +4412,16 @@ public BytesMessage getTriggerInputForShieldedTRC20Contract(
}
parametersBuilder.setBurnCiphertext(burnCiper);
}
String input = parametersBuilder
.getTriggerContractInput(shieldedTRC20Parameters, spendAuthoritySignature, value, false,
transparentToAddressTvm);
String input;
try {
input = parametersBuilder
.getTriggerContractInput(shieldedTRC20Parameters, spendAuthoritySignature, value, false,
transparentToAddressTvm);
} catch (IllegalArgumentException e) {
// the builder names the offending argument in fixed strings; none of them echoes an input
throw new ContractValidateException(
"invalid shielded TRC-20 trigger input: " + e.getMessage(), e);
}
if (Objects.isNull(input)) {
throw new ZksnarkException("generate the trigger contract parameters error");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.apache.commons.lang3.StringUtils;
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.Sha256Hash;
import org.tron.common.utils.StringUtil;
import org.tron.common.utils.WalletUtil;
import org.tron.core.ChainBaseManager;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.config.args.Args;
Expand Down Expand Up @@ -132,7 +132,7 @@ public String toString() {
ByteString address = helloMessage.getAddress();
if (!address.isEmpty()) {
builder.append("address:")
.append(StringUtil.encode58Check(address.toByteArray())).append("\n");
.append(WalletUtil.getAddressString(address.toByteArray())).append("\n");
}

ByteString signature = helloMessage.getSignature();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.DecoderException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.db.Manager;
Expand All @@ -21,18 +20,19 @@ public class GetBrokerageServlet extends RateLimiterServlet {
private Manager manager;

protected void doGet(HttpServletRequest request, HttpServletResponse response) {
byte[] address;
try {
address = Util.getAddress(request);
} catch (Exception e) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This catch-all around Util.getAddress routes every exception into the debug-level client-error path, but getAddress already converts all expected request-input failures to InvalidHttpParameterException (blank/missing address, malformed hex or base58, unparseable JSON body). Anything else that escapes it — e.g. an IOException from reading the POST body in getRequestValue, or an unexpected runtime failure — is a genuine server-side fault, and Util.processError now hides it at DEBUG while reporting it to the client as a parameter error. Per the new Util.processServerError contract, server-side failures should be logged at ERROR. Catch InvalidHttpParameterException in the parsing block and let unexpected exceptions hit the second catch (single try block with both catch clauses). GetRewardServlet has the identical issue.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At framework/src/main/java/org/tron/core/services/http/GetBrokerageServlet.java, line 22:

<comment>This catch-all around `Util.getAddress` routes every exception into the debug-level client-error path, but `getAddress` already converts all expected request-input failures to `InvalidHttpParameterException` (blank/missing address, malformed hex or base58, unparseable JSON body). Anything else that escapes it — e.g. an `IOException` from reading the POST body in `getRequestValue`, or an unexpected runtime failure — is a genuine server-side fault, and `Util.processError` now hides it at DEBUG while reporting it to the client as a parameter error. Per the new `Util.processServerError` contract, server-side failures should be logged at ERROR. Catch `InvalidHttpParameterException` in the parsing block and let unexpected exceptions hit the second catch (single try block with both catch clauses). `GetRewardServlet` has the identical issue.</comment>

<file context>
@@ -17,18 +16,19 @@ public class GetBrokerageServlet extends RateLimiterServlet {
+    byte[] address;
+    try {
+      address = Util.getAddress(request);
+    } catch (Exception e) {
+      Util.processError(e, response);
+      return;
</file context>

Util.processError(e, response);
return;
}
try {
int value = 0;
byte[] address = Util.getAddress(request);
long cycle = manager.getDynamicPropertiesStore().getCurrentCycleNumber();
if (address != null) {
value = manager.getDelegationStore().getBrokerage(cycle, address);
}
int value = manager.getDelegationStore().getBrokerage(cycle, address);
response.getWriter().println("{\"brokerage\": " + value + "}");
} catch (DecoderException | IllegalArgumentException e) {
Util.writeAuditedError(Util.INVALID_ADDRESS_MSG, response);
} catch (Exception e) {
Util.processError(e, response);
Util.processServerError(e, response);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.DecoderException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.db.Manager;
Expand All @@ -21,18 +20,19 @@ public class GetRewardServlet extends RateLimiterServlet {
private Manager manager;

protected void doGet(HttpServletRequest request, HttpServletResponse response) {
byte[] address;
try {
long value = 0;
byte[] address = Util.getAddress(request);
if (address != null) {
value = manager.getMortgageService().queryReward(address);
}
address = Util.getAddress(request);
} catch (Exception e) {
Util.processError(e, response);
return;
}
try {
long value = manager.getMortgageService().queryReward(address);
String out = JsonFormat.isInt64AsString()
? "{\"reward\": \"" + value + "\"}"
: "{\"reward\": " + value + "}";
response.getWriter().println(out);
} catch (DecoderException | IllegalArgumentException e) {
Util.writeAuditedError(Util.INVALID_ADDRESS_MSG, response);
} catch (Exception e) {
Util.processServerError(e, response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public class JsonFormat {
= "Writing to a StringBuilder threw an IOException (should never happen).";
private static final String EXPECTED_STRING = "Expected string.";
private static final String MISSING_END_QUOTE = "String missing ending quote.";
// Keeps the genesis block addresses compatible with their Base58Check rendering.
private static final int MAX_BASE58_ADDRESS_SIZE = 128;

// Bounds all work and error messages performed by parseInteger after tokenization.
static final int MAX_INTEGER_TOKEN_LENGTH = 256;
Expand Down Expand Up @@ -889,6 +891,9 @@ static String escapeBytes(ByteString input, final String fieldName, boolean self
static String escapeBytesSelfType(ByteString input, final String fieldName) {
//Address
if (HttpSelfFormatFieldName.isAddressFormat(fieldName)) {
if (input.size() > MAX_BASE58_ADDRESS_SIZE) {
return ByteArray.toHexString(input.toByteArray());
}
return StringUtil.encode58Check(input.toByteArray());
}
//Normal String
Expand Down
Loading
Loading