@@ -56,6 +56,7 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
5656 {" logTopic(uint256,uint256)" , std::bind (&SemanticTest::logTopic, this , _1)},
5757 {" logAddress(uint256)" , std::bind (&SemanticTest::logAddress, this , _1)},
5858 {" logData(uint256)" , std::bind (&SemanticTest::logData, this , _1)},
59+ {" expectEvent(uint256,string)" , std::bind (&SemanticTest::expectEvent, this , _1)},
5960 }}};
6061
6162 string choice = m_reader.stringSetting (" compileViaYul" , " default" );
@@ -142,6 +143,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142143 selectVM (evmc_capabilities::EVMC_CAPABILITY_EVM1);
143144
144145 reset ();
146+ m_touchedLogs.clear ();
145147
146148 m_compileViaYul = _compileViaYul;
147149 if (_compileToEwasm)
@@ -162,6 +164,14 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
162164
163165 bool constructed = false ;
164166
167+ // Iterate through the test calls and set the previous call.
168+ TestFunctionCall* previousCall{nullptr };
169+ for (auto & test: m_tests)
170+ {
171+ test.setPreviousCall (previousCall);
172+ previousCall = &test;
173+ }
174+
165175 for (auto & test: m_tests)
166176 {
167177 if (constructed)
@@ -261,6 +271,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
261271
262272 if (m_transactionSuccessful != !test.call ().expectations .failure || outputMismatch)
263273 success = false ;
274+ success = checkLogs (test);
264275 test.setFailure (!m_transactionSuccessful);
265276 }
266277
@@ -269,6 +280,10 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
269280 }
270281 }
271282
283+ TestFunctionCall fakeLastTestFunctionCall (FunctionCall{});
284+ fakeLastTestFunctionCall.setPreviousCall (previousCall);
285+ success = checkLogs (fakeLastTestFunctionCall);
286+
272287 if (success && !m_runWithYul && _compileViaYul)
273288 {
274289 m_compileViaYulCanBeSet = true ;
@@ -404,6 +419,43 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
404419 _stream << _linePrefix << " // " << setting.first << " : " << setting.second << endl;
405420}
406421
422+ bool SemanticTest::checkLogs (TestFunctionCall& _call)
423+ {
424+ _call.setLogs (SolidityExecutionFramework::recordedLogs ());
425+
426+ TestFunctionCall* producer = _call.previousCall ();
427+ std::vector<FunctionCall const *> consumers{};
428+ // Only non-builtins are able to produce logs.
429+ // So lets search from the current call up to the first non-builtin.
430+ while (producer != nullptr && producer->call ().kind == FunctionCall::Kind::Builtin)
431+ if (producer->previousCall () != nullptr )
432+ {
433+ // On the way up to the producer we track all builtins that where on the way.
434+ // Only builtins can consume logs, we store them in the consumers vector.
435+ consumers.emplace_back (&producer->call ());
436+ producer = producer->previousCall ();
437+ }
438+
439+ // Producer will now point to the call that probably produced a log.
440+ if (producer)
441+ {
442+ // We iterate through the consumers to find out what logs they have consumed.
443+ for (auto & consumer: consumers)
444+ for (auto logIdx: m_touchedLogs[consumer])
445+ // All logs that where touched by the consumer, will be marked as
446+ // touched within the producer.
447+ touchLog (producer->call (), logIdx);
448+
449+ // Finally we update the consumed logs within the producer.
450+ for (auto & logIdx: m_touchedLogs[&producer->call ()])
451+ producer->consumedLogs ().insert (logIdx);
452+
453+ std::cout << producer->consumedLogs ().size () << " / " << producer->logs ().size () << std::endl;
454+ return producer->consumedLogs ().size () == producer->logs ().size ();
455+ }
456+ return true ;
457+ }
458+
407459bytes SemanticTest::numLogs (FunctionCall const &)
408460{
409461 // numLogs()
@@ -419,6 +471,7 @@ bytes SemanticTest::numLogTopics(FunctionCall const& call)
419471 size_t logCount = SolidityExecutionFramework::numLogs ();
420472 // todo: hex strings not supported by lexical_cast<..>(..)
421473 auto logIdx = lexical_cast<size_t >(call.arguments .parameters .front ().rawString );
474+ touchLog (call, logIdx);
422475 if (logCount > 0 && logIdx < logCount)
423476 return util::toBigEndian (u256{SolidityExecutionFramework::numLogTopics (logIdx)});
424477 // empty result means failure.
@@ -430,6 +483,7 @@ bytes SemanticTest::logTopic(FunctionCall const& call)
430483 // logTopic(uint256,uint256)
431484 assert (call.arguments .parameters .size () == 2 );
432485 auto logIdx = lexical_cast<size_t >(call.arguments .parameters .front ().rawString );
486+ touchLog (call, logIdx);
433487 auto topicIdx = lexical_cast<size_t >(call.arguments .parameters .back ().rawString );
434488 size_t logCount = SolidityExecutionFramework::numLogs ();
435489 // todo: hex strings not supported by lexical_cast<..>(..)
@@ -449,6 +503,7 @@ bytes SemanticTest::logAddress(FunctionCall const& call)
449503 size_t logCount = SolidityExecutionFramework::numLogs ();
450504 // todo: hex strings not supported by lexical_cast<..>(..)
451505 auto logIdx = lexical_cast<size_t >(call.arguments .parameters .front ().rawString );
506+ touchLog (call, logIdx);
452507 if (logCount > 0 && logIdx < logCount)
453508 return util::toBigEndian (u256{u160{SolidityExecutionFramework::logAddress (logIdx)}});
454509 // empty result means failure.
@@ -462,12 +517,45 @@ bytes SemanticTest::logData(FunctionCall const& call)
462517 size_t logCount = SolidityExecutionFramework::numLogs ();
463518 // todo: hex strings not supported by lexical_cast<..>(..)
464519 auto logIdx = lexical_cast<size_t >(call.arguments .parameters .front ().rawString );
520+ touchLog (call, logIdx);
465521 if (logCount > 0 && logIdx < logCount)
466522 return SolidityExecutionFramework::logData (logIdx);
467523 // empty result means failure.
468524 return bytes{};
469525}
470526
527+ bytes SemanticTest::expectEvent (FunctionCall const & call)
528+ {
529+ // expectEvent(uint256,string): logIdx, eventSignature
530+ assert (call.arguments .parameters .size () == 2 );
531+ size_t logCount = SolidityExecutionFramework::numLogs ();
532+ // todo: hex strings not supported by lexical_cast<..>(..)
533+ auto logIdx = lexical_cast<size_t >(call.arguments .parameters .front ().rawString );
534+ touchLog (call, logIdx);
535+ auto logSignature = call.arguments .parameters .back ().rawString ;
536+ assert (logSignature.length () >= 2 );
537+ logSignature = logSignature.substr (1 , logSignature.length () - 2 );
538+ h256 logSignatureHash{util::keccak256 (logSignature)};
539+ if (logCount > 0 && logIdx < logCount)
540+ {
541+ vector<h256> topics;
542+ size_t topicCount = SolidityExecutionFramework::numLogTopics (logIdx);
543+ for (size_t topicIdx = 0 ; topicIdx < topicCount; ++topicIdx)
544+ topics.push_back (SolidityExecutionFramework::logTopic (logIdx, topicIdx));
545+ // remove topics[0], if the signature matches.
546+ if (!topics.empty () && topics[0 ] == logSignatureHash)
547+ topics.erase (topics.begin ());
548+ bytes result;
549+ for (auto & topic : topics)
550+ result += util::toBigEndian (topic);
551+ result += SolidityExecutionFramework::logData (logIdx);
552+ // todo: anonymous events with no data would be treated as error, maybe not that important.
553+ return result;
554+ }
555+ // empty result means failure.
556+ return bytes{};
557+ }
558+
471559void SemanticTest::parseExpectations (istream& _stream)
472560{
473561 TestFileParser parser{_stream, &this ->m_builtins };
0 commit comments