Skip to content
This repository was archived by the owner on Aug 27, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/libServer/EthRpcMethods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,8 @@ Json::Value EthRpcMethods::GetEthTransactionReceipt(
Eth::GetLogsFromReceipt(transactionBodyPtr->GetTransactionReceipt());

logs = Eth::ConvertScillaEventsToEvm(logs);
Json::Value filteredLogs =
JSONUtils::GetInstance().FilterDuplicateLogs(logs);

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

for (const auto &error : convertedErrors) {
logs.append(error);
filteredLogs.append(error);
}

for (const auto &exception : convertedExceptions) {
logs.append(exception);
filteredLogs.append(exception);
}

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

Eth::DecorateReceiptLogs(logs, txnhash, blockHash, blockNumber,
Eth::DecorateReceiptLogs(filteredLogs, txnhash, blockHash, blockNumber,
transactionIndex, baselogIndex);
const auto bloomLogs = Eth::GetBloomFromReceiptHex(
transactionBodyPtr->GetTransactionReceipt());

auto res = Eth::populateReceiptHelper(
hashId, success, sender, toAddr, cumGas, gasPrice, blockHash,
blockNumber, contractAddress, logs, bloomLogs, transactionIndex,
blockNumber, contractAddress, filteredLogs, bloomLogs, transactionIndex,
transactionBodyPtr->GetTransaction());

return res;
Expand Down
43 changes: 36 additions & 7 deletions src/libUtils/JsonUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,30 @@ class JSONUtils {
std::unique_ptr<Json::StreamWriter>(writeBuilder.newStreamWriter());
}

~JSONUtils(){};
~JSONUtils() {};

public:
static JSONUtils& GetInstance() {
static JSONUtils jsonutils;
return jsonutils;
}

bool getUint128FromObject(const Json::Value& obj, const std::string& key, uint128_t &result) const {
bool getUint128FromObject(const Json::Value& obj, const std::string& key,
uint128_t& result) const {
return this->getUint128FromObject(obj, key.c_str(), result);
}

/// Get an object value as a uint128_t.
/// @return false if we failed, true if we succeeded.
bool getUint128FromObject(const Json::Value& obj, const char* key, uint128_t &result) const {
if (obj.isObject() &&
obj.isMember(key)) {
bool getUint128FromObject(const Json::Value& obj, const char* key,
uint128_t& result) const {
if (obj.isObject() && obj.isMember(key)) {
Json::Value member = obj[key];
if (member.isString()) {
// Parse it.
try {
result = DataConversion::ConvertStrToInt<uint128_t>(member.asString());
result =
DataConversion::ConvertStrToInt<uint128_t>(member.asString());
return true;
} catch (...) {
return false;
Expand Down Expand Up @@ -118,6 +120,33 @@ class JSONUtils {
std::lock_guard<std::mutex> g(m_mutexWriter);
m_writer->write(_json, &os);
}

static std::size_t hashJsonValue(const Json::Value& log) {
Json::StreamWriterBuilder writer;
std::string logStr = Json::writeString(writer, log);
return std::hash<std::string>{}(logStr);
}

static bool equalJsonValue(const Json::Value& lhs, const Json::Value& rhs) {
return lhs == rhs;
}

Json::Value FilterDuplicateLogs(const Json::Value& logs) {
using CustomHash = std::function<std::size_t(const Json::Value&)>;
using CustomEqual =
std::function<bool(const Json::Value&, const Json::Value&)>;

std::unordered_set<Json::Value, CustomHash, CustomEqual> uniqueLogs(
10, hashJsonValue, equalJsonValue);
Json::Value filteredLogs(Json::arrayValue);

for (const auto& log : logs) {
if (uniqueLogs.insert(log).second) {
filteredLogs.append(log);
}
}
return filteredLogs;
}
};

#endif // ZILLIQA_SRC_LIBUTILS_JSONUTILS_H_
8 changes: 8 additions & 0 deletions tests/EvmAcceptanceTests/contracts/Event.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@ contract Event {
child.one_log();
revert();
}
function duplicate_one_log() public {
emit Log(msg.sender, "Hello World!");
emit Log(msg.sender, "Hello World!");
emit Log(msg.sender, "Hello World!");
emit Log(msg.sender, "Hello World!");
emit Log(msg.sender, "Hello World2!");
emit Log(msg.sender, "Hello World3!");
}
}
15 changes: 10 additions & 5 deletions tests/EvmAcceptanceTests/test/EventsLogs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {expect} from "chai";
import {Contract} from "ethers";
import hre, {ethers} from "hardhat";
import { expect } from "chai";
import { Contract } from "ethers";
import hre, { ethers } from "hardhat";

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

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

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