Skip to content
Merged
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 @@ -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;
Comment thread
waynercheung marked this conversation as resolved.

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

private static final String FILTER_NOT_FOUND = "filter not found";
private static final String INVALID_PARAMS = "invalid params";
Comment thread
waynercheung marked this conversation as resolved.
private static final String INVALID_FILTER_REQUEST = "invalid filter request";
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 @@ -396,17 +398,29 @@ public String ethGetBlockTransactionCountByNumber(String blockNumOrTag)
@Override
public BlockResult ethGetBlockByHash(String blockHash, Boolean fullTransactionObjects)
throws JsonRpcInvalidParamsException {
final Block b = getBlockByJsonHash(blockHash);
byte[] hash = hashToByteArray(blockHash);
requireParam(fullTransactionObjects, INVALID_PARAMS);
final Block b = getBlockByHash(hash);
return getBlockResult(b, fullTransactionObjects);
}

@Override
public BlockResult ethGetBlockByNumber(String blockNumOrTag, Boolean fullTransactionObjects)
throws JsonRpcInvalidParamsException {
final Block b = getBlockByNumOrTag(blockNumOrTag);
Long blockNum = parseBlockSelector(blockNumOrTag);
requireParam(fullTransactionObjects, INVALID_PARAMS);
final Block b = getBlockBySelector(blockNum);
return (b == null ? null : getBlockResult(b, fullTransactionObjects));
}

private static <T> T requireParam(T value, String message)
throws JsonRpcInvalidParamsException {
if (value == null) {
throw new JsonRpcInvalidParamsException(message);
}
return value;
}

/**
* Reject any block selector that is not "latest".
* Accepts "latest" silently; throws for other tags, numeric blocks, or invalid input.
Expand All @@ -424,20 +438,37 @@ private void requireLatestBlockTag(String blockNumOrTag)
}

private Block getBlockByJsonHash(String blockHash) throws JsonRpcInvalidParamsException {
byte[] bHash = hashToByteArray(blockHash);
return wallet.getBlockById(ByteString.copyFrom(bHash));
return getBlockByHash(hashToByteArray(blockHash));
}

private Block getBlockByHash(byte[] blockHash) {
return wallet.getBlockById(ByteString.copyFrom(blockHash));
}

private Block getBlockByNumOrTag(String blockNumOrTag) throws JsonRpcInvalidParamsException {
return getBlockBySelector(parseBlockSelector(blockNumOrTag));
}

/**
* Validates a block number or tag without reading a block. Returns null for "latest".
*/
private Long parseBlockSelector(String blockNumOrTag) throws JsonRpcInvalidParamsException {
if (JsonRpcApiUtil.isBlockTag(blockNumOrTag)) {
if (LATEST_STR.equalsIgnoreCase(blockNumOrTag)) {
// Return the head block directly from blockStore, bypassing blockIndexStore
// which may not yet be written when latestBlockHeaderNumber is already updated.
return wallet.getNowBlock();
return null;
}
return wallet.getBlockByNum(JsonRpcApiUtil.parseBlockTag(blockNumOrTag, wallet));
return JsonRpcApiUtil.parseBlockTag(blockNumOrTag, wallet);
}
return wallet.getBlockByNum(parseBlockNumber(blockNumOrTag));
return parseBlockNumber(blockNumOrTag);
}

private Block getBlockBySelector(Long blockNum) {
if (blockNum == null) {
// Return the head block directly from blockStore, bypassing blockIndexStore
// which may not yet be written when latestBlockHeaderNumber is already updated.
return wallet.getNowBlock();
}
return wallet.getBlockByNum(blockNum);
}

private BlockResult getBlockResult(Block block, boolean fullTx) {
Expand Down Expand Up @@ -708,6 +739,8 @@ public String gasPrice() {
@Override
public String estimateGas(CallArguments args) throws JsonRpcInvalidRequestException,
JsonRpcInvalidParamsException, JsonRpcInternalException {
requireParam(args, INVALID_PARAMS);

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

ContractType contractType = args.getContractType(wallet);
Expand Down Expand Up @@ -1036,6 +1069,7 @@ private List<TransactionReceipt> getTransactionReceiptsFromBlock(BlockCapsule bl
public String getCall(CallArguments transactionCall, Object blockParamObj)
throws JsonRpcInvalidParamsException, JsonRpcInvalidRequestException,
JsonRpcInternalException {
requireParam(transactionCall, INVALID_PARAMS);

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

requireParam(args, INVALID_PARAMS);

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

requireParam(fr, INVALID_FILTER_REQUEST);

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

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

requireParam(filterId, INVALID_PARAMS);

Map<String, BlockFilterAndResult> blockFilter2Result;
Map<String, LogFilterAndResult> eventFilter2Result;
if (getSource() == RequestSource.FULLNODE) {
Expand All @@ -1539,22 +1579,19 @@ 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 {
JsonRpcInvalidParamsException, JsonRpcMethodNotFoundException {
disableInPBFT("eth_getFilterChanges");

requireParam(filterId, INVALID_PARAMS);

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

requireParam(fr, INVALID_FILTER_REQUEST);

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

requireParam(filterId, INVALID_PARAMS);

Map<String, LogFilterAndResult> eventFilter2Result;
if (getSource() == RequestSource.FULLNODE) {
eventFilter2Result = eventFilter2ResultFull;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,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 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());
}
}
Loading
Loading