Summary
因为EIP191的签名消息太过于的简单,所以当签名数据比较复杂的时候,用户往往只能看到一串十六进制的字符串,而无法核实签名数据是否和预期的相符合,所以有了EIP712。
EIP191的问题:
- 没有明确的防重放的规定
- message如果是一个结构体则美玉欧相应的编码规范,开发者一般会自己按照自己的方式进行编码,这样就有可能造成一些外部组件导致无法解析编码
EIP712就解决了上面两个问题:
- 通过DOMAIN_SEPARATOR设定,来防止重放攻击
- 规范对结构体的编码方式
格式
在EIP712中的version specific data中,存放着DOMAIN_SEPARATOR的hash,这是一个结构体,格式如下:
struct EIP712Domain{
string name, //用户可读的域名,如DAPP的名字
string version, // 目前签名的域的版本号
uint256 chainId, // EIP-155中定义的chain ID, 如以太坊主网为1
address verifyingContract, // 用于验证签名的合约地址
bytes32 salt // 随机数,这个往往被省略
}
有这个数据,即包括chainID,又包括合约地址,还包括app名字,版本号等数据,不可能被重放攻击了吧。
步骤
- 准备前缀
- 计算Domain_SEPARATOR的hash
DOMAIN_SEPARATOR_HASH = keccak256(
abi.encode(
// encodeType
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
// encodeData
keccak256(bytes(name)),
keccak256(bytes('1')),
chainId,
address(this)
)
);
- 拼接
0x19 0x01 DOMAIN_SEPARATOR_HASH "abc"。
- 对于结构体
// Mail 是待签名的结构体
struct Mail {
address from;
address to;
string contents;
}
//对结构体编码
messageHash = keccak256(
abi.encode(
keccak256("Mail(address from,address to,string contents)"
mail.from,
mail.to,
keccak256(bytes(mail.contents))
)
);
可以看到messageHash中,把结构体名称,属性名称都编码进去了,因此钱包等第三方能够知道编码的结构体数据结构。解决了结构体编码的规范
再集合前面DOMAIN_SEPARATOR的例子,最终的EIP712编码就为:
0x19 0x01 DOMAIN_SEPARATOR_HASH messageHash
Refer
-
https://github.com/AmazingAng/WTF-Solidity/tree/main/52_EIP712
-
Remco Bloemen (@Recmo), Leonid Logvinov (@LogvinovLeon), Jacob Evans (@dekz), "EIP-712: Typed structured data hashing and signing," Ethereum Improvement Proposals, no. 712, September 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-712.
-
https://mirror.xyz/xyyme.eth/cJX3zqiiUg2dxB1nmbXbDcQ1DSdajHP5iNgBc6wEZz4