Skip to content

Commit

Permalink
Adding event and error selector fields on the lines of the function s…
Browse files Browse the repository at this point in the history
…elector fields
  • Loading branch information
nishant-sachdeva authored and chriseth committed May 23, 2022
1 parent 2cb29db commit d4c06d2
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
### 0.8.15 (unreleased)

Language Features:
* General: Add `E.selector` for a non-anonymous event `E` to access the 32-byte selector topic.
* General: Errors and Events allow qualified access from other contracts.


Compiler Features:
Expand Down
10 changes: 10 additions & 0 deletions docs/contracts/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ four indexed arguments rather than three.
In particular, it is possible to "fake" the signature of another event
using an anonymous event.

Members of Events
=================

- ``event.selector``: For non-anonymous events, this is a ``bytes32`` value
containing the ``keccak256`` hash of the event signature, as used in the default topic.


Example
=======

.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ class EventDefinition: public CallableDeclaration, public StructurallyDocumented
FunctionTypePointer functionType(bool /*_internal*/) const override;

bool isVisibleInDerivedContracts() const override { return true; }
bool isVisibleViaContractTypeAccess() const override { return false; /* TODO */ }
bool isVisibleViaContractTypeAccess() const override { return true; }

EventDefinitionAnnotation& annotation() const override;

Expand Down
6 changes: 6 additions & 0 deletions libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3335,6 +3335,12 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
}
case Kind::Error:
return {{"selector", TypeProvider::fixedBytes(4)}};
case Kind::Event:
{
if (!(dynamic_cast<EventDefinition const&>(declaration()).isAnonymous()))
return {{"selector", TypeProvider::fixedBytes(32)}};
return MemberList::MemberMap();
}
default:
return MemberList::MemberMap();
}
Expand Down
15 changes: 12 additions & 3 deletions libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1602,9 +1602,14 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
if (functionType->hasDeclaration())
{
m_context << functionType->externalIdentifier();
/// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
if (functionType->kind() == FunctionType::Kind::Event)
m_context << u256(h256::Arith(util::keccak256(functionType->externalSignature())));
else
{
m_context << functionType->externalIdentifier();
/// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
}
return false;
}
else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
Expand Down Expand Up @@ -1775,9 +1780,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (member == "selector")
{
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type);
// all events should have already been caught by this stage
solAssert(!(functionType.kind() == FunctionType::Kind::Event));

if (functionType.kind() == FunctionType::Kind::External)
CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2);
m_context << Instruction::SWAP1 << Instruction::POP;

/// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
}
Expand Down
15 changes: 14 additions & 1 deletion libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1785,7 +1785,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
functionType.declaration().isPartOfExternalInterface(),
""
);
define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n";
define(IRVariable{_memberAccess}) << formatNumber(
util::selectorFromSignature(functionType.externalSignature())
) << "\n";
}
else if (functionType.kind() == FunctionType::Kind::Event)
{
solAssert(functionType.hasDeclaration());
solAssert(functionType.kind() == FunctionType::Kind::Event);
solAssert(
!(dynamic_cast<EventDefinition const&>(functionType.declaration()).isAnonymous())
);
define(IRVariable{_memberAccess}) << formatNumber(
u256(h256::Arith(util::keccak256(functionType.externalSignature())))
) << "\n";
}
else
solAssert(false, "Invalid use of .selector: " + functionType.toString(false));
Expand Down
48 changes: 48 additions & 0 deletions test/libsolidity/semanticTests/error/error_selector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
library L {
error E();
}
library S {
error E(uint);
}
library T {
error E();
}

error E();

interface I {
error E();
function f() external pure;
}

contract D {
error F();
}

contract C is D {
function test1() public pure returns (bytes4, bytes4, bytes4, bytes4) {
assert(L.E.selector == T.E.selector);
assert(L.E.selector != S.E.selector);
assert(E.selector == L.E.selector);
assert(I.E.selector == L.E.selector);
return (L.E.selector, S.E.selector, E.selector, I.E.selector);
}

bytes4 s1 = L.E.selector;
bytes4 s2 = S.E.selector;
bytes4 s3 = T.E.selector;
bytes4 s4 = I.E.selector;
function test2() external returns (bytes4, bytes4, bytes4, bytes4) {
return (s1, s2, s3, s4);
}

function test3() external returns (bytes4) {
return (F.selector);
}
}
// ====
// compileViaYul: also
// ----
// test1() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000
// test2() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x92bbf6e800000000000000000000000000000000000000000000000000000000
// test3() -> 0x28811f5900000000000000000000000000000000000000000000000000000000
47 changes: 47 additions & 0 deletions test/libsolidity/semanticTests/events/event_selector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
library L {
event E();
}
library S {
event E(uint);
}
library T {
event E();
}
interface I {
event E();
}

