Skip to content

Commit

Permalink
Add support for interfaceID.
Browse files Browse the repository at this point in the history
  • Loading branch information
aarlt committed Apr 14, 2020
1 parent 7a9e024 commit ef743cc
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 11 deletions.
9 changes: 9 additions & 0 deletions docs/units-and-global-variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,12 @@ available for a contract type ``C``:
regular calls.
The same restrictions as with ``.creationCode`` also apply for this
property.

Additionally to the properties that are available for contract types,
a different set is available for interface type ``I``:

``type(I).interfaceId``:
This property returns the `EIP-165 <https://eips.ethereum.org/EIPS/eip-165>`_
interface identifier of the given interface ``I``, excluding all
potentially derived functions. The interface identifier is defined as the ``XOR`` of all
function selectors defined within the interface. The returned interface identifier is of type ``bytes4``.
2 changes: 2 additions & 0 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2617,6 +2617,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
}
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
annotation.isPure = true;
}

return false;
Expand Down
1 change: 1 addition & 0 deletions libsolidity/analysis/ViewPureChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::MetaType, "creationCode"},
{MagicType::Kind::MetaType, "runtimeCode"},
{MagicType::Kind::MetaType, "name"},
{MagicType::Kind::MetaType, "interfaceId"},
};
set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"}
Expand Down
16 changes: 9 additions & 7 deletions libsolidity/ast/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ bool ContractDefinition::derivesFrom(ContractDefinition const& _base) const
return util::contains(annotation().linearizedBaseContracts, &_base);
}

map<util::FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
map<util::FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions(bool _includeDerivedFunctions) const
{
auto exportedFunctionList = interfaceFunctionList();
auto exportedFunctionList = interfaceFunctionList(_includeDerivedFunctions);

map<util::FixedHash<4>, FunctionTypePointer> exportedFunctions;
for (auto const& it: exportedFunctionList)
Expand Down Expand Up @@ -174,14 +174,16 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
return *m_interfaceEvents;
}

vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList() const
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeDerivedFunctions) const
{
if (!m_interfaceFunctionList)
if (!m_interfaceFunctionList[_includeDerivedFunctions])
{
set<string> signaturesSeen;
m_interfaceFunctionList = make_unique<vector<pair<util::FixedHash<4>, FunctionTypePointer>>>();
m_interfaceFunctionList[_includeDerivedFunctions] = make_unique<vector<pair<util::FixedHash<4>, FunctionTypePointer>>>();
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
{
if (_includeDerivedFunctions == false && contract != this)
continue;
vector<FunctionTypePointer> functions;
for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isPartOfExternalInterface())
Expand All @@ -199,12 +201,12 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
{
signaturesSeen.insert(functionSignature);
util::FixedHash<4> hash(util::keccak256(functionSignature));
m_interfaceFunctionList->emplace_back(hash, fun);
m_interfaceFunctionList[_includeDerivedFunctions]->emplace_back(hash, fun);
}
}
}
}
return *m_interfaceFunctionList;
return *m_interfaceFunctionList[_includeDerivedFunctions];
}

TypePointer ContractDefinition::type() const
Expand Down
6 changes: 3 additions & 3 deletions libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ class ContractDefinition: public Declaration, public StructurallyDocumented

/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions() const;
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions(bool _includeDerivedFunctions = true) const;
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList(bool _includeDerivedFunctions = true) const;

/// @returns a list of all declarations in this contract
std::vector<Declaration const*> declarations() const { return filteredNodes<Declaration>(m_subNodes); }
Expand Down Expand Up @@ -528,7 +528,7 @@ class ContractDefinition: public Declaration, public StructurallyDocumented
ContractKind m_contractKind;
bool m_abstract{false};

mutable std::unique_ptr<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::map<bool, std::unique_ptr<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;
};

Expand Down
4 changes: 3 additions & 1 deletion libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3719,7 +3719,9 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{"name", TypeProvider::stringMemory()},
});
else
return {};
return MemberList::MemberMap({
{"interfaceId", TypeProvider::fixedBytes(4)},
});
}
}
solAssert(false, "Unknown kind of magic.");
Expand Down
11 changes: 11 additions & 0 deletions libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
utils().storeStringData(contract.name());
}
else if (member == "interfaceId")
{
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
u256 result{0};
for (auto const& function: contract.interfaceFunctionList(false))
result = result ^ u256(function.first.asBytes());
m_context << result;
/// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
}
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
{
// no-op
Expand Down
4 changes: 4 additions & 0 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,10 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
solUnimplementedAssert(false, "");
}
else if (member == "interfaceId")
{
solUnimplementedAssert(false, "");
}
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
{
// no-op
Expand Down
64 changes: 64 additions & 0 deletions test/libsolidity/semanticTests/interfaceID/interfaces.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
interface HelloWorld {
function hello() external pure;
function world(int) external pure;
}

interface HelloWorldDerived is HelloWorld {
function other() external pure;
}

interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

contract Test {
bytes4 public ghello_world_interfaceId = type(HelloWorld).interfaceId;
bytes4 public ERC165_interfaceId = type(ERC165).interfaceId;

function hello() public pure returns (bytes4 data){
HelloWorld i;
return i.hello.selector;
}

function world() public pure returns (bytes4 data){
HelloWorld i;
return i.world.selector;
}

function hello_world() public pure returns (bytes4 data){
HelloWorld i;
return i.hello.selector ^ i.world.selector;
}

function hello_world_interfaceId() public pure returns (bytes4 data){
return type(HelloWorld).interfaceId;
}

function other() public pure returns (bytes4 data){
HelloWorldDerived i;
return i.other.selector;
}

function hello_world_derived_interfaceId() public pure returns (bytes4 data){
return type(HelloWorldDerived).interfaceId;
}
}

// ----
// hello() -> left(0x19ff1d21)
// world() -> left(0xdf419679)
//
// ERC165_interfaceId() -> left(0x01ffc9a7)
//
// hello_world() -> left(0xc6be8b58)
// hello_world_interfaceId() -> left(0xc6be8b58)
// ghello_world_interfaceId() -> left(0xc6be8b58)
//
// other() -> left(0x85295877)
// hello_world_derived_interfaceId() -> left(0x85295877)

0 comments on commit ef743cc

Please sign in to comment.