Skip to content
This repository was archived by the owner on Aug 27, 2025. It is now read-only.

Commit 4ca5ba8

Browse files
Filter duplicate Eth logs at api level
1 parent f0bcbb0 commit 4ca5ba8

File tree

4 files changed

+60
-16
lines changed

4 files changed

+60
-16
lines changed

src/libServer/EthRpcMethods.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,8 @@ Json::Value EthRpcMethods::GetEthTransactionReceipt(
17771777
Eth::GetLogsFromReceipt(transactionBodyPtr->GetTransactionReceipt());
17781778

17791779
logs = Eth::ConvertScillaEventsToEvm(logs);
1780+
Json::Value filteredLogs =
1781+
JSONUtils::GetInstance().FilterDuplicateLogs(logs);
17801782

17811783
const auto [errors, exceptions] = Eth::GetErrorsAndExceptionsFromReceipt(
17821784
transactionBodyPtr->GetTransactionReceipt());
@@ -1785,24 +1787,24 @@ Json::Value EthRpcMethods::GetEthTransactionReceipt(
17851787
Eth::ConvertScillaExceptionsToEvm(exceptions);
17861788

17871789
for (const auto &error : convertedErrors) {
1788-
logs.append(error);
1790+
filteredLogs.append(error);
17891791
}
17901792

17911793
for (const auto &exception : convertedExceptions) {
1792-
logs.append(exception);
1794+
filteredLogs.append(exception);
17931795
}
17941796

17951797
const auto baselogIndex =
17961798
Eth::GetBaseLogIndexForReceiptInBlock(argHash, txBlock);
17971799

1798-
Eth::DecorateReceiptLogs(logs, txnhash, blockHash, blockNumber,
1800+
Eth::DecorateReceiptLogs(filteredLogs, txnhash, blockHash, blockNumber,
17991801
transactionIndex, baselogIndex);
18001802
const auto bloomLogs = Eth::GetBloomFromReceiptHex(
18011803
transactionBodyPtr->GetTransactionReceipt());
18021804

18031805
auto res = Eth::populateReceiptHelper(
18041806
hashId, success, sender, toAddr, cumGas, gasPrice, blockHash,
1805-
blockNumber, contractAddress, logs, bloomLogs, transactionIndex,
1807+
blockNumber, contractAddress, filteredLogs, bloomLogs, transactionIndex,
18061808
transactionBodyPtr->GetTransaction());
18071809

18081810
return res;

src/libUtils/JsonUtils.h

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,28 +45,30 @@ class JSONUtils {
4545
std::unique_ptr<Json::StreamWriter>(writeBuilder.newStreamWriter());
4646
}
4747

48-
~JSONUtils(){};
48+
~JSONUtils() {};
4949

5050
public:
5151
static JSONUtils& GetInstance() {
5252
static JSONUtils jsonutils;
5353
return jsonutils;
5454
}
5555

56-
bool getUint128FromObject(const Json::Value& obj, const std::string& key, uint128_t &result) const {
56+
bool getUint128FromObject(const Json::Value& obj, const std::string& key,
57+
uint128_t& result) const {
5758
return this->getUint128FromObject(obj, key.c_str(), result);
5859
}
59-
60+
6061
/// Get an object value as a uint128_t.
6162
/// @return false if we failed, true if we succeeded.
62-
bool getUint128FromObject(const Json::Value& obj, const char* key, uint128_t &result) const {
63-
if (obj.isObject() &&
64-
obj.isMember(key)) {
63+
bool getUint128FromObject(const Json::Value& obj, const char* key,
64+
uint128_t& result) const {
65+
if (obj.isObject() && obj.isMember(key)) {
6566
Json::Value member = obj[key];
6667
if (member.isString()) {
6768
// Parse it.
6869
try {
69-
result = DataConversion::ConvertStrToInt<uint128_t>(member.asString());
70+
result =
71+
DataConversion::ConvertStrToInt<uint128_t>(member.asString());
7072
return true;
7173
} catch (...) {
7274
return false;
@@ -118,6 +120,33 @@ class JSONUtils {
118120
std::lock_guard<std::mutex> g(m_mutexWriter);
119121
m_writer->write(_json, &os);
120122
}
123+
124+
static std::size_t hashJsonValue(const Json::Value& log) {
125+
Json::StreamWriterBuilder writer;
126+
std::string logStr = Json::writeString(writer, log);
127+
return std::hash<std::string>{}(logStr);
128+
}
129+
130+
static bool equalJsonValue(const Json::Value& lhs, const Json::Value& rhs) {
131+
return lhs == rhs;
132+
}
133+
134+
Json::Value FilterDuplicateLogs(const Json::Value& logs) {
135+
using CustomHash = std::function<std::size_t(const Json::Value&)>;
136+
using CustomEqual =
137+
std::function<bool(const Json::Value&, const Json::Value&)>;
138+
139+
std::unordered_set<Json::Value, CustomHash, CustomEqual> uniqueLogs(
140+
10, hashJsonValue, equalJsonValue);
141+
Json::Value filteredLogs(Json::arrayValue);
142+
143+
for (const auto& log : logs) {
144+
if (uniqueLogs.insert(log).second) {
145+
filteredLogs.append(log);
146+
}
147+
}
148+
return filteredLogs;
149+
}
121150
};
122151

123152
#endif // ZILLIQA_SRC_LIBUTILS_JSONUTILS_H_

tests/EvmAcceptanceTests/contracts/Event.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,12 @@ contract Event {
3232
child.one_log();
3333
revert();
3434
}
35+
function duplicate_one_log() public {
36+
emit Log(msg.sender, "Hello World!");
37+
emit Log(msg.sender, "Hello World!");
38+
emit Log(msg.sender, "Hello World!");
39+
emit Log(msg.sender, "Hello World!");
40+
emit Log(msg.sender, "Hello World2!");
41+
emit Log(msg.sender, "Hello World3!");
42+
}
3543
}

tests/EvmAcceptanceTests/test/EventsLogs.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {expect} from "chai";
2-
import {Contract} from "ethers";
3-
import hre, {ethers} from "hardhat";
1+
import { expect } from "chai";
2+
import { Contract } from "ethers";
3+
import hre, { ethers } from "hardhat";
44

55
const FUND = ethers.utils.parseUnits("1", "gwei");
66

@@ -28,9 +28,14 @@ describe("Events and logs #parallel", function () {
2828
});
2929

3030
it("Should return log from child contract even if function failed @block-2", async function () {
31-
const tx = await contract.one_log_and_fail({gasLimit: 250000});
31+
const tx = await contract.one_log_and_fail({ gasLimit: 250000 });
3232
const receipt = await ethers.provider.getTransactionReceipt(tx.hash);
33-
console.log("Receipt: " + JSON.stringify(receipt));
3433
expect(receipt.logs.length).to.be.eq(1);
3534
});
35+
it("Should return 3 log whenever a function with duplicate one event is called @block-1", async function () {
36+
const tx = await contract.duplicate_one_log();
37+
const receipt = await ethers.provider.getTransactionReceipt(tx.hash);
38+
//console.log("Receipt = ", JSON.stringify(receipt));
39+
expect(receipt.logs.length).to.be.eq(3);
40+
});
3641
});

0 commit comments

Comments
 (0)