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 @@ -63,6 +63,7 @@ public class JsonRpcApiUtil {
public static final String TAG_SAFE_SUPPORT_ERROR = "TAG safe not supported";
public static final String BLOCK_NUM_ERROR = "invalid block number";
public static final String TX_INDEX_ERROR = "invalid index value";
public static final String INVALID_FILTER_REQUEST = "invalid filter request";

private static final SecureRandom random = new SecureRandom();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,9 @@ String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException,
@JsonRpcErrors({
@JsonRpcError(exception = JsonRpcMethodNotFoundException.class, code = -32601, data = "{}"),
@JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"),
@JsonRpcError(exception = ItemNotFoundException.class, code = -32000, data = "{}"),
})
boolean uninstallFilter(String filterId) throws JsonRpcInvalidParamsException,
JsonRpcMethodNotFoundException, ItemNotFoundException;
JsonRpcMethodNotFoundException;

@JsonRpcMethod("eth_getFilterChanges")
@JsonRpcErrors({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public enum RequestSource {
}

private static final String FILTER_NOT_FOUND = "filter not found";
private static final String INVALID_PARAMS = "invalid params";
public static final int EXPIRE_SECONDS = 5 * 60;
private final int maxBlockFilterNum = Args.getInstance().getJsonRpcMaxBlockFilterNum();
private final int maxLogFilterNum = Args.getInstance().getJsonRpcMaxLogFilterNum();
Expand Down Expand Up @@ -363,14 +364,14 @@ public String ethGetBlockTransactionCountByNumber(String blockNumOrTag)
public BlockResult ethGetBlockByHash(String blockHash, Boolean fullTransactionObjects)
throws JsonRpcInvalidParamsException {
final Block b = getBlockByJsonHash(blockHash);
return getBlockResult(b, fullTransactionObjects);
return getBlockResult(b, Boolean.TRUE.equals(fullTransactionObjects));
}

@Override
public BlockResult ethGetBlockByNumber(String blockNumOrTag, Boolean fullTransactionObjects)
throws JsonRpcInvalidParamsException {
final Block b = getBlockByNumOrTag(blockNumOrTag);
return (b == null ? null : getBlockResult(b, fullTransactionObjects));
return (b == null ? null : getBlockResult(b, Boolean.TRUE.equals(fullTransactionObjects)));
}

/**
Expand Down Expand Up @@ -673,6 +674,10 @@ public String gasPrice() {
@Override
public String estimateGas(CallArguments args) throws JsonRpcInvalidRequestException,
JsonRpcInvalidParamsException, JsonRpcInternalException {
if (args == null) {
throw new JsonRpcInvalidParamsException(INVALID_PARAMS);
}

byte[] ownerAddress = addressCompatibleToByteArray(args.getFrom());

ContractType contractType = args.getContractType(wallet);
Expand Down Expand Up @@ -1001,6 +1006,9 @@ private List<TransactionReceipt> getTransactionReceiptsFromBlock(BlockCapsule bl
public String getCall(CallArguments transactionCall, Object blockParamObj)
throws JsonRpcInvalidParamsException, JsonRpcInvalidRequestException,
JsonRpcInternalException {
if (transactionCall == null) {
throw new JsonRpcInvalidParamsException(INVALID_PARAMS);
}

String blockNumOrTag;
if (blockParamObj instanceof HashMap) {
Expand Down Expand Up @@ -1333,6 +1341,10 @@ public TransactionJson buildTransaction(BuildArguments args)
throw new JsonRpcMethodNotFoundException(msg);
}

if (args == null) {
throw new JsonRpcInvalidParamsException(INVALID_PARAMS);
}

byte[] fromAddressData;
try {
fromAddressData = addressCompatibleToByteArray(args.getFrom());
Expand Down Expand Up @@ -1441,6 +1453,10 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException,
JsonRpcMethodNotFoundException, JsonRpcExceedLimitException {
disableInPBFT("eth_newFilter");

if (fr == null) {
throw new JsonRpcInvalidParamsException(JsonRpcApiUtil.INVALID_FILTER_REQUEST);
}

// not supports finalized as block parameter
if (FINALIZED_STR.equalsIgnoreCase(fr.getFromBlock())
|| FINALIZED_STR.equalsIgnoreCase(fr.getToBlock())) {
Expand Down Expand Up @@ -1488,10 +1504,13 @@ public String newBlockFilter() throws JsonRpcMethodNotFoundException,
}

@Override
public boolean uninstallFilter(String filterId) throws ItemNotFoundException,
JsonRpcMethodNotFoundException {
public boolean uninstallFilter(String filterId) throws JsonRpcMethodNotFoundException {
disableInPBFT("eth_uninstallFilter");

if (filterId == null) {
return false;
}

Map<String, BlockFilterAndResult> blockFilter2Result;
Map<String, LogFilterAndResult> eventFilter2Result;
if (getSource() == RequestSource.FULLNODE) {
Expand All @@ -1503,22 +1522,21 @@ public boolean uninstallFilter(String filterId) throws ItemNotFoundException,
}

filterId = ByteArray.fromHex(filterId);
if (eventFilter2Result.containsKey(filterId)) {
eventFilter2Result.remove(filterId);
} else if (blockFilter2Result.containsKey(filterId)) {
blockFilter2Result.remove(filterId);
} else {
throw new ItemNotFoundException(FILTER_NOT_FOUND);
if (eventFilter2Result.remove(filterId) != null) {
return true;
}

return true;
return blockFilter2Result.remove(filterId) != null;
}

@Override
public Object[] getFilterChanges(String filterId) throws ItemNotFoundException,
JsonRpcMethodNotFoundException {
disableInPBFT("eth_getFilterChanges");

if (filterId == null) {
throw new ItemNotFoundException(FILTER_NOT_FOUND);
}

Map<String, BlockFilterAndResult> blockFilter2Result;
Map<String, LogFilterAndResult> eventFilter2Result;
if (getSource() == RequestSource.FULLNODE) {
Expand All @@ -1540,6 +1558,10 @@ public LogFilterElement[] getLogs(FilterRequest fr) throws JsonRpcInvalidParamsE
JsonRpcMethodNotFoundException, JsonRpcTooManyResultException {
disableInPBFT("eth_getLogs");

if (fr == null) {
throw new JsonRpcInvalidParamsException(JsonRpcApiUtil.INVALID_FILTER_REQUEST);
}

long currentMaxBlockNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber();
//convert FilterRequest to LogFilterWrapper
LogFilterWrapper logFilterWrapper = new LogFilterWrapper(fr, currentMaxBlockNum, wallet, true);
Expand All @@ -1554,6 +1576,10 @@ public LogFilterElement[] getFilterLogs(String filterId) throws
JsonRpcMethodNotFoundException, JsonRpcTooManyResultException {
disableInPBFT("eth_getFilterLogs");

if (filterId == null) {
throw new ItemNotFoundException(FILTER_NOT_FOUND);
}

Map<String, LogFilterAndResult> eventFilter2Result;
if (getSource() == RequestSource.FULLNODE) {
eventFilter2Result = eventFilter2ResultFull;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.tron.core.services.jsonrpc.filters;

import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.INVALID_FILTER_REQUEST;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.addressToByteArray;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.topicToByteArray;

Expand Down Expand Up @@ -28,7 +29,8 @@ public class LogFilter {
private byte[][] contractAddresses = new byte[0][];
// example: [[func1, func2], null, [A, B], [C]]
// first topic must be func1 or func2,second can be any,third must be A or B,forth must be C
// [A, null] is not allowed.
// A null positional topic is a wildcard, so [A, null] is valid.
// A null entry in an OR-list is invalid, so [[A, null]] is not allowed.
@Getter
@Setter
private List<byte[][]> topics = new ArrayList<>();
Expand All @@ -46,6 +48,10 @@ public LogFilter() {
* construct one LogFilter from part parameters of FilterRequest
*/
public LogFilter(FilterRequest fr) throws JsonRpcInvalidParamsException {
if (fr == null) {
throw new JsonRpcInvalidParamsException(INVALID_FILTER_REQUEST);
}

if (fr.getAddress() instanceof String) {
withContractAddress(addressToByteArray((String) fr.getAddress()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseQuantityValue;
import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.requireValidHex;

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.google.protobuf.ByteString;
import java.util.Arrays;
import lombok.AllArgsConstructor;
Expand All @@ -23,6 +25,13 @@
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;

/**
* Arguments for {@code buildTransaction}.
*
* <p>Fields annotated with {@link JsonSetter} retain their defaults when an explicit JSON
* {@code null} is deserialized. This protection applies to Jackson input only; direct setter calls
* may still assign {@code null} and callers must preserve the DTO invariants.
*/
@NoArgsConstructor
@AllArgsConstructor
@ToString
Expand Down Expand Up @@ -65,28 +74,34 @@ public class BuildArguments {

@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private Long tokenId = 0L;
@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private Long tokenValue = 0L;
@Getter
@Setter
private String abi = "";
@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private Long consumeUserResourcePercent = 0L;
@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private Long originEnergyLimit = 0L;
@Getter
@Setter
private String name = "";

@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private Integer permissionId = 0;
@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private String extraData = "";

@Getter
Expand Down Expand Up @@ -205,4 +220,4 @@ private static boolean calldataEquals(String a, String b) {
return Arrays.equals(ByteArray.fromHexString(a), ByteArray.fromHexString(b));
}

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

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.google.protobuf.ByteString;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -20,13 +22,21 @@
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;

/**
* Arguments for EVM call-style JSON-RPC methods.
*
* <p>Fields annotated with {@link JsonSetter} retain their defaults when an explicit JSON
* {@code null} is deserialized. This protection applies to Jackson input only; direct setter calls
* may still assign {@code null} and callers must preserve the DTO invariants.
*/
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class CallArguments {

@Getter
@Setter
@JsonSetter(nulls = Nulls.SKIP)
private String from = "0x0000000000000000000000000000000000000000";
@Getter
@Setter
Expand Down Expand Up @@ -108,4 +118,4 @@ public ContractType getContractType(Wallet wallet) throws JsonRpcInvalidRequestE
public long parseValue() throws JsonRpcInvalidParamsException {
return parseQuantityValue(value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.tron.core.jsonrpc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Assert;
import org.junit.Test;
import org.tron.core.services.jsonrpc.types.BuildArguments;
import org.tron.core.services.jsonrpc.types.CallArguments;

public class JsonRpcArgumentNullBindingTest {

private static final ObjectMapper MAPPER = new ObjectMapper();

@Test
public void testBuildArgumentsExplicitNullMatchesOmittedDefaults() throws Exception {
BuildArguments omitted = MAPPER.readValue("{}", BuildArguments.class);
String[] optionalFields = {
"tokenId",
"tokenValue",
"consumeUserResourcePercent",
"originEnergyLimit",
"permissionId",
"extraData"
};

for (String field : optionalFields) {
ObjectNode input = MAPPER.createObjectNode();
input.putNull(field);
BuildArguments explicitNull = MAPPER.treeToValue(input, BuildArguments.class);

assertBuildDefaultsEqual(omitted, explicitNull);
}
}

@Test
public void testCallArgumentsNullFromMatchesOmittedDefault() throws Exception {
CallArguments omitted = MAPPER.readValue("{}", CallArguments.class);
CallArguments explicitNull = MAPPER.readValue("{\"from\":null}", CallArguments.class);

Assert.assertEquals(omitted.getFrom(), explicitNull.getFrom());
Assert.assertEquals(
"0x0000000000000000000000000000000000000000", explicitNull.getFrom());
}

private static void assertBuildDefaultsEqual(BuildArguments expected, BuildArguments actual) {
Assert.assertEquals(expected.getTokenId(), actual.getTokenId());
Assert.assertEquals(expected.getTokenValue(), actual.getTokenValue());
Assert.assertEquals(expected.getConsumeUserResourcePercent(),
actual.getConsumeUserResourcePercent());
Assert.assertEquals(expected.getOriginEnergyLimit(), actual.getOriginEnergyLimit());
Assert.assertEquals(expected.getPermissionId(), actual.getPermissionId());
Assert.assertEquals(expected.getExtraData(), actual.getExtraData());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.db.Manager;
import org.tron.core.exception.jsonrpc.JsonRpcInternalException;
import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException;
import org.tron.core.services.NodeInfoService;
import org.tron.core.services.jsonrpc.TronJsonRpcImpl;
import org.tron.core.services.jsonrpc.types.CallArguments;
Expand Down Expand Up @@ -51,6 +52,34 @@ public void tearDown() throws Exception {
CommonParameter.getInstance().setEstimateEnergy(originalEstimateEnergy);
}

@Test
public void testNullCallArgumentsRejectedAsInvalidParams() throws Exception {
mockRpc = new TronJsonRpcImpl(mock(NodeInfoService.class), mock(Wallet.class));

JsonRpcInvalidParamsException callError = assertThrows(JsonRpcInvalidParamsException.class,
() -> mockRpc.getCall(null, "latest"));
Assert.assertEquals("invalid params", callError.getMessage());

JsonRpcInvalidParamsException estimateError = assertThrows(JsonRpcInvalidParamsException.class,
() -> mockRpc.estimateGas(null));
Assert.assertEquals("invalid params", estimateError.getMessage());
}

@Test
public void testEstimateGasKeepsInvalidQuantityAndDataAsInvalidParams() throws Exception {
mockRpc = new TronJsonRpcImpl(mock(NodeInfoService.class), mock(Wallet.class));

CallArguments invalidQuantity = newCallArgs();
invalidQuantity.setValue("0xzz");
assertThrows(JsonRpcInvalidParamsException.class,
() -> mockRpc.estimateGas(invalidQuantity));

CallArguments invalidData = newCallArgs();
invalidData.setData("0xzz");
assertThrows(JsonRpcInvalidParamsException.class,
() -> mockRpc.estimateGas(invalidData));
}

@Test
public void testGetCallAppendsRevertReason() throws Exception {
byte[] revertData = ByteArray.fromHexString(ERROR_REVERT_HEX);
Expand Down
Loading
Loading