contract D {
event F();
}

contract C is D {
function test1() external pure returns (bytes32, bytes32, bytes32) {
assert(L.E.selector == T.E.selector);
assert(I.E.selector == L.E.selector);

assert(L.E.selector != S.E.selector);
assert(T.E.selector != S.E.selector);
assert(I.E.selector != S.E.selector);

return (L.E.selector, S.E.selector, I.E.selector);
}

bytes32 s1 = L.E.selector;
bytes32 s2 = S.E.selector;
bytes32 s3 = T.E.selector;
bytes32 s4 = I.E.selector;
function test2() external returns (bytes32, bytes32, bytes32, bytes32) {
return (s1, s2, s3, s4);
}

function test3() external returns (bytes32) {
return (F.selector);
}
}
// ====
// compileViaYul: also
// ----
// test1() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x2ff0672f372fbe844b353429d4510ea5e43683af134c54f75f789ff57bc0c0, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028
// test2() -> 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x2ff0672f372fbe844b353429d4510ea5e43683af134c54f75f789ff57bc0c0, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028, 0x92bbf6e823a631f3c8e09b1c8df90f378fb56f7fbc9701827e1ff8aad7f6a028
// test3() -> 0x28811f5935c16a099486acb976b3a6b4942950a1425a74e9eb3e9b7f7135e12a
17 changes: 17 additions & 0 deletions test/libsolidity/semanticTests/events/simple.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
contract C {
event E();
}

contract Test {
event E(uint256, uint256);
function f() public {
emit C.E();
emit E(1,2);
}
}
// ====
// compileViaYul: also
// ----
// f() ->
// ~ emit E()
// ~ emit E(uint256,uint256): 0x01, 0x02
48 changes: 48 additions & 0 deletions test/libsolidity/syntaxTests/errors/error_selector_syntax.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
library L {
error E();
}
library S {
error E(uint);
}
library T {
error E();
}

error E();

interface I {
error E();
function f() external pure;
}

contract D {
error F();
}

contract C is D {
function test1() public pure returns (bytes4, bytes4, bytes4, bytes4) {
assert(L.E.selector == T.E.selector);
assert(L.E.selector != S.E.selector);
assert(E.selector == L.E.selector);
assert(I.E.selector == L.E.selector);
return (L.E.selector, S.E.selector, E.selector, I.E.selector);
}

bytes4 s1 = L.E.selector;
bytes4 s2 = S.E.selector;
bytes4 s3 = T.E.selector;
bytes4 s4 = I.E.selector;

function test2() view external returns (bytes4, bytes4, bytes4, bytes4) {
return (s1, s2, s3, s4);
}

function test3() pure external returns (bytes4) {
return (F.selector);
}
}
// ----
// Warning 2519: (16-26): This declaration shadows an existing declaration.
// Warning 2519: (45-59): This declaration shadows an existing declaration.
// Warning 2519: (78-88): This declaration shadows an existing declaration.
// Warning 2519: (122-132): This declaration shadows an existing declaration.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
library Y {
event E() anonymous;
}

contract D {
function test1() external pure returns (bytes32) {
return Y.E.selector;
}

}
// ----
// TypeError 9582: (123-135): Member "selector" not found or not visible after argument-dependent lookup in function ().
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
library Y {
event E() anonymous;
}

contract C {
bytes32 s5 = Y.E.selector;

function test2() view external returns (bytes32) {
return s5;
}
}
// ----
// TypeError 9582: (70-82): Member "selector" not found or not visible after argument-dependent lookup in function ().
43 changes: 43 additions & 0 deletions test/libsolidity/syntaxTests/events/event_selector_syntax.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
library L {
event E();
}
library S {
event E(uint);
}
library T {
event E();
}
interface I {
event E();
}

contract D {
event F();
}

contract C is D {
function test1() external pure returns (bytes32, bytes32, bytes32) {
assert(L.E.selector == T.E.selector);
assert(I.E.selector == L.E.selector);

assert(L.E.selector != S.E.selector);
assert(T.E.selector != S.E.selector);
assert(I.E.selector != S.E.selector);

return (L.E.selector, S.E.selector, I.E.selector);
}

bytes32 s1 = L.E.selector;
bytes32 s2 = S.E.selector;
bytes32 s3 = T.E.selector;
bytes32 s4 = I.E.selector;

function test2() view external returns (bytes32, bytes32, bytes32, bytes32) {
return (s1, s2, s3, s4);
}

function test3() pure external returns (bytes32) {
return (F.selector);
}
}
// ----

0 comments on commit d4c06d2

Please sign in to comment.