mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add native support of EIP-712 struct typehash
This commit is contained in:
parent
5d7533b540
commit
54b46ce39e
@ -119,6 +119,7 @@ Type Information
|
||||
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(S).typehash`` (``bytes32``): the typehash of the given struct type ``S``, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||
|
||||
|
@ -385,6 +385,13 @@ for an interface type ``I``:
|
||||
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
|
||||
function selectors defined within the interface itself - excluding all inherited functions.
|
||||
|
||||
The following properties are available for an struct type ``S``:
|
||||
|
||||
``type(S).typehash``:
|
||||
A ``bytes32`` value containing the `EIP-712 <https://eips.ethereum.org/EIPS/eip-712>`_
|
||||
typehash of the given structure ``S``. This identifier is defined as ``keccak256`` of
|
||||
structure name and all the fields with their types, wrapped in braces and separated by commas.
|
||||
|
||||
The following properties are available for an integer type ``T``:
|
||||
|
||||
``type(T).min``
|
||||
|
@ -3391,6 +3391,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
annotation.isPure = true;
|
||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
||||
annotation.isPure = true;
|
||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "typehash")
|
||||
annotation.isPure = true;
|
||||
else if (
|
||||
magicType->kind() == MagicType::Kind::MetaType &&
|
||||
(memberName == "min" || memberName == "max")
|
||||
|
@ -398,6 +398,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
||||
{MagicType::Kind::MetaType, "runtimeCode"},
|
||||
{MagicType::Kind::MetaType, "name"},
|
||||
{MagicType::Kind::MetaType, "interfaceId"},
|
||||
{MagicType::Kind::MetaType, "typehash"},
|
||||
{MagicType::Kind::MetaType, "min"},
|
||||
{MagicType::Kind::MetaType, "max"},
|
||||
};
|
||||
|
@ -389,6 +389,17 @@ std::vector<std::pair<ASTPointer<IdentifierPath>, std::optional<Token>>> UsingFo
|
||||
return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to<vector>;
|
||||
}
|
||||
|
||||
util::h256 StructDefinition::typehash() const
|
||||
{
|
||||
std::string str = name() + "(";
|
||||
for (size_t i = 0; i < m_members.size(); i++)
|
||||
{
|
||||
str += i == 0 ? "" : ",";
|
||||
str += m_members[i]->type()->canonicalName() + " " + m_members[i]->name();
|
||||
}
|
||||
return util::keccak256(str + ")");
|
||||
}
|
||||
|
||||
Type const* StructDefinition::type() const
|
||||
{
|
||||
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
|
||||
|
@ -741,6 +741,9 @@ public:
|
||||
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
|
||||
|
||||
/// @returns the EIP-712 compatible typehash of this struct.
|
||||
util::h256 typehash() const;
|
||||
|
||||
Type const* type() const override;
|
||||
|
||||
bool isVisibleInDerivedContracts() const override { return true; }
|
||||
|
@ -4213,6 +4213,13 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
||||
{"name", TypeProvider::stringMemory()},
|
||||
});
|
||||
}
|
||||
else if (m_typeArgument->category() == Type::Category::Struct)
|
||||
{
|
||||
StructType const* structTypePointer = dynamic_cast<StructType const*>(m_typeArgument);
|
||||
return MemberList::MemberMap({
|
||||
{"typehash", structTypePointer},
|
||||
});
|
||||
}
|
||||
else if (m_typeArgument->category() == Type::Category::Integer)
|
||||
{
|
||||
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
|
||||
|
@ -1931,6 +1931,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||
m_context << (u256{contract.interfaceId()} << (256 - 32));
|
||||
}
|
||||
else if (member == "typehash")
|
||||
{
|
||||
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||
StructDefinition const& struct_ = dynamic_cast<StructType const&>(*arg).structDefinition();
|
||||
m_context << struct_.typehash();
|
||||
}
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
|
@ -1941,6 +1941,13 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
ContractDefinition const& contract = contractType.contractDefinition();
|
||||
define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
|
||||
}
|
||||
else if (member == "typehash")
|
||||
{
|
||||
Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||
auto const& structType = dynamic_cast<StructType const&>(*arg);
|
||||
StructDefinition const& struct_ = structType.structDefinition();
|
||||
define(_memberAccess) << formatNumber(struct_.typehash()) << "\n";
|
||||
}
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
|
@ -1409,6 +1409,11 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition();
|
||||
defineExpr(_memberAccess, contract.interfaceId());
|
||||
}
|
||||
else if (memberName == "typehash")
|
||||
{
|
||||
StructDefinition const& structDef = dynamic_cast<StructType const&>(*magicType->typeArgument()).structDefinition();
|
||||
defineExpr(_memberAccess, u256(structDef.typehash()));
|
||||
}
|
||||
else
|
||||
// NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not
|
||||
// at all usable in the SMT checker currently
|
||||
|
@ -1916,6 +1916,9 @@ BOOST_AUTO_TEST_CASE(builtins)
|
||||
interface I {}
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint x;
|
||||
}
|
||||
function accessBuiltin() public payable {
|
||||
abi.decode;
|
||||
abi.encode;
|
||||
@ -1956,6 +1959,7 @@ BOOST_AUTO_TEST_CASE(builtins)
|
||||
address(0).staticcall;
|
||||
type(C).name;
|
||||
type(I).interfaceId;
|
||||
type(S).typehash;
|
||||
type(uint).min;
|
||||
type(uint).max;
|
||||
assert;
|
||||
|
Loading…
Reference in New Issue
Block a user