From cb4acdcfffb7c478e58682164998b218509db6ff Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Thu, 15 May 2025 09:21:02 +0800 Subject: [PATCH 01/12] add eth_getBlockReceipts --- .../core/services/jsonrpc/TronJsonRpc.java | 8 ++ .../services/jsonrpc/TronJsonRpcImpl.java | 22 +++++ .../tron/core/jsonrpc/JsonrpcServiceTest.java | 91 +++++++++++++++++++ 3 files changed, 121 insertions(+) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 52a3a2380d1..1eaf48017c2 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -146,6 +146,14 @@ TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTag, Stri }) TransactionReceipt getTransactionReceipt(String txid) throws JsonRpcInvalidParamsException; + @JsonRpcMethod("eth_getBlockReceipts") + @JsonRpcErrors({ + @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), + @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}") + }) + List getBlockReceipts(String bnOrId) + throws JsonRpcInvalidParamsException, JsonRpcInternalException; + @JsonRpcMethod("eth_call") @JsonRpcErrors({ @JsonRpcError(exception = JsonRpcInvalidRequestException.class, code = -32600, data = "{}"), diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index eb432432a1c..43824ac506f 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -15,6 +15,7 @@ import com.google.protobuf.GeneratedMessageV3; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -780,6 +781,27 @@ public TransactionReceipt getTransactionReceipt(String txId) return new TransactionReceipt(block, transactionInfo, wallet); } + @Override + public List getBlockReceipts(String blockNumOrTag) + throws JsonRpcInvalidParamsException, JsonRpcInternalException { + BlockResult blockResult = ethGetBlockByNumber(blockNumOrTag, true); + List transactionReceiptList = new ArrayList<>(); + + for (Object o: blockResult.getTransactions()){ + if (o instanceof TransactionResult) { + String txHash = ((TransactionResult) o).getHash(); + + TransactionReceipt transactionReceipt = getTransactionReceipt(txHash); + if (transactionReceipt == null) { + throw new JsonRpcInternalException("transactionReceipt is null, txHash is " + txHash); + } + transactionReceiptList.add(transactionReceipt); + } + } + + return transactionReceiptList; + } + @Override public String getCall(CallArguments transactionCall, Object blockParamObj) throws JsonRpcInvalidParamsException, JsonRpcInvalidRequestException, diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index 0f2214c5c9c..945ca83bb14 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -1,6 +1,7 @@ package org.tron.core.jsonrpc; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getByJsonBlockId; +import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.TAG_PENDING_SUPPORT_ERROR; import com.alibaba.fastjson.JSON; import com.google.gson.JsonArray; @@ -33,8 +34,11 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionInfoCapsule; +import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.capsule.utils.BlockUtil; import org.tron.core.config.args.Args; +import org.tron.core.exception.JsonRpcInternalException; import org.tron.core.exception.JsonRpcInvalidParamsException; import org.tron.core.services.NodeInfoService; import org.tron.core.services.interfaceJsonRpcOnPBFT.JsonRpcServiceOnPBFT; @@ -44,6 +48,7 @@ import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.core.services.jsonrpc.filters.LogFilterWrapper; import org.tron.core.services.jsonrpc.types.BlockResult; +import org.tron.core.services.jsonrpc.types.TransactionReceipt; import org.tron.core.services.jsonrpc.types.TransactionResult; import org.tron.protos.Protocol; import org.tron.protos.Protocol.Transaction.Contract.ContractType; @@ -155,6 +160,43 @@ public void init() { dbManager.getTransactionStore() .put(transactionCapsule3.getTransactionId().getBytes(), transactionCapsule3); + dbManager.getTransactionStore() + .put(transactionCapsule3.getTransactionId().getBytes(), transactionCapsule3); + + blockCapsule0.getTransactions().forEach(tx -> { + TransactionCapsule transactionCapsule = new TransactionCapsule(tx.getInstance()); + transactionCapsule.setBlockNum(blockCapsule0.getNum()); + dbManager.getTransactionStore() + .put(transactionCapsule.getTransactionId().getBytes(), transactionCapsule); + }); + + TransactionRetCapsule transactionRetCapsule0 = new TransactionRetCapsule(); + blockCapsule0.getTransactions().forEach(tx -> { + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); + transactionInfoCapsule.setBlockNumber(blockCapsule0.getNum()); + transactionRetCapsule0.addTransactionInfo(transactionInfoCapsule.getInstance()); + }); + dbManager.getTransactionRetStore().put(ByteArray.fromLong(blockCapsule0.getNum()), transactionRetCapsule0); + + TransactionRetCapsule transactionRetCapsule1 = new TransactionRetCapsule(); + blockCapsule1.getTransactions().forEach(tx -> { + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); + transactionInfoCapsule.setBlockNumber(blockCapsule1.getNum()); + transactionRetCapsule1.addTransactionInfo(transactionInfoCapsule.getInstance()); + }); + dbManager.getTransactionRetStore().put(ByteArray.fromLong(blockCapsule1.getNum()), transactionRetCapsule1); + + TransactionRetCapsule transactionRetCapsule2 = new TransactionRetCapsule(); + blockCapsule2.getTransactions().forEach(tx -> { + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); + transactionInfoCapsule.setBlockNumber(blockCapsule2.getNum()); + transactionRetCapsule2.addTransactionInfo(transactionInfoCapsule.getInstance()); + }); + dbManager.getTransactionRetStore().put(ByteArray.fromLong(blockCapsule2.getNum()), transactionRetCapsule2); + tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); } @@ -968,4 +1010,53 @@ public void testNewFilterFinalizedBlock() { Assert.assertEquals("invalid block range params", e.getMessage()); } } + + @Test + public void testGetBlockReceipts() { + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("0x2710"); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("earliest"); + System.out.println(transactionReceiptList); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("latest"); + System.out.println(transactionReceiptList); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("finalized"); + Assert.assertFalse(transactionReceiptList.isEmpty()); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + + try { + tronJsonRpc.getBlockReceipts("pending"); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals(TAG_PENDING_SUPPORT_ERROR, e.getMessage()); + } + + try { + tronJsonRpc.getBlockReceipts("test"); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals("invalid block number", e.getMessage()); + } + + } } From 3e95e9e3992955560480b9512e9149f61a357878 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Fri, 16 May 2025 12:49:19 +0800 Subject: [PATCH 02/12] improve getBlockReceipts --- .../core/services/jsonrpc/TronJsonRpcImpl.java | 14 +++++++++----- .../org/tron/core/jsonrpc/JsonrpcServiceTest.java | 9 +++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 43824ac506f..e0645efc081 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -784,19 +784,23 @@ public TransactionReceipt getTransactionReceipt(String txId) @Override public List getBlockReceipts(String blockNumOrTag) throws JsonRpcInvalidParamsException, JsonRpcInternalException { - BlockResult blockResult = ethGetBlockByNumber(blockNumOrTag, true); + Block block = wallet.getByJsonBlockId(blockNumOrTag); + if (block == null) { + return null; + } List transactionReceiptList = new ArrayList<>(); - for (Object o: blockResult.getTransactions()){ - if (o instanceof TransactionResult) { - String txHash = ((TransactionResult) o).getHash(); + for (Transaction transaction: block.getTransactionsList()){ + TransactionCapsule transactionCapsule = new TransactionCapsule(transaction); + byte[] txId = transactionCapsule.getTransactionId().getBytes(); + String txHash = ByteArray.toJsonHex(txId); TransactionReceipt transactionReceipt = getTransactionReceipt(txHash); if (transactionReceipt == null) { throw new JsonRpcInternalException("transactionReceipt is null, txHash is " + txHash); } transactionReceiptList.add(transactionReceipt); - } + } return transactionReceiptList; diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index 945ca83bb14..d5c9548deb6 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -1023,7 +1023,6 @@ public void testGetBlockReceipts() { try { List transactionReceiptList = tronJsonRpc.getBlockReceipts("earliest"); - System.out.println(transactionReceiptList); Assert.assertFalse(transactionReceiptList.isEmpty()); } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { throw new RuntimeException(e); @@ -1031,7 +1030,6 @@ public void testGetBlockReceipts() { try { List transactionReceiptList = tronJsonRpc.getBlockReceipts("latest"); - System.out.println(transactionReceiptList); Assert.assertFalse(transactionReceiptList.isEmpty()); } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { throw new RuntimeException(e); @@ -1058,5 +1056,12 @@ public void testGetBlockReceipts() { Assert.assertEquals("invalid block number", e.getMessage()); } + try { + List transactionReceiptList = tronJsonRpc.getBlockReceipts("0x2"); + Assert.assertNull(transactionReceiptList); + } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { + throw new RuntimeException(e); + } + } } From 41ce13a80b91c86aaafb920af5f67f1a1ac465f9 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Mon, 26 May 2025 14:10:34 +0800 Subject: [PATCH 03/12] fix checkStyle --- .../org/tron/core/services/jsonrpc/TronJsonRpc.java | 4 ++-- .../tron/core/services/jsonrpc/TronJsonRpcImpl.java | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 1eaf48017c2..5a07840ff26 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -148,8 +148,8 @@ TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTag, Stri @JsonRpcMethod("eth_getBlockReceipts") @JsonRpcErrors({ - @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), - @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}") + @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), + @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}") }) List getBlockReceipts(String bnOrId) throws JsonRpcInvalidParamsException, JsonRpcInternalException; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index e0645efc081..c4e8d360cf6 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -795,11 +795,11 @@ public List getBlockReceipts(String blockNumOrTag) byte[] txId = transactionCapsule.getTransactionId().getBytes(); String txHash = ByteArray.toJsonHex(txId); - TransactionReceipt transactionReceipt = getTransactionReceipt(txHash); - if (transactionReceipt == null) { - throw new JsonRpcInternalException("transactionReceipt is null, txHash is " + txHash); - } - transactionReceiptList.add(transactionReceipt); + TransactionReceipt transactionReceipt = getTransactionReceipt(txHash); + if (transactionReceipt == null) { + throw new JsonRpcInternalException("transactionReceipt is null, txHash is " + txHash); + } + transactionReceiptList.add(transactionReceipt); } From 897ffed83e8d2bf274ddf1832191cca2b79adfd0 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Wed, 18 Jun 2025 14:18:16 +0800 Subject: [PATCH 04/12] feat(api): improve getBlockReceipts performance --- .../services/jsonrpc/TronJsonRpcImpl.java | 129 +++++++++++++++--- .../jsonrpc/types/TransactionReceipt.java | 4 + .../tron/core/jsonrpc/JsonrpcServiceTest.java | 30 +++- 3 files changed, 144 insertions(+), 19 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index c4e8d360cf6..fd7ea827ca5 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -4,8 +4,10 @@ import static org.tron.core.services.http.Util.setTransactionExtraData; import static org.tron.core.services.http.Util.setTransactionPermissionId; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.addressCompatibleToByteArray; +import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.convertToTronAddress; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.generateFilterId; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getEnergyUsageTotal; +import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getToAddress; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getTransactionIndex; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getTxID; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.triggerCallContract; @@ -39,6 +41,7 @@ import org.tron.api.GrpcAPI.Return; import org.tron.api.GrpcAPI.Return.response_code; import org.tron.api.GrpcAPI.TransactionExtention; +import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.crypto.Hash; import org.tron.common.es.ExecutorServiceManager; import org.tron.common.logsfilter.ContractEventParser; @@ -81,8 +84,10 @@ import org.tron.core.store.StorageRowStore; import org.tron.core.vm.program.Storage; import org.tron.program.Version; +import org.tron.protos.Protocol; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; +import org.tron.protos.Protocol.ResourceReceipt; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result.code; @@ -788,22 +793,7 @@ public List getBlockReceipts(String blockNumOrTag) if (block == null) { return null; } - List transactionReceiptList = new ArrayList<>(); - - for (Transaction transaction: block.getTransactionsList()){ - TransactionCapsule transactionCapsule = new TransactionCapsule(transaction); - byte[] txId = transactionCapsule.getTransactionId().getBytes(); - String txHash = ByteArray.toJsonHex(txId); - - TransactionReceipt transactionReceipt = getTransactionReceipt(txHash); - if (transactionReceipt == null) { - throw new JsonRpcInternalException("transactionReceipt is null, txHash is " + txHash); - } - transactionReceiptList.add(transactionReceipt); - - } - - return transactionReceiptList; + return getTransactionReceipts(block, wallet); } @Override @@ -1418,6 +1408,113 @@ public static Object[] getFilterResult(String filterId, Map getTransactionReceipts(Block block, Wallet wallet) + throws JsonRpcInternalException { + List blockReceipts = new ArrayList<>(); + + BlockCapsule blockCapsule = new BlockCapsule(block); + long blockNum = blockCapsule.getNum(); + + // Get TransactionInfo for all transactions in the block + TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); + + if (infoList == null) { + return null; + } + + // Calculate cumulativeGas and cumulativeLogCount + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + // Iterate through TransactionInfoList + for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { + TransactionInfo txInfo = infoList.getTransactionInfo(index); + + // Get corresponding transaction from block + Protocol.Transaction transaction = block.getTransactions(index); + if (transaction == null) { + throw new JsonRpcInternalException( + String.format("Transaction not found at index %d in block %s", + index, ByteArray.toHexString(blockCapsule.getBlockId().getBytes()))); + } + + // Verify TransactionInfo and Transaction are the same transaction + TransactionCapsule txCapsule = new TransactionCapsule(transaction); + String txInfoId = ByteArray.toHexString(txInfo.getId().toByteArray()); + String txId = ByteArray.toHexString(txCapsule.getTransactionId().getBytes()); + if (!txInfoId.equals(txId)) { + logger.error("Transaction ID mismatch at index {}: txInfo={}, tx={}", + index, txInfoId, txId); + throw new JsonRpcInternalException( + String.format("Transaction ID mismatch at index %d: txInfo=%s, tx=%s", + index, txInfoId, txId)); + } + + // Update cumulativeGas and cumulativeLogCount + ResourceReceipt resourceReceipt = txInfo.getReceipt(); + long energyUsage = resourceReceipt.getEnergyUsageTotal(); + cumulativeGas += energyUsage; + + // Create TransactionReceipt + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setBlockHash(ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes())); + receipt.setBlockNumber(ByteArray.toJsonHex(blockCapsule.getNum())); + receipt.setTransactionHash(ByteArray.toJsonHex(txInfo.getId().toByteArray())); + receipt.setTransactionIndex(ByteArray.toJsonHex(index)); + receipt.setFrom(ByteArray.toJsonHexAddress( + TransactionCapsule.getOwner(transaction.getRawData().getContract(0)))); + receipt.setTo(ByteArray.toJsonHexAddress(getToAddress(transaction))); + receipt.setCumulativeGasUsed(ByteArray.toJsonHex(cumulativeGas)); + receipt.setGasUsed(ByteArray.toJsonHex(energyUsage)); + receipt.status = resourceReceipt.getResultValue() <= 1 ? "0x1" : "0x0"; + receipt.setEffectiveGasPrice( + ByteArray.toJsonHex(wallet.getEnergyFee(blockCapsule.getTimeStamp()))); + + // Set contract address for contract creation transactions + if (transaction.getRawData().getContract(0).getType() == ContractType.CreateSmartContract) { + receipt.setContractAddress( + ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray())); + } + + // Process transaction logs + List logList = new ArrayList<>(); + for (int logIndex = 0; logIndex < txInfo.getLogCount(); logIndex++) { + TransactionInfo.Log log = txInfo.getLogList().get(logIndex); + TransactionReceipt.TransactionLog transactionLog = new TransactionReceipt.TransactionLog(); + transactionLog.setLogIndex(ByteArray.toJsonHex(logIndex + cumulativeLogCount)); + transactionLog.setTransactionHash(receipt.getTransactionHash()); + transactionLog.setTransactionIndex(receipt.getTransactionIndex()); + transactionLog.setBlockHash(receipt.getBlockHash()); + transactionLog.setBlockNumber(receipt.getBlockNumber()); + byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); + transactionLog.setAddress(ByteArray.toJsonHexAddress(addressByte)); + transactionLog.setData(ByteArray.toJsonHex(log.getData().toByteArray())); + + String[] topics = new String[log.getTopicsCount()]; + for (int i = 0; i < log.getTopicsCount(); i++) { + topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); + } + transactionLog.setTopics(topics); + logList.add(transactionLog); + } + receipt.setLogs(logList.toArray(new TransactionReceipt.TransactionLog[logList.size()])); + receipt.setLogsBloom(ByteArray.toJsonHex(new byte[256])); // No value + receipt.root = null; + receipt.setType("0x0"); + + cumulativeLogCount += txInfo.getLogCount(); + blockReceipts.add(receipt); + } + return blockReceipts; + } + @Override public void close() throws IOException { ExecutorServiceManager.shutdownAndAwaitTermination(sectionExecutor, esName); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index 81b7c763cca..d97e6ae3a63 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -23,6 +23,10 @@ @JsonPropertyOrder(alphabetic = true) public class TransactionReceipt { + public TransactionReceipt() { + + } + @JsonPropertyOrder(alphabetic = true) public static class TransactionLog { diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index d5c9548deb6..36c602b2569 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -132,6 +132,7 @@ public void init() { (Wallet.getAddressPreFixString() + "ED738B3A0FE390EAA71B768B6D02CDBD18FB207B")))) .build(); + transactionCapsule1 = new TransactionCapsule(transferContract1, ContractType.TransferContract); transactionCapsule1.setBlockNum(blockCapsule1.getNum()); TransactionCapsule transactionCapsule2 = new TransactionCapsule(transferContract2, @@ -177,16 +178,31 @@ public void init() { transactionInfoCapsule.setBlockNumber(blockCapsule0.getNum()); transactionRetCapsule0.addTransactionInfo(transactionInfoCapsule.getInstance()); }); - dbManager.getTransactionRetStore().put(ByteArray.fromLong(blockCapsule0.getNum()), transactionRetCapsule0); + dbManager.getTransactionRetStore().put( + ByteArray.fromLong(blockCapsule0.getNum()), transactionRetCapsule0); + + List logs = new ArrayList<>(); + logs.add(Protocol.TransactionInfo.Log.newBuilder() + .setAddress(ByteString.copyFrom("address1".getBytes())) + .setData(ByteString.copyFrom("data1".getBytes())) + .addTopics(ByteString.copyFrom("topic1".getBytes())) + .build()); + logs.add(Protocol.TransactionInfo.Log.newBuilder() + .setAddress(ByteString.copyFrom("address2".getBytes())) + .setData(ByteString.copyFrom("data2".getBytes())) + .addTopics(ByteString.copyFrom("topic2".getBytes())) + .build()); TransactionRetCapsule transactionRetCapsule1 = new TransactionRetCapsule(); blockCapsule1.getTransactions().forEach(tx -> { TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); transactionInfoCapsule.setId(tx.getTransactionId().getBytes()); transactionInfoCapsule.setBlockNumber(blockCapsule1.getNum()); + transactionInfoCapsule.addAllLog(logs); transactionRetCapsule1.addTransactionInfo(transactionInfoCapsule.getInstance()); }); - dbManager.getTransactionRetStore().put(ByteArray.fromLong(blockCapsule1.getNum()), transactionRetCapsule1); + dbManager.getTransactionRetStore() + .put(ByteArray.fromLong(blockCapsule1.getNum()), transactionRetCapsule1); TransactionRetCapsule transactionRetCapsule2 = new TransactionRetCapsule(); blockCapsule2.getTransactions().forEach(tx -> { @@ -195,7 +211,8 @@ public void init() { transactionInfoCapsule.setBlockNumber(blockCapsule2.getNum()); transactionRetCapsule2.addTransactionInfo(transactionInfoCapsule.getInstance()); }); - dbManager.getTransactionRetStore().put(ByteArray.fromLong(blockCapsule2.getNum()), transactionRetCapsule2); + dbManager.getTransactionRetStore() + .put(ByteArray.fromLong(blockCapsule2.getNum()), transactionRetCapsule2); tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); } @@ -1017,6 +1034,13 @@ public void testGetBlockReceipts() { try { List transactionReceiptList = tronJsonRpc.getBlockReceipts("0x2710"); Assert.assertFalse(transactionReceiptList.isEmpty()); + for (TransactionReceipt transactionReceipt: transactionReceiptList) { + TransactionReceipt transactionReceipt1 + = tronJsonRpc.getTransactionReceipt(transactionReceipt.getTransactionHash()); + + Assert.assertEquals( + JSON.toJSONString(transactionReceipt), JSON.toJSONString(transactionReceipt1)); + } } catch (JsonRpcInvalidParamsException | JsonRpcInternalException e) { throw new RuntimeException(e); } From 70c5cdff01dc4d68d19a9dd995230c1c2935d3b2 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Fri, 20 Jun 2025 16:18:08 +0800 Subject: [PATCH 05/12] feat(api): add TransactionReceiptFactory.java and optimize code --- .../services/jsonrpc/TronJsonRpcImpl.java | 140 ++------ .../jsonrpc/types/TransactionReceipt.java | 109 ++----- .../types/TransactionReceiptFactory.java | 303 ++++++++++++++++++ 3 files changed, 363 insertions(+), 189 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index fd7ea827ca5..3e2bdf4b007 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -80,6 +80,7 @@ import org.tron.core.services.jsonrpc.types.BuildArguments; import org.tron.core.services.jsonrpc.types.CallArguments; import org.tron.core.services.jsonrpc.types.TransactionReceipt; +import org.tron.core.services.jsonrpc.types.TransactionReceiptFactory; import org.tron.core.services.jsonrpc.types.TransactionResult; import org.tron.core.store.StorageRowStore; import org.tron.core.vm.program.Storage; @@ -87,7 +88,6 @@ import org.tron.protos.Protocol; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; -import org.tron.protos.Protocol.ResourceReceipt; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result.code; @@ -769,6 +769,13 @@ public TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTa return getTransactionByBlockAndIndex(block, index); } + /** + * Get a transaction receipt by transaction hash + * + * @param txId the transaction hash in hex format (with or without 0x prefix) + * @return TransactionReceipt object for the specified transaction, or null if not found + * @throws JsonRpcInvalidParamsException if the transaction hash format is invalid + */ @Override public TransactionReceipt getTransactionReceipt(String txId) throws JsonRpcInvalidParamsException { @@ -783,9 +790,23 @@ public TransactionReceipt getTransactionReceipt(String txId) return null; } - return new TransactionReceipt(block, transactionInfo, wallet); + BlockCapsule blockCapsule = new BlockCapsule(block); + long blockNum = blockCapsule.getNum(); + TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + + return TransactionReceiptFactory.createFromBlockAndTxInfo( + blockCapsule, transactionInfo, infoList, energyFee); } + /** + * Get all transaction receipts for a specific block + * @param blockNumOrTag the block number or tag (latest, earliest, pending) + * @return List of TransactionReceipt objects for all transactions in the block, + * null if block not found + * @throws JsonRpcInvalidParamsException if the parameter format is invalid + * @throws JsonRpcInternalException if there's an internal error + */ @Override public List getBlockReceipts(String blockNumOrTag) throws JsonRpcInvalidParamsException, JsonRpcInternalException { @@ -793,7 +814,13 @@ public List getBlockReceipts(String blockNumOrTag) if (block == null) { return null; } - return getTransactionReceipts(block, wallet); + + BlockCapsule blockCapsule = new BlockCapsule(block); + long blockNum = blockCapsule.getNum(); + TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + + return TransactionReceiptFactory.createFromBlock(blockCapsule, infoList, energyFee); } @Override @@ -1408,113 +1435,6 @@ public static Object[] getFilterResult(String filterId, Map getTransactionReceipts(Block block, Wallet wallet) - throws JsonRpcInternalException { - List blockReceipts = new ArrayList<>(); - - BlockCapsule blockCapsule = new BlockCapsule(block); - long blockNum = blockCapsule.getNum(); - - // Get TransactionInfo for all transactions in the block - TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); - - if (infoList == null) { - return null; - } - - // Calculate cumulativeGas and cumulativeLogCount - long cumulativeGas = 0; - long cumulativeLogCount = 0; - - // Iterate through TransactionInfoList - for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { - TransactionInfo txInfo = infoList.getTransactionInfo(index); - - // Get corresponding transaction from block - Protocol.Transaction transaction = block.getTransactions(index); - if (transaction == null) { - throw new JsonRpcInternalException( - String.format("Transaction not found at index %d in block %s", - index, ByteArray.toHexString(blockCapsule.getBlockId().getBytes()))); - } - - // Verify TransactionInfo and Transaction are the same transaction - TransactionCapsule txCapsule = new TransactionCapsule(transaction); - String txInfoId = ByteArray.toHexString(txInfo.getId().toByteArray()); - String txId = ByteArray.toHexString(txCapsule.getTransactionId().getBytes()); - if (!txInfoId.equals(txId)) { - logger.error("Transaction ID mismatch at index {}: txInfo={}, tx={}", - index, txInfoId, txId); - throw new JsonRpcInternalException( - String.format("Transaction ID mismatch at index %d: txInfo=%s, tx=%s", - index, txInfoId, txId)); - } - - // Update cumulativeGas and cumulativeLogCount - ResourceReceipt resourceReceipt = txInfo.getReceipt(); - long energyUsage = resourceReceipt.getEnergyUsageTotal(); - cumulativeGas += energyUsage; - - // Create TransactionReceipt - TransactionReceipt receipt = new TransactionReceipt(); - receipt.setBlockHash(ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes())); - receipt.setBlockNumber(ByteArray.toJsonHex(blockCapsule.getNum())); - receipt.setTransactionHash(ByteArray.toJsonHex(txInfo.getId().toByteArray())); - receipt.setTransactionIndex(ByteArray.toJsonHex(index)); - receipt.setFrom(ByteArray.toJsonHexAddress( - TransactionCapsule.getOwner(transaction.getRawData().getContract(0)))); - receipt.setTo(ByteArray.toJsonHexAddress(getToAddress(transaction))); - receipt.setCumulativeGasUsed(ByteArray.toJsonHex(cumulativeGas)); - receipt.setGasUsed(ByteArray.toJsonHex(energyUsage)); - receipt.status = resourceReceipt.getResultValue() <= 1 ? "0x1" : "0x0"; - receipt.setEffectiveGasPrice( - ByteArray.toJsonHex(wallet.getEnergyFee(blockCapsule.getTimeStamp()))); - - // Set contract address for contract creation transactions - if (transaction.getRawData().getContract(0).getType() == ContractType.CreateSmartContract) { - receipt.setContractAddress( - ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray())); - } - - // Process transaction logs - List logList = new ArrayList<>(); - for (int logIndex = 0; logIndex < txInfo.getLogCount(); logIndex++) { - TransactionInfo.Log log = txInfo.getLogList().get(logIndex); - TransactionReceipt.TransactionLog transactionLog = new TransactionReceipt.TransactionLog(); - transactionLog.setLogIndex(ByteArray.toJsonHex(logIndex + cumulativeLogCount)); - transactionLog.setTransactionHash(receipt.getTransactionHash()); - transactionLog.setTransactionIndex(receipt.getTransactionIndex()); - transactionLog.setBlockHash(receipt.getBlockHash()); - transactionLog.setBlockNumber(receipt.getBlockNumber()); - byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); - transactionLog.setAddress(ByteArray.toJsonHexAddress(addressByte)); - transactionLog.setData(ByteArray.toJsonHex(log.getData().toByteArray())); - - String[] topics = new String[log.getTopicsCount()]; - for (int i = 0; i < log.getTopicsCount(); i++) { - topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); - } - transactionLog.setTopics(topics); - logList.add(transactionLog); - } - receipt.setLogs(logList.toArray(new TransactionReceipt.TransactionLog[logList.size()])); - receipt.setLogsBloom(ByteArray.toJsonHex(new byte[256])); // No value - receipt.root = null; - receipt.setType("0x0"); - - cumulativeLogCount += txInfo.getLogCount(); - blockReceipts.add(receipt); - } - return blockReceipts; - } - @Override public void close() throws IOException { ExecutorServiceManager.shutdownAndAwaitTermination(sectionExecutor, esName); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index d97e6ae3a63..c6dd0fcdd81 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -22,11 +22,6 @@ @JsonPropertyOrder(alphabetic = true) public class TransactionReceipt { - - public TransactionReceipt() { - - } - @JsonPropertyOrder(alphabetic = true) public static class TransactionLog { @@ -108,84 +103,40 @@ public TransactionLog() { @Setter private String type = "0x0"; + public TransactionReceipt() { + } + + /** + * @deprecated Use TransactionReceiptFactory.createFromBlockAndTxInfo() instead + */ + @Deprecated public TransactionReceipt(Protocol.Block block, TransactionInfo txInfo, Wallet wallet) { BlockCapsule blockCapsule = new BlockCapsule(block); - String txid = ByteArray.toHexString(txInfo.getId().toByteArray()); - long blockNum = blockCapsule.getNum(); - - Protocol.Transaction transaction = null; - long cumulativeGas = 0; - long cumulativeLogCount = 0; - - TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); - for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { - TransactionInfo info = infoList.getTransactionInfo(index); - ResourceReceipt resourceReceipt = info.getReceipt(); - - long energyUsage = resourceReceipt.getEnergyUsageTotal(); - cumulativeGas += energyUsage; + TransactionInfoList transactionInfoList + = wallet.getTransactionInfoByBlockNum(blockCapsule.getNum()); + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); - if (ByteArray.toHexString(info.getId().toByteArray()).equals(txid)) { - transactionIndex = ByteArray.toJsonHex(index); - cumulativeGasUsed = ByteArray.toJsonHex(cumulativeGas); - gasUsed = ByteArray.toJsonHex(energyUsage); - status = resourceReceipt.getResultValue() <= 1 ? "0x1" : "0x0"; + TransactionReceipt receipt = TransactionReceiptFactory.createFromBlockAndTxInfo( + blockCapsule, txInfo, transactionInfoList, energyFee); - transaction = block.getTransactions(index); - break; - } else { - cumulativeLogCount += info.getLogCount(); - } + if (receipt == null) { + return; } - - blockHash = ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes()); - blockNumber = ByteArray.toJsonHex(blockCapsule.getNum()); - transactionHash = ByteArray.toJsonHex(txInfo.getId().toByteArray()); - effectiveGasPrice = ByteArray.toJsonHex(wallet.getEnergyFee(blockCapsule.getTimeStamp())); - - from = null; - to = null; - contractAddress = null; - - if (transaction != null && !transaction.getRawData().getContractList().isEmpty()) { - Contract contract = transaction.getRawData().getContract(0); - byte[] fromByte = TransactionCapsule.getOwner(contract); - byte[] toByte = getToAddress(transaction); - from = ByteArray.toJsonHexAddress(fromByte); - to = ByteArray.toJsonHexAddress(toByte); - - if (contract.getType() == ContractType.CreateSmartContract) { - contractAddress = ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray()); - } - } - - // logs - List logList = new ArrayList<>(); - for (int index = 0; index < txInfo.getLogCount(); index++) { - TransactionInfo.Log log = txInfo.getLogList().get(index); - - TransactionReceipt.TransactionLog transactionLog = new TransactionReceipt.TransactionLog(); - // index is the index in the block - transactionLog.logIndex = ByteArray.toJsonHex(index + cumulativeLogCount); - transactionLog.transactionHash = transactionHash; - transactionLog.transactionIndex = transactionIndex; - transactionLog.blockHash = blockHash; - transactionLog.blockNumber = blockNumber; - byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); - transactionLog.address = ByteArray.toJsonHexAddress(addressByte); - transactionLog.data = ByteArray.toJsonHex(log.getData().toByteArray()); - - String[] topics = new String[log.getTopicsCount()]; - for (int i = 0; i < log.getTopicsCount(); i++) { - topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); - } - transactionLog.topics = topics; - - logList.add(transactionLog); - } - - logs = logList.toArray(new TransactionReceipt.TransactionLog[logList.size()]); - logsBloom = ByteArray.toJsonHex(new byte[256]); // no value - root = null; + // Copy all fields from the created receipt to this instance + this.blockHash = receipt.getBlockHash(); + this.blockNumber = receipt.getBlockNumber(); + this.transactionIndex = receipt.getTransactionIndex(); + this.transactionHash = receipt.getTransactionHash(); + this.from = receipt.getFrom(); + this.to = receipt.getTo(); + this.cumulativeGasUsed = receipt.getCumulativeGasUsed(); + this.effectiveGasPrice = receipt.getEffectiveGasPrice(); + this.gasUsed = receipt.getGasUsed(); + this.contractAddress = receipt.getContractAddress(); + this.logs = receipt.getLogs(); + this.logsBloom = receipt.getLogsBloom(); + this.root = receipt.root; + this.status = receipt.status; + this.type = receipt.getType(); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java new file mode 100644 index 00000000000..dafabb4a6f9 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java @@ -0,0 +1,303 @@ +package org.tron.core.services.jsonrpc.types; + +import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.convertToTronAddress; +import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getToAddress; + +import java.util.ArrayList; +import java.util.List; +import org.tron.api.GrpcAPI.TransactionInfoList; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.exception.JsonRpcInternalException; +import org.tron.protos.Protocol; +import org.tron.protos.Protocol.ResourceReceipt; +import org.tron.protos.Protocol.Transaction.Contract; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.Protocol.TransactionInfo; + +/** + * Factory class for creating TransactionReceipt instances + */ +public class TransactionReceiptFactory { + + /** + * Creates a single TransactionReceipt from block and transaction info + * This method finds the transaction context and creates a receipt with all necessary fields + * @param blockCapsule the block containing the transaction, used for set + * blockHash/blockNumber/address/from/to fields + * @param txInfo the transaction info containing execution details + * @param transactionInfoList the complete transaction info list in the block, + * used for cumulative calculations + * @param energyFee the energy fee at the block timestamp + * @return TransactionReceipt object, or null if the transaction is not found in the block + */ + public static TransactionReceipt createFromBlockAndTxInfo( + BlockCapsule blockCapsule, + TransactionInfo txInfo, + TransactionInfoList transactionInfoList, + long energyFee) { + String txId = ByteArray.toHexString(txInfo.getId().toByteArray()); + TransactionContext context = findTransactionContext(transactionInfoList, txId); + + // txId not in transactionInfoList + if (context == null) { + return null; + } + + return createReceipt(blockCapsule, txInfo, context, energyFee); + } + + /** + * Creates all TransactionReceipts from a block + * This method processes all transactions in the block + * and creates receipts with cumulative gas calculations + * @param blockCapsule the block containing transactions + * @param transactionInfoList the transaction info list for the block + * @param energyFee the energy fee for the block timestamp + * @return List of TransactionReceipt objects for all transactions in the block + */ + public static List createFromBlock( + BlockCapsule blockCapsule, + TransactionInfoList transactionInfoList, + long energyFee) throws JsonRpcInternalException { + + if (blockCapsule == null || transactionInfoList == null) { + return null; + } + + // Validate transaction list size consistency + int transactionSizeInBlock = blockCapsule.getTransactions().size(); + if (transactionInfoList.getTransactionInfoCount() != transactionSizeInBlock) { + throw new JsonRpcInternalException( + String.format("TransactionList size mismatch: " + + "block has %d transactions, but transactionInfoList has %d", + transactionSizeInBlock, transactionInfoList.getTransactionInfoCount())); + } + + List receipts = new ArrayList<>(); + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + for (int index = 0; index < transactionInfoList.getTransactionInfoCount(); index++) { + TransactionInfo info = transactionInfoList.getTransactionInfo(index); + ResourceReceipt resourceReceipt = info.getReceipt(); + + long energyUsage = resourceReceipt.getEnergyUsageTotal(); + cumulativeGas += energyUsage; + + TransactionContext context = new TransactionContext( + index, cumulativeGas, energyUsage, cumulativeLogCount); + + TransactionReceipt receipt = createReceipt(blockCapsule, info, context, energyFee); + receipts.add(receipt); + + cumulativeLogCount += info.getLogCount(); + } + + return receipts; + } + + /** + * Finds transaction context for a specific transaction ID within the block + * Calculates cumulative gas and log count up to the target transaction + * @param infoList the transaction info list for the block + * @param txId the transaction ID to search for + * @return TransactionContext containing index and cumulative values, or null if not found + */ + private static TransactionContext findTransactionContext(TransactionInfoList infoList, + String txId) { + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { + TransactionInfo info = infoList.getTransactionInfo(index); + ResourceReceipt resourceReceipt = info.getReceipt(); + + long energyUsage = resourceReceipt.getEnergyUsageTotal(); + cumulativeGas += energyUsage; + + if (ByteArray.toHexString(info.getId().toByteArray()).equals(txId)) { + return new TransactionContext(index, cumulativeGas, energyUsage, cumulativeLogCount); + } else { + cumulativeLogCount += info.getLogCount(); + } + } + return null; + } + + /** + * Creates a TransactionReceipt with the given context + * Orchestrates the creation process by calling all field-setting methods + * @param blockCapsule the block containing the transaction + * @param txInfo the transaction info + * @param context the transaction context with index and cumulative values + * @param energyFee the energy fee for the block + * @return fully populated TransactionReceipt object + */ + private static TransactionReceipt createReceipt( + BlockCapsule blockCapsule, + TransactionInfo txInfo, + TransactionContext context, + long energyFee) { + + TransactionReceipt receipt = new TransactionReceipt(); + + setBasicFields(receipt, blockCapsule, txInfo, context, energyFee); + setContractFields(receipt, blockCapsule, txInfo, context); + setLogs(receipt, txInfo, context); + setDefaultFields(receipt); + + return receipt; + } + + /** + * Sets basic fields of the receipt including block info, transaction hash, and gas usage + * @param receipt the receipt object to populate + * @param blockCapsule the block containing the transaction + * @param txInfo the transaction info + * @param context the transaction context with index and cumulative values + * @param energyFee the energy fee for the block + */ + private static void setBasicFields( + TransactionReceipt receipt, + BlockCapsule blockCapsule, + TransactionInfo txInfo, + TransactionContext context, + long energyFee) { + + receipt.setBlockHash(ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes())); + receipt.setBlockNumber(ByteArray.toJsonHex(blockCapsule.getNum())); + receipt.setTransactionHash(ByteArray.toJsonHex(txInfo.getId().toByteArray())); + receipt.setTransactionIndex(ByteArray.toJsonHex(context.index)); + receipt.setCumulativeGasUsed(ByteArray.toJsonHex(context.cumulativeGas)); + receipt.setGasUsed(ByteArray.toJsonHex(context.energyUsage)); + receipt.status = txInfo.getReceipt().getResultValue() <= 1 ? "0x1" : "0x0"; + receipt.setEffectiveGasPrice(ByteArray.toJsonHex(energyFee)); + } + + /** + * Sets contract-related fields including from/to addresses + * and contract address for smart contract creation + * @param receipt the receipt object to populate + * @param blockCapsule the block containing the transaction + * @param txInfo the transaction info + * @param context the transaction context with index + */ + private static void setContractFields( + TransactionReceipt receipt, + BlockCapsule blockCapsule, + TransactionInfo txInfo, + TransactionContext context) { + + receipt.setFrom(null); + receipt.setTo(null); + receipt.setContractAddress(null); + + TransactionCapsule txCapsule = blockCapsule.getTransactions().get(context.index); + + Protocol.Transaction transaction = txCapsule.getInstance(); + if (!transaction.getRawData().getContractList().isEmpty()) { + Contract contract = transaction.getRawData().getContract(0); + byte[] fromByte = TransactionCapsule.getOwner(contract); + byte[] toByte = getToAddress(transaction); + receipt.setFrom(ByteArray.toJsonHexAddress(fromByte)); + receipt.setTo(ByteArray.toJsonHexAddress(toByte)); + + if (contract.getType() == ContractType.CreateSmartContract) { + receipt.setContractAddress( + ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray())); + } + } + } + + /** + * Sets transaction logs by creating TransactionLog objects for each log entry + * @param receipt the receipt object to populate + * @param txInfo the transaction info containing log data + * @param context the transaction context with cumulative log count + */ + private static void setLogs( + TransactionReceipt receipt, + TransactionInfo txInfo, + TransactionContext context) { + + List logList = new ArrayList<>(); + for (int logIndex = 0; logIndex < txInfo.getLogCount(); logIndex++) { + TransactionInfo.Log log = txInfo.getLogList().get(logIndex); + TransactionReceipt.TransactionLog transactionLog + = createTransactionLog(log, receipt, logIndex, context); + logList.add(transactionLog); + } + receipt.setLogs(logList.toArray(new TransactionReceipt.TransactionLog[0])); + } + + /** + * Creates a single transaction log with all required fields + * @param log the log data from the transaction + * @param receipt the parent receipt for reference + * @param logIndex the index of this log within the transaction + * @param context the transaction context with cumulative log count + * @return populated TransactionLog object + */ + private static TransactionReceipt.TransactionLog createTransactionLog( + TransactionInfo.Log log, + TransactionReceipt receipt, + int logIndex, + TransactionContext context) { + + TransactionReceipt.TransactionLog transactionLog = new TransactionReceipt.TransactionLog(); + transactionLog.setLogIndex(ByteArray.toJsonHex(logIndex + context.cumulativeLogCount)); + transactionLog.setTransactionHash(receipt.getTransactionHash()); + transactionLog.setTransactionIndex(receipt.getTransactionIndex()); + transactionLog.setBlockHash(receipt.getBlockHash()); + transactionLog.setBlockNumber(receipt.getBlockNumber()); + + byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); + transactionLog.setAddress(ByteArray.toJsonHexAddress(addressByte)); + transactionLog.setData(ByteArray.toJsonHex(log.getData().toByteArray())); + + String[] topics = new String[log.getTopicsCount()]; + for (int i = 0; i < log.getTopicsCount(); i++) { + topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); + } + transactionLog.setTopics(topics); + + return transactionLog; + } + + /** + * Sets default fields that are not specific to the transaction + * @param receipt the receipt object to populate + */ + private static void setDefaultFields(TransactionReceipt receipt) { + receipt.setLogsBloom(ByteArray.toJsonHex(new byte[256])); // no value + receipt.root = null; + receipt.setType("0x0"); + } + + /** + * Context class to hold transaction creation parameters + * Contains index and cumulative values needed for receipt creation + */ + private static class TransactionContext { + final int index; + final long cumulativeGas; + final long energyUsage; + final long cumulativeLogCount; + + /** + * Creates a transaction context with the given parameters + * @param index the transaction index within the block + * @param cumulativeGas the cumulative gas used up to this transaction + * @param energyUsage the energy usage for this specific transaction + * @param cumulativeLogCount the cumulative log count up to this transaction + */ + TransactionContext(int index, long cumulativeGas, long energyUsage, long cumulativeLogCount) { + this.index = index; + this.cumulativeGas = cumulativeGas; + this.energyUsage = energyUsage; + this.cumulativeLogCount = cumulativeLogCount; + } + } +} \ No newline at end of file From b357630bc3a6fbe725a5c3dd4a995087a3e7bb04 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Fri, 20 Jun 2025 16:29:32 +0800 Subject: [PATCH 06/12] feat(api): remove Deprecated TransactionReceipt constructor function --- .../services/jsonrpc/TronJsonRpcImpl.java | 4 --- .../jsonrpc/types/TransactionReceipt.java | 33 ----------------- .../types/TransactionReceiptFactory.java | 11 +++--- .../jsonrpc/TransactionReceiptTest.java | 35 +++++++++++++++---- 4 files changed, 33 insertions(+), 50 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 3e2bdf4b007..099d15c7985 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -4,10 +4,8 @@ import static org.tron.core.services.http.Util.setTransactionExtraData; import static org.tron.core.services.http.Util.setTransactionPermissionId; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.addressCompatibleToByteArray; -import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.convertToTronAddress; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.generateFilterId; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getEnergyUsageTotal; -import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getToAddress; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getTransactionIndex; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getTxID; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.triggerCallContract; @@ -17,7 +15,6 @@ import com.google.protobuf.GeneratedMessageV3; import java.io.Closeable; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -85,7 +82,6 @@ import org.tron.core.store.StorageRowStore; import org.tron.core.vm.program.Storage; import org.tron.program.Version; -import org.tron.protos.Protocol; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; import org.tron.protos.Protocol.Transaction; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index c6dd0fcdd81..38fd35d27c6 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -106,37 +106,4 @@ public TransactionLog() { public TransactionReceipt() { } - /** - * @deprecated Use TransactionReceiptFactory.createFromBlockAndTxInfo() instead - */ - @Deprecated - public TransactionReceipt(Protocol.Block block, TransactionInfo txInfo, Wallet wallet) { - BlockCapsule blockCapsule = new BlockCapsule(block); - TransactionInfoList transactionInfoList - = wallet.getTransactionInfoByBlockNum(blockCapsule.getNum()); - long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); - - TransactionReceipt receipt = TransactionReceiptFactory.createFromBlockAndTxInfo( - blockCapsule, txInfo, transactionInfoList, energyFee); - - if (receipt == null) { - return; - } - // Copy all fields from the created receipt to this instance - this.blockHash = receipt.getBlockHash(); - this.blockNumber = receipt.getBlockNumber(); - this.transactionIndex = receipt.getTransactionIndex(); - this.transactionHash = receipt.getTransactionHash(); - this.from = receipt.getFrom(); - this.to = receipt.getTo(); - this.cumulativeGasUsed = receipt.getCumulativeGasUsed(); - this.effectiveGasPrice = receipt.getEffectiveGasPrice(); - this.gasUsed = receipt.getGasUsed(); - this.contractAddress = receipt.getContractAddress(); - this.logs = receipt.getLogs(); - this.logsBloom = receipt.getLogsBloom(); - this.root = receipt.root; - this.status = receipt.status; - this.type = receipt.getType(); - } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java index dafabb4a6f9..d9ca87c9862 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java @@ -87,7 +87,7 @@ public static List createFromBlock( cumulativeGas += energyUsage; TransactionContext context = new TransactionContext( - index, cumulativeGas, energyUsage, cumulativeLogCount); + index, cumulativeGas, cumulativeLogCount); TransactionReceipt receipt = createReceipt(blockCapsule, info, context, energyFee); receipts.add(receipt); @@ -118,7 +118,7 @@ private static TransactionContext findTransactionContext(TransactionInfoList inf cumulativeGas += energyUsage; if (ByteArray.toHexString(info.getId().toByteArray()).equals(txId)) { - return new TransactionContext(index, cumulativeGas, energyUsage, cumulativeLogCount); + return new TransactionContext(index, cumulativeGas, cumulativeLogCount); } else { cumulativeLogCount += info.getLogCount(); } @@ -171,7 +171,7 @@ private static void setBasicFields( receipt.setTransactionHash(ByteArray.toJsonHex(txInfo.getId().toByteArray())); receipt.setTransactionIndex(ByteArray.toJsonHex(context.index)); receipt.setCumulativeGasUsed(ByteArray.toJsonHex(context.cumulativeGas)); - receipt.setGasUsed(ByteArray.toJsonHex(context.energyUsage)); + receipt.setGasUsed(ByteArray.toJsonHex(txInfo.getReceipt().getEnergyUsageTotal())); receipt.status = txInfo.getReceipt().getResultValue() <= 1 ? "0x1" : "0x0"; receipt.setEffectiveGasPrice(ByteArray.toJsonHex(energyFee)); } @@ -283,20 +283,17 @@ private static void setDefaultFields(TransactionReceipt receipt) { private static class TransactionContext { final int index; final long cumulativeGas; - final long energyUsage; final long cumulativeLogCount; /** * Creates a transaction context with the given parameters * @param index the transaction index within the block * @param cumulativeGas the cumulative gas used up to this transaction - * @param energyUsage the energy usage for this specific transaction * @param cumulativeLogCount the cumulative log count up to this transaction */ - TransactionContext(int index, long cumulativeGas, long energyUsage, long cumulativeLogCount) { + TransactionContext(int index, long cumulativeGas, long cumulativeLogCount) { this.index = index; this.cumulativeGas = cumulativeGas; - this.energyUsage = energyUsage; this.cumulativeLogCount = cumulativeLogCount; } } diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java index f10526e30a4..5c9c57f031d 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java @@ -1,16 +1,22 @@ package org.tron.core.services.jsonrpc; +import com.alibaba.fastjson.JSON; import com.google.protobuf.ByteString; +import java.util.List; import javax.annotation.Resource; import org.junit.Assert; import org.junit.Test; +import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.BaseTest; import org.tron.common.utils.ByteArray; import org.tron.core.Constant; import org.tron.core.Wallet; +import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.JsonRpcInternalException; import org.tron.core.services.jsonrpc.types.TransactionReceipt; +import org.tron.core.services.jsonrpc.types.TransactionReceiptFactory; import org.tron.core.store.TransactionRetStore; import org.tron.protos.Protocol; @@ -27,7 +33,7 @@ public class TransactionReceiptTest extends BaseTest { } @Test - public void testTransactionReceipt() { + public void testTransactionReceipt() throws JsonRpcInternalException { Protocol.TransactionInfo transactionInfo = Protocol.TransactionInfo.newBuilder() .setId(ByteString.copyFrom("1".getBytes())) .setContractAddress(ByteString.copyFrom("address1".getBytes())) @@ -50,17 +56,34 @@ public void testTransactionReceipt() { raw.addContract(contract.build()); Protocol.Transaction transaction = Protocol.Transaction.newBuilder().setRawData(raw).build(); - TransactionReceipt transactionReceipt = new TransactionReceipt( - Protocol.Block.newBuilder().setBlockHeader( - Protocol.BlockHeader.newBuilder().setRawData( - Protocol.BlockHeader.raw.newBuilder().setNumber(1))).addTransactions( - transaction).build(), transactionInfo, wallet); + Protocol.Block block = Protocol.Block.newBuilder().setBlockHeader( + Protocol.BlockHeader.newBuilder().setRawData( + Protocol.BlockHeader.raw.newBuilder().setNumber(1))).addTransactions( + transaction).build(); + BlockCapsule blockCapsule = new BlockCapsule(block); + long blockNum = blockCapsule.getNum(); + TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); + long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + + TransactionReceipt transactionReceipt = TransactionReceiptFactory.createFromBlockAndTxInfo( + blockCapsule, transactionInfo, infoList, energyFee); + + Assert.assertNotNull(transactionReceipt); Assert.assertEquals(transactionReceipt.getBlockNumber(),"0x1"); Assert.assertEquals(transactionReceipt.getTransactionIndex(),"0x0"); Assert.assertEquals(transactionReceipt.getLogs().length,1); Assert.assertEquals(transactionReceipt.getBlockHash(), "0x0000000000000001464f071c8a336fd22eb5145dff1b245bda013ec89add8497"); + + + List transactionReceiptList = TransactionReceiptFactory.createFromBlock( + blockCapsule, infoList, energyFee); + + Assert.assertNotNull(transactionReceiptList); + Assert.assertEquals(transactionReceiptList.size(), 1); + Assert.assertEquals( + JSON.toJSONString(transactionReceiptList.get(0)), JSON.toJSONString(transactionReceipt)); } } From 8998701c1a14728a50c21b01f85a5e078da70ac1 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Thu, 26 Jun 2025 11:22:24 +0800 Subject: [PATCH 07/12] feat(api): del TransactionReceiptFactory.java and add new TransactionReceipt Constructor --- .../services/jsonrpc/TronJsonRpcImpl.java | 92 +++++- .../jsonrpc/types/TransactionReceipt.java | 159 ++++++---- .../types/TransactionReceiptFactory.java | 300 ------------------ .../jsonrpc/TransactionReceiptTest.java | 60 ++-- 4 files changed, 227 insertions(+), 384 deletions(-) delete mode 100644 framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 099d15c7985..95405979837 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -15,6 +15,7 @@ import com.google.protobuf.GeneratedMessageV3; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -77,13 +78,14 @@ import org.tron.core.services.jsonrpc.types.BuildArguments; import org.tron.core.services.jsonrpc.types.CallArguments; import org.tron.core.services.jsonrpc.types.TransactionReceipt; -import org.tron.core.services.jsonrpc.types.TransactionReceiptFactory; +import org.tron.core.services.jsonrpc.types.TransactionReceipt.TransactionContext; import org.tron.core.services.jsonrpc.types.TransactionResult; import org.tron.core.store.StorageRowStore; import org.tron.core.vm.program.Storage; import org.tron.program.Version; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; +import org.tron.protos.Protocol.ResourceReceipt; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.Transaction.Result.code; @@ -96,6 +98,7 @@ import org.tron.protos.contract.SmartContractOuterClass.SmartContractDataWrapper; import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract; + @Slf4j(topic = "API") @Component public class TronJsonRpcImpl implements TronJsonRpc, Closeable { @@ -788,11 +791,46 @@ public TransactionReceipt getTransactionReceipt(String txId) BlockCapsule blockCapsule = new BlockCapsule(block); long blockNum = blockCapsule.getNum(); - TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); + TransactionInfoList transactionInfoList = wallet.getTransactionInfoByBlockNum(blockNum); long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); - return TransactionReceiptFactory.createFromBlockAndTxInfo( - blockCapsule, transactionInfo, infoList, energyFee); + // Find transaction context + TransactionReceipt.TransactionContext context + = findTransactionContext(transactionInfoList, + transactionInfo.getId()); + + if (context == null) { + return null; // Transaction not found in block + } + + return new TransactionReceipt(blockCapsule, transactionInfo, context, energyFee); + } + + /** + * Finds transaction context for a specific transaction ID within the block + * Calculates cumulative gas and log count up to the target transaction + * @param infoList the transactionInfo list for the block + * @param txId the transaction ID + * @return TransactionContext containing index and cumulative values, or null if not found + */ + private TransactionContext findTransactionContext(TransactionInfoList infoList, + ByteString txId) { + + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { + TransactionInfo info = infoList.getTransactionInfo(index); + ResourceReceipt resourceReceipt = info.getReceipt(); + + if (info.getId().equals(txId)) { + return new TransactionContext(index, cumulativeGas, cumulativeLogCount); + } else { + cumulativeGas += resourceReceipt.getEnergyUsageTotal(); + cumulativeLogCount += info.getLogCount(); + } + } + return null; } /** @@ -813,10 +851,52 @@ public List getBlockReceipts(String blockNumOrTag) BlockCapsule blockCapsule = new BlockCapsule(block); long blockNum = blockCapsule.getNum(); - TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); + TransactionInfoList transactionInfoList = wallet.getTransactionInfoByBlockNum(blockNum); long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); - return TransactionReceiptFactory.createFromBlock(blockCapsule, infoList, energyFee); + // Validate transaction list size consistency + int transactionSizeInBlock = blockCapsule.getTransactions().size(); + if (transactionSizeInBlock != transactionInfoList.getTransactionInfoCount()) { + throw new JsonRpcInternalException( + String.format("TransactionList size mismatch: " + + "block has %d transactions, but transactionInfoList has %d", + transactionSizeInBlock, transactionInfoList.getTransactionInfoCount())); + } + + return getTransactionReceiptsFromBlock(blockCapsule, transactionInfoList, energyFee); + } + + /** + * Get all TransactionReceipts from a block + * This method processes all transactions in the block + * and creates receipts with cumulative gas calculations + * @param blockCapsule the block containing transactions + * @param transactionInfoList the transaction info list for the block + * @param energyFee the energy fee for the block timestamp + * @return List of TransactionReceipt objects for all transactions in the block + */ + private List getTransactionReceiptsFromBlock(BlockCapsule blockCapsule, + TransactionInfoList transactionInfoList, long energyFee) { + + List receipts = new ArrayList<>(); + long cumulativeGas = 0; + long cumulativeLogCount = 0; + + for (int index = 0; index < transactionInfoList.getTransactionInfoCount(); index++) { + TransactionInfo info = transactionInfoList.getTransactionInfo(index); + ResourceReceipt resourceReceipt = info.getReceipt(); + + TransactionReceipt.TransactionContext context = new TransactionContext( + index, cumulativeGas, cumulativeLogCount); + + // Use the constructor with pre-calculated context + TransactionReceipt receipt = new TransactionReceipt(blockCapsule, info, context, energyFee); + receipts.add(receipt); + + cumulativeGas += resourceReceipt.getEnergyUsageTotal();; + cumulativeLogCount += info.getLogCount(); + } + return receipts; } @Override diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index 38fd35d27c6..86a088ed03f 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -9,101 +9,154 @@ import java.util.List; import lombok.Getter; import lombok.Setter; -import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.utils.ByteArray; -import org.tron.core.Wallet; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionCapsule; import org.tron.protos.Protocol; -import org.tron.protos.Protocol.ResourceReceipt; import org.tron.protos.Protocol.Transaction.Contract; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.Protocol.TransactionInfo; +@Getter +@Setter @JsonPropertyOrder(alphabetic = true) public class TransactionReceipt { @JsonPropertyOrder(alphabetic = true) + @Getter + @Setter public static class TransactionLog { - @Getter - @Setter private String logIndex; - @Getter - @Setter private String blockHash; - @Getter - @Setter private String blockNumber; - @Getter - @Setter private String transactionIndex; - @Getter - @Setter private String transactionHash; - @Getter - @Setter private String address; - @Getter - @Setter private String data; - @Getter - @Setter private String[] topics; - @Getter - @Setter private boolean removed = false; - public TransactionLog() { - } + public TransactionLog() {} } - @Getter - @Setter private String blockHash; - @Getter - @Setter private String blockNumber; - @Getter - @Setter private String transactionIndex; - @Getter - @Setter private String transactionHash; - @Getter - @Setter private String from; - @Getter - @Setter private String to; - @Getter - @Setter private String cumulativeGasUsed; - @Getter - @Setter private String effectiveGasPrice; - @Getter - @Setter private String gasUsed; - @Getter - @Setter private String contractAddress; - @Getter - @Setter private TransactionLog[] logs; - @Getter - @Setter private String logsBloom; + @JsonInclude(JsonInclude.Include.NON_NULL) - public String root; // 32 bytes of post-transaction stateroot (pre Byzantium) + private String root; // 32 bytes of post-transaction stateroot (pre Byzantium) + @JsonInclude(JsonInclude.Include.NON_NULL) - public String status; // either 1 (success) or 0 (failure) (post Byzantium) + private String status; // either 1 (success) or 0 (failure) (post Byzantium) - @Getter - @Setter private String type = "0x0"; - public TransactionReceipt() { + /** + * Constructor for creating a TransactionReceipt + * + * @param blockCapsule the block containing the transaction + * @param txInfo the transaction info containing execution details + * @param context the pre-calculated transaction context + * @param energyFee the energy fee at the block timestamp + */ + public TransactionReceipt( + BlockCapsule blockCapsule, + TransactionInfo txInfo, + TransactionContext context, + long energyFee) { + // Set basic fields + this.blockHash = ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes()); + this.blockNumber = ByteArray.toJsonHex(blockCapsule.getNum()); + this.transactionHash = ByteArray.toJsonHex(txInfo.getId().toByteArray()); + this.transactionIndex = ByteArray.toJsonHex(context.index); + this.cumulativeGasUsed = + ByteArray.toJsonHex(context.cumulativeGas + txInfo.getReceipt().getEnergyUsageTotal()); + this.gasUsed = ByteArray.toJsonHex(txInfo.getReceipt().getEnergyUsageTotal()); + this.status = txInfo.getReceipt().getResultValue() <= 1 ? "0x1" : "0x0"; + this.effectiveGasPrice = ByteArray.toJsonHex(energyFee); + + // Set contract fields + this.from = null; + this.to = null; + this.contractAddress = null; + + TransactionCapsule txCapsule = blockCapsule.getTransactions().get(context.index); + Protocol.Transaction transaction = txCapsule.getInstance(); + if (!transaction.getRawData().getContractList().isEmpty()) { + Contract contract = transaction.getRawData().getContract(0); + byte[] fromByte = TransactionCapsule.getOwner(contract); + byte[] toByte = getToAddress(transaction); + this.from = ByteArray.toJsonHexAddress(fromByte); + this.to = ByteArray.toJsonHexAddress(toByte); + + if (contract.getType() == ContractType.CreateSmartContract) { + this.contractAddress = + ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray()); + } + } + + // Set logs + List logList = new ArrayList<>(); + for (int logIndex = 0; logIndex < txInfo.getLogCount(); logIndex++) { + TransactionInfo.Log log = txInfo.getLogList().get(logIndex); + TransactionLog transactionLog = new TransactionLog(); + transactionLog.setLogIndex(ByteArray.toJsonHex(logIndex + context.cumulativeLogCount)); + transactionLog.setTransactionHash(this.transactionHash); + transactionLog.setTransactionIndex(this.transactionIndex); + transactionLog.setBlockHash(this.blockHash); + transactionLog.setBlockNumber(this.blockNumber); + + byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); + transactionLog.setAddress(ByteArray.toJsonHexAddress(addressByte)); + transactionLog.setData(ByteArray.toJsonHex(log.getData().toByteArray())); + + String[] topics = new String[log.getTopicsCount()]; + for (int i = 0; i < log.getTopicsCount(); i++) { + topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); + } + transactionLog.setTopics(topics); + + logList.add(transactionLog); + } + this.logs = logList.toArray(new TransactionLog[0]); + + // Set default fields + this.logsBloom = ByteArray.toJsonHex(new byte[256]); // no value + this.root = null; + this.type = "0x0"; } -} \ No newline at end of file + /** + * Context class to hold transaction creation parameters Contains index and cumulative values + * needed for receipt creation + */ + @Getter + public static class TransactionContext { + private final int index; + private final long cumulativeGas; + private final long cumulativeLogCount; + + /** + * Creates a transaction context with the given parameters + * + * @param index the transaction index within the block + * @param cumulativeGas the cumulative gas used up to this transaction + * @param cumulativeLogCount the cumulative log count up to this transaction + */ + public TransactionContext(int index, long cumulativeGas, long cumulativeLogCount) { + this.index = index; + this.cumulativeGas = cumulativeGas; + this.cumulativeLogCount = cumulativeLogCount; + } + } +} diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java deleted file mode 100644 index d9ca87c9862..00000000000 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceiptFactory.java +++ /dev/null @@ -1,300 +0,0 @@ -package org.tron.core.services.jsonrpc.types; - -import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.convertToTronAddress; -import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.getToAddress; - -import java.util.ArrayList; -import java.util.List; -import org.tron.api.GrpcAPI.TransactionInfoList; -import org.tron.common.utils.ByteArray; -import org.tron.core.capsule.BlockCapsule; -import org.tron.core.capsule.TransactionCapsule; -import org.tron.core.exception.JsonRpcInternalException; -import org.tron.protos.Protocol; -import org.tron.protos.Protocol.ResourceReceipt; -import org.tron.protos.Protocol.Transaction.Contract; -import org.tron.protos.Protocol.Transaction.Contract.ContractType; -import org.tron.protos.Protocol.TransactionInfo; - -/** - * Factory class for creating TransactionReceipt instances - */ -public class TransactionReceiptFactory { - - /** - * Creates a single TransactionReceipt from block and transaction info - * This method finds the transaction context and creates a receipt with all necessary fields - * @param blockCapsule the block containing the transaction, used for set - * blockHash/blockNumber/address/from/to fields - * @param txInfo the transaction info containing execution details - * @param transactionInfoList the complete transaction info list in the block, - * used for cumulative calculations - * @param energyFee the energy fee at the block timestamp - * @return TransactionReceipt object, or null if the transaction is not found in the block - */ - public static TransactionReceipt createFromBlockAndTxInfo( - BlockCapsule blockCapsule, - TransactionInfo txInfo, - TransactionInfoList transactionInfoList, - long energyFee) { - String txId = ByteArray.toHexString(txInfo.getId().toByteArray()); - TransactionContext context = findTransactionContext(transactionInfoList, txId); - - // txId not in transactionInfoList - if (context == null) { - return null; - } - - return createReceipt(blockCapsule, txInfo, context, energyFee); - } - - /** - * Creates all TransactionReceipts from a block - * This method processes all transactions in the block - * and creates receipts with cumulative gas calculations - * @param blockCapsule the block containing transactions - * @param transactionInfoList the transaction info list for the block - * @param energyFee the energy fee for the block timestamp - * @return List of TransactionReceipt objects for all transactions in the block - */ - public static List createFromBlock( - BlockCapsule blockCapsule, - TransactionInfoList transactionInfoList, - long energyFee) throws JsonRpcInternalException { - - if (blockCapsule == null || transactionInfoList == null) { - return null; - } - - // Validate transaction list size consistency - int transactionSizeInBlock = blockCapsule.getTransactions().size(); - if (transactionInfoList.getTransactionInfoCount() != transactionSizeInBlock) { - throw new JsonRpcInternalException( - String.format("TransactionList size mismatch: " - + "block has %d transactions, but transactionInfoList has %d", - transactionSizeInBlock, transactionInfoList.getTransactionInfoCount())); - } - - List receipts = new ArrayList<>(); - long cumulativeGas = 0; - long cumulativeLogCount = 0; - - for (int index = 0; index < transactionInfoList.getTransactionInfoCount(); index++) { - TransactionInfo info = transactionInfoList.getTransactionInfo(index); - ResourceReceipt resourceReceipt = info.getReceipt(); - - long energyUsage = resourceReceipt.getEnergyUsageTotal(); - cumulativeGas += energyUsage; - - TransactionContext context = new TransactionContext( - index, cumulativeGas, cumulativeLogCount); - - TransactionReceipt receipt = createReceipt(blockCapsule, info, context, energyFee); - receipts.add(receipt); - - cumulativeLogCount += info.getLogCount(); - } - - return receipts; - } - - /** - * Finds transaction context for a specific transaction ID within the block - * Calculates cumulative gas and log count up to the target transaction - * @param infoList the transaction info list for the block - * @param txId the transaction ID to search for - * @return TransactionContext containing index and cumulative values, or null if not found - */ - private static TransactionContext findTransactionContext(TransactionInfoList infoList, - String txId) { - long cumulativeGas = 0; - long cumulativeLogCount = 0; - - for (int index = 0; index < infoList.getTransactionInfoCount(); index++) { - TransactionInfo info = infoList.getTransactionInfo(index); - ResourceReceipt resourceReceipt = info.getReceipt(); - - long energyUsage = resourceReceipt.getEnergyUsageTotal(); - cumulativeGas += energyUsage; - - if (ByteArray.toHexString(info.getId().toByteArray()).equals(txId)) { - return new TransactionContext(index, cumulativeGas, cumulativeLogCount); - } else { - cumulativeLogCount += info.getLogCount(); - } - } - return null; - } - - /** - * Creates a TransactionReceipt with the given context - * Orchestrates the creation process by calling all field-setting methods - * @param blockCapsule the block containing the transaction - * @param txInfo the transaction info - * @param context the transaction context with index and cumulative values - * @param energyFee the energy fee for the block - * @return fully populated TransactionReceipt object - */ - private static TransactionReceipt createReceipt( - BlockCapsule blockCapsule, - TransactionInfo txInfo, - TransactionContext context, - long energyFee) { - - TransactionReceipt receipt = new TransactionReceipt(); - - setBasicFields(receipt, blockCapsule, txInfo, context, energyFee); - setContractFields(receipt, blockCapsule, txInfo, context); - setLogs(receipt, txInfo, context); - setDefaultFields(receipt); - - return receipt; - } - - /** - * Sets basic fields of the receipt including block info, transaction hash, and gas usage - * @param receipt the receipt object to populate - * @param blockCapsule the block containing the transaction - * @param txInfo the transaction info - * @param context the transaction context with index and cumulative values - * @param energyFee the energy fee for the block - */ - private static void setBasicFields( - TransactionReceipt receipt, - BlockCapsule blockCapsule, - TransactionInfo txInfo, - TransactionContext context, - long energyFee) { - - receipt.setBlockHash(ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes())); - receipt.setBlockNumber(ByteArray.toJsonHex(blockCapsule.getNum())); - receipt.setTransactionHash(ByteArray.toJsonHex(txInfo.getId().toByteArray())); - receipt.setTransactionIndex(ByteArray.toJsonHex(context.index)); - receipt.setCumulativeGasUsed(ByteArray.toJsonHex(context.cumulativeGas)); - receipt.setGasUsed(ByteArray.toJsonHex(txInfo.getReceipt().getEnergyUsageTotal())); - receipt.status = txInfo.getReceipt().getResultValue() <= 1 ? "0x1" : "0x0"; - receipt.setEffectiveGasPrice(ByteArray.toJsonHex(energyFee)); - } - - /** - * Sets contract-related fields including from/to addresses - * and contract address for smart contract creation - * @param receipt the receipt object to populate - * @param blockCapsule the block containing the transaction - * @param txInfo the transaction info - * @param context the transaction context with index - */ - private static void setContractFields( - TransactionReceipt receipt, - BlockCapsule blockCapsule, - TransactionInfo txInfo, - TransactionContext context) { - - receipt.setFrom(null); - receipt.setTo(null); - receipt.setContractAddress(null); - - TransactionCapsule txCapsule = blockCapsule.getTransactions().get(context.index); - - Protocol.Transaction transaction = txCapsule.getInstance(); - if (!transaction.getRawData().getContractList().isEmpty()) { - Contract contract = transaction.getRawData().getContract(0); - byte[] fromByte = TransactionCapsule.getOwner(contract); - byte[] toByte = getToAddress(transaction); - receipt.setFrom(ByteArray.toJsonHexAddress(fromByte)); - receipt.setTo(ByteArray.toJsonHexAddress(toByte)); - - if (contract.getType() == ContractType.CreateSmartContract) { - receipt.setContractAddress( - ByteArray.toJsonHexAddress(txInfo.getContractAddress().toByteArray())); - } - } - } - - /** - * Sets transaction logs by creating TransactionLog objects for each log entry - * @param receipt the receipt object to populate - * @param txInfo the transaction info containing log data - * @param context the transaction context with cumulative log count - */ - private static void setLogs( - TransactionReceipt receipt, - TransactionInfo txInfo, - TransactionContext context) { - - List logList = new ArrayList<>(); - for (int logIndex = 0; logIndex < txInfo.getLogCount(); logIndex++) { - TransactionInfo.Log log = txInfo.getLogList().get(logIndex); - TransactionReceipt.TransactionLog transactionLog - = createTransactionLog(log, receipt, logIndex, context); - logList.add(transactionLog); - } - receipt.setLogs(logList.toArray(new TransactionReceipt.TransactionLog[0])); - } - - /** - * Creates a single transaction log with all required fields - * @param log the log data from the transaction - * @param receipt the parent receipt for reference - * @param logIndex the index of this log within the transaction - * @param context the transaction context with cumulative log count - * @return populated TransactionLog object - */ - private static TransactionReceipt.TransactionLog createTransactionLog( - TransactionInfo.Log log, - TransactionReceipt receipt, - int logIndex, - TransactionContext context) { - - TransactionReceipt.TransactionLog transactionLog = new TransactionReceipt.TransactionLog(); - transactionLog.setLogIndex(ByteArray.toJsonHex(logIndex + context.cumulativeLogCount)); - transactionLog.setTransactionHash(receipt.getTransactionHash()); - transactionLog.setTransactionIndex(receipt.getTransactionIndex()); - transactionLog.setBlockHash(receipt.getBlockHash()); - transactionLog.setBlockNumber(receipt.getBlockNumber()); - - byte[] addressByte = convertToTronAddress(log.getAddress().toByteArray()); - transactionLog.setAddress(ByteArray.toJsonHexAddress(addressByte)); - transactionLog.setData(ByteArray.toJsonHex(log.getData().toByteArray())); - - String[] topics = new String[log.getTopicsCount()]; - for (int i = 0; i < log.getTopicsCount(); i++) { - topics[i] = ByteArray.toJsonHex(log.getTopics(i).toByteArray()); - } - transactionLog.setTopics(topics); - - return transactionLog; - } - - /** - * Sets default fields that are not specific to the transaction - * @param receipt the receipt object to populate - */ - private static void setDefaultFields(TransactionReceipt receipt) { - receipt.setLogsBloom(ByteArray.toJsonHex(new byte[256])); // no value - receipt.root = null; - receipt.setType("0x0"); - } - - /** - * Context class to hold transaction creation parameters - * Contains index and cumulative values needed for receipt creation - */ - private static class TransactionContext { - final int index; - final long cumulativeGas; - final long cumulativeLogCount; - - /** - * Creates a transaction context with the given parameters - * @param index the transaction index within the block - * @param cumulativeGas the cumulative gas used up to this transaction - * @param cumulativeLogCount the cumulative log count up to this transaction - */ - TransactionContext(int index, long cumulativeGas, long cumulativeLogCount) { - this.index = index; - this.cumulativeGas = cumulativeGas; - this.cumulativeLogCount = cumulativeLogCount; - } - } -} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java index 5c9c57f031d..6a0746f8f47 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java @@ -1,12 +1,9 @@ package org.tron.core.services.jsonrpc; -import com.alibaba.fastjson.JSON; import com.google.protobuf.ByteString; -import java.util.List; import javax.annotation.Resource; import org.junit.Assert; import org.junit.Test; -import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.BaseTest; import org.tron.common.utils.ByteArray; import org.tron.core.Constant; @@ -16,20 +13,18 @@ import org.tron.core.config.args.Args; import org.tron.core.exception.JsonRpcInternalException; import org.tron.core.services.jsonrpc.types.TransactionReceipt; -import org.tron.core.services.jsonrpc.types.TransactionReceiptFactory; +import org.tron.core.services.jsonrpc.types.TransactionReceipt.TransactionContext; import org.tron.core.store.TransactionRetStore; import org.tron.protos.Protocol; public class TransactionReceiptTest extends BaseTest { - @Resource - private Wallet wallet; + @Resource private Wallet wallet; - @Resource - private TransactionRetStore transactionRetStore; + @Resource private TransactionRetStore transactionRetStore; static { - Args.setParam(new String[]{"-d", dbPath()}, Constant.TEST_CONF); + Args.setParam(new String[] {"-d", dbPath()}, Constant.TEST_CONF); } @Test @@ -38,7 +33,7 @@ public void testTransactionReceipt() throws JsonRpcInternalException { .setId(ByteString.copyFrom("1".getBytes())) .setContractAddress(ByteString.copyFrom("address1".getBytes())) .setReceipt(Protocol.ResourceReceipt.newBuilder() - .setEnergyUsageTotal(0L) + .setEnergyUsageTotal(100L) .setResult(Protocol.Transaction.Result.contractResult.DEFAULT) .build()) .addLog(Protocol.TransactionInfo.Log.newBuilder() @@ -62,28 +57,43 @@ public void testTransactionReceipt() throws JsonRpcInternalException { transaction).build(); BlockCapsule blockCapsule = new BlockCapsule(block); - long blockNum = blockCapsule.getNum(); - TransactionInfoList infoList = wallet.getTransactionInfoByBlockNum(blockNum); long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); + TransactionReceipt.TransactionContext context + = new TransactionContext(0, 2, 3); - TransactionReceipt transactionReceipt = TransactionReceiptFactory.createFromBlockAndTxInfo( - blockCapsule, transactionInfo, infoList, energyFee); + TransactionReceipt transactionReceipt = + new TransactionReceipt(blockCapsule, transactionInfo, context, energyFee); Assert.assertNotNull(transactionReceipt); - Assert.assertEquals(transactionReceipt.getBlockNumber(),"0x1"); - Assert.assertEquals(transactionReceipt.getTransactionIndex(),"0x0"); - Assert.assertEquals(transactionReceipt.getLogs().length,1); - Assert.assertEquals(transactionReceipt.getBlockHash(), - "0x0000000000000001464f071c8a336fd22eb5145dff1b245bda013ec89add8497"); + String blockHash = "0x0000000000000001464f071c8a336fd22eb5145dff1b245bda013ec89add8497"; + // assert basic fields + Assert.assertEquals(transactionReceipt.getBlockHash(), blockHash); + Assert.assertEquals(transactionReceipt.getBlockNumber(), "0x1"); + Assert.assertEquals(transactionReceipt.getTransactionHash(), "0x31"); + Assert.assertEquals(transactionReceipt.getTransactionIndex(), "0x0"); + Assert.assertEquals(transactionReceipt.getCumulativeGasUsed(), ByteArray.toJsonHex(102)); + Assert.assertEquals(transactionReceipt.getGasUsed(), ByteArray.toJsonHex(100)); + Assert.assertEquals(transactionReceipt.getEffectiveGasPrice(), ByteArray.toJsonHex(energyFee)); + Assert.assertEquals(transactionReceipt.getStatus(), "0x1"); - List transactionReceiptList = TransactionReceiptFactory.createFromBlock( - blockCapsule, infoList, energyFee); + // assert contract fields + Assert.assertEquals(transactionReceipt.getFrom(), ByteArray.toJsonHexAddress(new byte[0])); + Assert.assertEquals(transactionReceipt.getTo(), ByteArray.toJsonHexAddress(new byte[0])); + Assert.assertNull(transactionReceipt.getContractAddress()); - Assert.assertNotNull(transactionReceiptList); - Assert.assertEquals(transactionReceiptList.size(), 1); + // assert logs fields + Assert.assertEquals(transactionReceipt.getLogs().length, 1); + Assert.assertEquals(transactionReceipt.getLogs()[0].getLogIndex(), "0x3"); Assert.assertEquals( - JSON.toJSONString(transactionReceiptList.get(0)), JSON.toJSONString(transactionReceipt)); - } + transactionReceipt.getLogs()[0].getBlockHash(), blockHash); + Assert.assertEquals(transactionReceipt.getLogs()[0].getBlockNumber(), "0x1"); + Assert.assertEquals(transactionReceipt.getLogs()[0].getTransactionHash(), "0x31"); + Assert.assertEquals(transactionReceipt.getLogs()[0].getTransactionIndex(), "0x0"); + // assert default fields + Assert.assertNull(transactionReceipt.getRoot()); + Assert.assertEquals(transactionReceipt.getType(), "0x0"); + Assert.assertEquals(transactionReceipt.getLogsBloom(), ByteArray.toJsonHex(new byte[256])); + } } From af61a0d8eb9736e792c894d2ccf98327a79b381f Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Mon, 30 Jun 2025 12:19:17 +0800 Subject: [PATCH 08/12] feat(api): rename api params, add some comments for energyFee --- .../java/org/tron/core/services/jsonrpc/TronJsonRpc.java | 2 +- .../org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 6 ++++-- .../core/services/jsonrpc/types/TransactionReceipt.java | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 5a07840ff26..e90fb313866 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -151,7 +151,7 @@ TransactionResult getTransactionByBlockNumberAndIndex(String blockNumOrTag, Stri @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), @JsonRpcError(exception = JsonRpcInternalException.class, code = -32000, data = "{}") }) - List getBlockReceipts(String bnOrId) + List getBlockReceipts(String blockNumOrTag) throws JsonRpcInvalidParamsException, JsonRpcInternalException; @JsonRpcMethod("eth_call") diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 95405979837..a44b08b44c9 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -835,7 +835,7 @@ private TransactionContext findTransactionContext(TransactionInfoList infoList, /** * Get all transaction receipts for a specific block - * @param blockNumOrTag the block number or tag (latest, earliest, pending) + * @param blockNumOrTag the block number or tag (latest, earliest, pending, finalized) * @return List of TransactionReceipt objects for all transactions in the block, * null if block not found * @throws JsonRpcInvalidParamsException if the parameter format is invalid @@ -852,6 +852,8 @@ public List getBlockReceipts(String blockNumOrTag) BlockCapsule blockCapsule = new BlockCapsule(block); long blockNum = blockCapsule.getNum(); TransactionInfoList transactionInfoList = wallet.getTransactionInfoByBlockNum(blockNum); + + //energy price at the block timestamp long energyFee = wallet.getEnergyFee(blockCapsule.getTimeStamp()); // Validate transaction list size consistency @@ -872,7 +874,7 @@ public List getBlockReceipts(String blockNumOrTag) * and creates receipts with cumulative gas calculations * @param blockCapsule the block containing transactions * @param transactionInfoList the transaction info list for the block - * @param energyFee the energy fee for the block timestamp + * @param energyFee the energy price at the block timestamp * @return List of TransactionReceipt objects for all transactions in the block */ private List getTransactionReceiptsFromBlock(BlockCapsule blockCapsule, diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index 86a088ed03f..9af35384375 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -67,7 +67,7 @@ public TransactionLog() {} * @param blockCapsule the block containing the transaction * @param txInfo the transaction info containing execution details * @param context the pre-calculated transaction context - * @param energyFee the energy fee at the block timestamp + * @param energyFee the energy price at the block timestamp */ public TransactionReceipt( BlockCapsule blockCapsule, From 488284e7c94c0defe09078ac8e4e2d08e516eb9a Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Wed, 2 Jul 2025 14:17:40 +0800 Subject: [PATCH 09/12] feat(api): add comment --- .../org/tron/core/services/jsonrpc/types/TransactionReceipt.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index 9af35384375..e000ecb6347 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -79,6 +79,7 @@ public TransactionReceipt( this.blockNumber = ByteArray.toJsonHex(blockCapsule.getNum()); this.transactionHash = ByteArray.toJsonHex(txInfo.getId().toByteArray()); this.transactionIndex = ByteArray.toJsonHex(context.index); + // Compute cumulativeGasTillTxn this.cumulativeGasUsed = ByteArray.toJsonHex(context.cumulativeGas + txInfo.getReceipt().getEnergyUsageTotal()); this.gasUsed = ByteArray.toJsonHex(txInfo.getReceipt().getEnergyUsageTotal()); From e64fc1fe8fc78871d09fdc30a37568e992d964c6 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Thu, 3 Jul 2025 15:58:02 +0800 Subject: [PATCH 10/12] feat(style): assign default values at field declaration in TransactionReceipt --- .../services/jsonrpc/types/TransactionReceipt.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java index e000ecb6347..fd57ec0d9ad 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/types/TransactionReceipt.java @@ -51,15 +51,15 @@ public TransactionLog() {} private String gasUsed; private String contractAddress; private TransactionLog[] logs; - private String logsBloom; + private String logsBloom = ByteArray.toJsonHex(new byte[256]); // default no value; @JsonInclude(JsonInclude.Include.NON_NULL) - private String root; // 32 bytes of post-transaction stateroot (pre Byzantium) + private String root = null; // 32 bytes of post-transaction stateroot (pre Byzantium) @JsonInclude(JsonInclude.Include.NON_NULL) private String status; // either 1 (success) or 0 (failure) (post Byzantium) - private String type = "0x0"; + private String type = "0x0"; // legacy transaction, set 0 in java-tron /** * Constructor for creating a TransactionReceipt @@ -79,7 +79,7 @@ public TransactionReceipt( this.blockNumber = ByteArray.toJsonHex(blockCapsule.getNum()); this.transactionHash = ByteArray.toJsonHex(txInfo.getId().toByteArray()); this.transactionIndex = ByteArray.toJsonHex(context.index); - // Compute cumulativeGasTillTxn + // Compute cumulative gas until this transaction this.cumulativeGasUsed = ByteArray.toJsonHex(context.cumulativeGas + txInfo.getReceipt().getEnergyUsageTotal()); this.gasUsed = ByteArray.toJsonHex(txInfo.getReceipt().getEnergyUsageTotal()); @@ -131,10 +131,6 @@ public TransactionReceipt( } this.logs = logList.toArray(new TransactionLog[0]); - // Set default fields - this.logsBloom = ByteArray.toJsonHex(new byte[256]); // no value - this.root = null; - this.type = "0x0"; } /** From 9eef349553abaf09280cec86982c5bde61711d19 Mon Sep 17 00:00:00 2001 From: bladehan1 Date: Tue, 8 Jul 2025 18:43:01 +0800 Subject: [PATCH 11/12] fix(CheckStyle): Remove an empty statement --- .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index a44b08b44c9..eac6471d155 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -895,7 +895,7 @@ private List getTransactionReceiptsFromBlock(BlockCapsule bl TransactionReceipt receipt = new TransactionReceipt(blockCapsule, info, context, energyFee); receipts.add(receipt); - cumulativeGas += resourceReceipt.getEnergyUsageTotal();; + cumulativeGas += resourceReceipt.getEnergyUsageTotal(); cumulativeLogCount += info.getLogCount(); } return receipts; From 8a08f372bb5a5e9d0b04600cc837415ecc038522 Mon Sep 17 00:00:00 2001 From: 0xbigapple Date: Mon, 18 Aug 2025 18:16:13 +0800 Subject: [PATCH 12/12] Resolve merge conflict --- .../test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java | 3 +-- .../org/tron/core/services/jsonrpc/TransactionReceiptTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index 2d46852f4eb..f6f4a35c1b7 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -38,8 +38,7 @@ import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.capsule.utils.BlockUtil; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInternalException; -import org.tron.core.exception.JsonRpcInvalidParamsException; +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.interfaceJsonRpcOnPBFT.JsonRpcServiceOnPBFT; diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java index 6a0746f8f47..23bc11e293f 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/TransactionReceiptTest.java @@ -11,7 +11,7 @@ import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.config.args.Args; -import org.tron.core.exception.JsonRpcInternalException; +import org.tron.core.exception.jsonrpc.JsonRpcInternalException; import org.tron.core.services.jsonrpc.types.TransactionReceipt; import org.tron.core.services.jsonrpc.types.TransactionReceipt.TransactionContext; import org.tron.core.store.TransactionRetStore;