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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,50 @@ public static boolean paramQuantityIsNull(String quantity) {
return StringUtils.isEmpty(quantity) || quantity.equals("0x0");
}

/**
* Validation mode for {@link #requireValidHex}.
*/
public enum HexMode {
/**
* Execution-apis BYTES schema: requires {@code 0x} prefix and
* even total length; {@code ""} is accepted as empty bytes per
* geth's {@code hexutil.Bytes.UnmarshalText}.
*/
STRICT,
/**
* {@link ByteArray#fromHexString}'s lenient parsing: accepts bare
* hex and odd-length input. Kept for backward compatibility.
*/
LENIENT
}

/**
* Throws if {@code value} is not parseable hex under the given
* {@code mode}. {@code null} is treated as absent and returns
* silently. {@code fieldName} is used only in error messages.
*/
public static void requireValidHex(String fieldName, String value, HexMode mode)
Comment thread
waynercheung marked this conversation as resolved.
throws JsonRpcInvalidParamsException {
if (value == null) {
return;
}
if (mode == HexMode.STRICT) {
if (value.isEmpty()) {
return;
}
if (!value.startsWith("0x") || value.length() % 2 != 0) {
Comment thread
waynercheung marked this conversation as resolved.
throw new JsonRpcInvalidParamsException(
"invalid hex string for \"" + fieldName + "\"");
}
}
try {
ByteArray.fromHexString(value);
} catch (Exception e) {
throw new JsonRpcInvalidParamsException(
"invalid hex string for \"" + fieldName + "\"");
}
}

public static long parseQuantityValue(String value) throws JsonRpcInvalidParamsException {
long callValue = 0L;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -645,15 +645,15 @@ public String estimateGas(CallArguments args) throws JsonRpcInvalidRequestExcept
estimateEnergy(ownerAddress,
contractAddress,
args.parseValue(),
ByteArray.fromHexString(args.getData()),
ByteArray.fromHexString(args.resolveData()),
trxExtBuilder,
retBuilder,
estimateBuilder);
} else {
callTriggerConstantContract(ownerAddress,
contractAddress,
args.parseValue(),
ByteArray.fromHexString(args.getData()),
ByteArray.fromHexString(args.resolveData()),
trxExtBuilder,
retBuilder);
}
Expand Down Expand Up @@ -1007,7 +1007,7 @@ public String getCall(CallArguments transactionCall, Object blockParamObj)
byte[] contractAddressData = addressCompatibleToByteArray(transactionCall.getTo());

return call(addressData, contractAddressData, transactionCall.parseValue(),
ByteArray.fromHexString(transactionCall.getData()));
ByteArray.fromHexString(transactionCall.resolveData()));
}

