Merge pull request #12921 from ethereum/add_event_and_error_selector_fields_on_the_line_of_function_selector_field

Adding event and error selector fields
This commit is contained in:
chriseth 2022-05-23 11:34:13 +02:00 committed by GitHub
commit f0e43921f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 273 additions and 5 deletions

View File

@ -1,6 +1,8 @@
### 0.8.15 (unreleased) ### 0.8.15 (unreleased)
Language Features: 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: Compiler Features:

View File

@ -73,6 +73,16 @@ four indexed arguments rather than three.
In particular, it is possible to "fake" the signature of another event In particular, it is possible to "fake" the signature of another event
using an anonymous 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 .. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0

View File

@ -1216,7 +1216,7 @@ public:
FunctionTypePointer functionType(bool /*_internal*/) const override; FunctionTypePointer functionType(bool /*_internal*/) const override;
bool isVisibleInDerivedContracts() const override { return true; } bool isVisibleInDerivedContracts() const override { return true; }
bool isVisibleViaContractTypeAccess() const override { return false; /* TODO */ } bool isVisibleViaContractTypeAccess() const override { return true; }
EventDefinitionAnnotation& annotation() const override; EventDefinitionAnnotation& annotation() const override;

View File

@ -3335,6 +3335,12 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
} }
case Kind::Error: case Kind::Error:
return {{"selector", TypeProvider::fixedBytes(4)}}; return {{"selector", TypeProvider::fixedBytes(4)}};
case Kind::Event:
{
if (!(dynamic_cast<EventDefinition const&>(declaration()).isAnonymous()))
return {{"selector", TypeProvider::fixedBytes(32)}};
return MemberList::MemberMap();
}
default: default:
return MemberList::MemberMap(); return MemberList::MemberMap();
} }

View File

@ -1601,10 +1601,15 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
) )
{ {
if (functionType->hasDeclaration()) if (functionType->hasDeclaration())
{
if (functionType->kind() == FunctionType::Kind::Event)
m_context << u256(h256::Arith(util::keccak256(functionType->externalSignature())));
else
{ {
m_context << functionType->externalIdentifier(); m_context << functionType->externalIdentifier();
/// need to store it as bytes4 /// need to store it as bytes4
utils().leftShiftNumberOnStack(224); utils().leftShiftNumberOnStack(224);
}
return false; return false;
} }
else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression())) else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
@ -1775,9 +1780,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (member == "selector") if (member == "selector")
{ {
auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type); 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) if (functionType.kind() == FunctionType::Kind::External)
CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2); CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2);
m_context << Instruction::SWAP1 << Instruction::POP; m_context << Instruction::SWAP1 << Instruction::POP;
/// need to store it as bytes4 /// need to store it as bytes4
utils().leftShiftNumberOnStack(224); utils().leftShiftNumberOnStack(224);
} }

View File

@ -1785,7 +1785,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
functionType.declaration().isPartOfExternalInterface(), 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 else
solAssert(false, "Invalid use of .selector: " + functionType.toString(false)); solAssert(false, "Invalid use of .selector: " + functionType.toString(false));

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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 ().

View File

@ -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 ().

View File

@ -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);
}
}
// ----