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).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(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(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).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>`.
|
- ``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
|
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.
|
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``:
|
The following properties are available for an integer type ``T``:
|
||||||
|
|
||||||
``type(T).min``
|
``type(T).min``
|
||||||
|
@ -3391,6 +3391,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
|
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "typehash")
|
||||||
|
annotation.isPure = true;
|
||||||
else if (
|
else if (
|
||||||
magicType->kind() == MagicType::Kind::MetaType &&
|
magicType->kind() == MagicType::Kind::MetaType &&
|
||||||
(memberName == "min" || memberName == "max")
|
(memberName == "min" || memberName == "max")
|
||||||
|
@ -398,6 +398,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{MagicType::Kind::MetaType, "runtimeCode"},
|
{MagicType::Kind::MetaType, "runtimeCode"},
|
||||||
{MagicType::Kind::MetaType, "name"},
|
{MagicType::Kind::MetaType, "name"},
|
||||||
{MagicType::Kind::MetaType, "interfaceId"},
|
{MagicType::Kind::MetaType, "interfaceId"},
|
||||||
|
{MagicType::Kind::MetaType, "typehash"},
|
||||||
{MagicType::Kind::MetaType, "min"},
|
{MagicType::Kind::MetaType, "min"},
|
||||||
{MagicType::Kind::MetaType, "max"},
|
{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>;
|
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
|
Type const* StructDefinition::type() const
|
||||||
{
|
{
|
||||||
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
|
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; }
|
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;
|
Type const* type() const override;
|
||||||
|
|
||||||
bool isVisibleInDerivedContracts() const override { return true; }
|
bool isVisibleInDerivedContracts() const override { return true; }
|
||||||
|
@ -4213,6 +4213,13 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
|
|||||||
{"name", TypeProvider::stringMemory()},
|
{"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)
|
else if (m_typeArgument->category() == Type::Category::Integer)
|
||||||
{
|
{
|
||||||
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
|
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();
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||||
m_context << (u256{contract.interfaceId()} << (256 - 32));
|
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")
|
else if (member == "min" || member == "max")
|
||||||
{
|
{
|
||||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
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();
|
ContractDefinition const& contract = contractType.contractDefinition();
|
||||||
define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n";
|
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")
|
else if (member == "min" || member == "max")
|
||||||
{
|
{
|
||||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
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();
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition();
|
||||||
defineExpr(_memberAccess, contract.interfaceId());
|
defineExpr(_memberAccess, contract.interfaceId());
|
||||||
}
|
}
|
||||||
|
else if (memberName == "typehash")
|
||||||
|
{
|
||||||
|
StructDefinition const& structDef = dynamic_cast<StructType const&>(*magicType->typeArgument()).structDefinition();
|
||||||
|
defineExpr(_memberAccess, u256(structDef.typehash()));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
// NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not
|
// 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
|
// at all usable in the SMT checker currently
|
||||||
|
@ -1916,6 +1916,9 @@ BOOST_AUTO_TEST_CASE(builtins)
|
|||||||
interface I {}
|
interface I {}
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint x;
|
||||||
|
}
|
||||||
function accessBuiltin() public payable {
|
function accessBuiltin() public payable {
|
||||||
abi.decode;
|
abi.decode;
|
||||||
abi.encode;
|
abi.encode;
|
||||||
@ -1956,6 +1959,7 @@ BOOST_AUTO_TEST_CASE(builtins)
|
|||||||
address(0).staticcall;
|
address(0).staticcall;
|
||||||
type(C).name;
|
type(C).name;
|
||||||
type(I).interfaceId;
|
type(I).interfaceId;
|
||||||
|
type(S).typehash;
|
||||||
type(uint).min;
|
type(uint).min;
|
||||||
type(uint).max;
|
type(uint).max;
|
||||||
assert;
|
assert;
|
||||||
|
Loading…
Reference in New Issue
Block a user