@Override
Expand Down Expand Up @@ -1114,7 +1114,8 @@ private TransactionJson buildCreateSmartContractTransaction(byte[] ownerAddress,
smartBuilder.setOriginAddress(ByteString.copyFrom(ownerAddress));

// bytecode + parameter
smartBuilder.setBytecode(ByteString.copyFrom(ByteArray.fromHexString(args.getData())));
smartBuilder.setBytecode(
ByteString.copyFrom(ByteArray.fromHexString(args.resolveData())));

if (StringUtils.isNotEmpty(args.getName())) {
smartBuilder.setName(args.getName());
Expand Down Expand Up @@ -1159,8 +1160,9 @@ private TransactionJson buildTriggerSmartContractTransaction(byte[] ownerAddress
build.setOwnerAddress(ByteString.copyFrom(ownerAddress))
.setContractAddress(ByteString.copyFrom(contractAddress));

if (StringUtils.isNotEmpty(args.getData())) {
build.setData(ByteString.copyFrom(ByteArray.fromHexString(args.getData())));
String callData = args.resolveData();
if (StringUtils.isNotEmpty(callData)) {
build.setData(ByteString.copyFrom(ByteArray.fromHexString(callData)));
} else {
build.setData(ByteString.copyFrom(new byte[0]));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.paramQuantityIsNull;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.paramStringIsNull;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseQuantityValue;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.requireValidHex;

import com.google.protobuf.ByteString;
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;
import org.tron.api.GrpcAPI.BytesMessage;
import org.tron.common.utils.ByteArray;
import org.tron.core.Wallet;
import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException;
import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException;
import org.tron.core.services.jsonrpc.JsonRpcApiUtil.HexMode;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;

Expand All @@ -24,6 +28,16 @@
@ToString
public class BuildArguments {

/**
* Conflict error message wording. Mirrors go-ethereum's
* {@code setDefaults} verbatim — external EVM tooling may
* pattern-match this string. Do not change the wording without
* coordinating with downstream consumers.
*/
private static final String CONFLICT_ERR_MSG =
"both \"data\" and \"input\" are set and not equal. "
+ "Please use \"input\" to pass transaction call data";

@Getter
@Setter
private String from;
Expand All @@ -44,6 +58,9 @@ public class BuildArguments {
private String data;
@Getter
@Setter
private String input;
@Getter
@Setter
private String nonce = ""; //not used

@Getter
Expand Down Expand Up @@ -83,16 +100,50 @@ public BuildArguments(CallArguments args) {
gasPrice = args.getGasPrice();
value = args.getValue();
data = args.getData();
input = args.getInput();
}

/**
* Returns {@code input} if non-null, else {@code data}. Pure
* precedence resolution, mirroring go-ethereum's
* {@code TransactionArgs.data()}.
*
* <p>Both fields are first validated by
* {@link org.tron.core.services.jsonrpc.JsonRpcApiUtil#requireValidHex}
* — strict for {@code input}, lenient for {@code data} (see that
* method for the rules).
*
* <p>Conflict between {@code input} and {@code data} is not checked
* here. Build-path callers must route through
* {@link #getContractType(Wallet)} for the geth-equivalent
* {@code setDefaults} enforcement.
*
* <p>Java callers using positional constructors should pass
* {@code null} (not {@code ""}) for unset {@code input}.
*
* <p>Verb-prefix name (not {@code getXxx}) keeps Jackson and
* FastJSON's JavaBean introspection from invoking it during
* serialisation; two regression tests per DTO pin this invariant.
*/
public String resolveData() throws JsonRpcInvalidParamsException {
requireValidHex("input", input, HexMode.STRICT);
requireValidHex("data", data, HexMode.LENIENT);
return input != null ? input : data;
}

public ContractType getContractType(Wallet wallet) throws JsonRpcInvalidRequestException,
JsonRpcInvalidParamsException {
// Fail fast on bad hex / conflict before the state lookup;
// calldataEquals relies on resolveData() having validated hex first.
String resolvedData = resolveData();
Comment thread
waynercheung marked this conversation as resolved.
validateCallDataConflict();

ContractType contractType;

// to is null
if (paramStringIsNull(to)) {
Comment thread
waynercheung marked this conversation as resolved.
// data is null
if (paramStringIsNull(data)) {
if (paramStringIsNull(resolvedData)) {
throw new JsonRpcInvalidRequestException("invalid json request");
}

Expand Down Expand Up @@ -136,4 +187,22 @@ private boolean availableTransferAsset() {
return tokenId > 0 && tokenValue > 0 && paramQuantityIsNull(value);
}

/**
* Throws when both fields decode to non-equal bytes. Wording matches
* geth's setDefaults so existing tooling can detect the error string.
*/
private void validateCallDataConflict() throws JsonRpcInvalidParamsException {
Comment thread
waynercheung marked this conversation as resolved.
if (input != null && data != null && !calldataEquals(input, data)) {
throw new JsonRpcInvalidParamsException(CONFLICT_ERR_MSG);
}
}

/**
* Byte-level equality, so {@code "0xDEAD"} equals {@code "0xdead"}. Both
* args must have passed {@code requireValidHex} first.
*/
private static boolean calldataEquals(String a, String b) {
return Arrays.equals(ByteArray.fromHexString(a), ByteArray.fromHexString(b));
Comment thread
waynercheung marked this conversation as resolved.
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.addressCompatibleToByteArray;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.paramStringIsNull;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseQuantityValue;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.requireValidHex;

import com.google.protobuf.ByteString;
import lombok.AllArgsConstructor;
Expand All @@ -15,6 +16,7 @@
import org.tron.core.Wallet;
import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException;
import org.tron.core.exception.jsonrpc.JsonRpcInvalidRequestException;
import org.tron.core.services.jsonrpc.JsonRpcApiUtil.HexMode;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;

Expand Down Expand Up @@ -43,21 +45,41 @@ public class CallArguments {
private String data;
@Getter
@Setter
private String input;
@Getter
@Setter
private String nonce; // not used

/**
* Returns {@code input} if non-null, else {@code data}. Pure
* precedence resolution, mirroring go-ethereum's
* {@code TransactionArgs.data()}; no conflict check on the query
* path (matches geth's {@code ToMessage}). See
* {@link BuildArguments#resolveData()} for the rationale on
* naming, validation split, and serialiser interaction.
*/
public String resolveData() throws JsonRpcInvalidParamsException {
Comment thread
waynercheung marked this conversation as resolved.
requireValidHex("input", input, HexMode.STRICT);
requireValidHex("data", data, HexMode.LENIENT);
return input != null ? input : data;
}

/**
* just support TransferContract, CreateSmartContract and TriggerSmartContract
* */
public ContractType getContractType(Wallet wallet) throws JsonRpcInvalidRequestException,
JsonRpcInvalidParamsException {
ContractType contractType;

// from or to is null
if (paramStringIsNull(from)) {
throw new JsonRpcInvalidRequestException("invalid json request");
} else if (paramStringIsNull(to)) {
}
// Fail fast on bad hex before the state lookup.
String resolvedData = resolveData();

ContractType contractType;
if (paramStringIsNull(to)) {
// data is null
if (paramStringIsNull(data)) {
if (paramStringIsNull(resolvedData)) {
throw new JsonRpcInvalidRequestException("invalid json request");
}

Expand Down
Loading
Loading