EIP712
EIP712
当然!EIP-712 是以太坊的一项标准,它定义了一个结构化的消息签名方案,以便能够安全、标准化地对复杂数据进行签名和验证。简单来说,它是一个用于签名复杂数据结构(如结构化对象、数组、嵌套对象等)并保证其不可篡改的标准。
🌟 EIP-712 概述
EIP-712 规范提出了对结构化数据进行签名的方法,解决了以下问题:
- 避免重放攻击:如果没有结构化数据的签名,可能存在相同的签名被滥用的风险。
- 易于解析和验证:签名的对象可以被预先约定和验证,避免直接签名原始字节流。
- 提升用户体验:用户可以明确知道自己签署的内容,减少误签和恶意签名的风险。
🔍 EIP-712 的核心概念
EIP-712 通过将数据分为两部分来创建安全的签名:
Domain Separator(域分隔符):标识签名数据的来源。通过引入这个“域”概念,可以区分不同的签名场景,防止重放攻击。例如,一个签名用于交易,另一个用于账户管理,它们的域分隔符是不同的,彼此之间互不干扰。
类型哈希:对数据结构的类型进行哈希,确保签名的数据结构不被篡改。
数据结构:EIP-712 签名的数据本身是结构化的,数据包括了
type
、value
、domain
(域)等信息,所有内容都有严格的格式。
📜 EIP-712 的使用过程
1. 定义结构化数据
EIP-712 要求签名的数据必须是结构化的(如 struct
或 JSON 对象)。举个例子:
struct Order { |
2. 计算域分隔符
域分隔符(Domain Separator)通常包含以下信息:
- 合约地址(或域名)
- 合约版本
- 合约链 ID
- 其他一些身份标识信息
通过域分隔符可以确保签名只适用于特定的域,而不会被重放到其他地方。
例如:
bytes32 DOMAIN_SEPARATOR = keccak256(abi.encode( |
3. 计算类型哈希
每种类型的结构数据都有一个对应的类型哈希(Type Hash),它是通过 keccak256
对结构的声明进行哈希计算得出的。比如,对于 Order
结构:
bytes32 ORDER_TYPEHASH = keccak256( |
4. 组合哈希
组合域分隔符、类型哈希和数据本身,计算出最终的消息哈希:
bytes32 dataHash = keccak256(abi.encode( |
这个哈希值就是签名所需的数据。
5. 签名
使用 ECDSA
(椭圆曲线数字签名算法)对上述生成的消息哈希进行签名:
bytes memory signature = ECDSA.sign(hash, privateKey); |
签名完成后,消息和签名可以被发送到智能合约进行验证。
6. 验证签名
在合约中,接收到数据和签名后,使用 ECDSA.recover
恢复签名者地址,并验证是否与期望的地址一致:
address signer = ECDSA.recover(hash, signature); |
🧑💻 EIP-712 例子:
假设你要在智能合约中实现订单系统,订单结构为:
struct Order { |
你可以按以下步骤使用 EIP-712 来签名和验证该订单:
- 定义数据结构和类型哈希:
bytes32 private constant ORDER_TYPEHASH = keccak256( |
- 计算域分隔符:
bytes32 DOMAIN_SEPARATOR = keccak256(abi.encode( |
- 生成订单数据哈希:
bytes32 orderHash = keccak256(abi.encode( |
- 生成最终哈希:
bytes32 hash = MessageHashUtils.toTypedDataHash(DOMAIN_SEPARATOR, orderHash); |
- 签名和验证:
address signer = ECDSA.recover(hash, signature); |
✅ EIP-712 的优势
- 安全性:通过结构化的数据和域分隔符避免了签名的篡改和重放攻击。
- 用户体验:用户可以明确看到他们签名的数据内容,避免不必要的误签。
- 跨链和跨平台一致性:EIP-712 使得不同链或应用间能够一致地处理结构化数据的签名和验证。
🏆 总结
EIP-712 允许以太坊智能合约验证结构化数据的签名,避免了直接对原始字节流进行签名的风险。它通过域分隔符、类型哈希和结构化数据,提供了一个安全、灵活、易用的签名机制,广泛应用于 DApp、DeFi、NFT 等领域。