跳到主要内容

Summary

因为EIP191的签名消息太过于的简单,所以当签名数据比较复杂的时候,用户往往只能看到一串十六进制的字符串,而无法核实签名数据是否和预期的相符合,所以有了EIP712。

EIP191的问题:

  1. 没有明确的防重放的规定
  2. message如果是一个结构体则美玉欧相应的编码规范,开发者一般会自己按照自己的方式进行编码,这样就有可能造成一些外部组件导致无法解析编码

EIP712就解决了上面两个问题:

  1. 通过DOMAIN_SEPARATOR设定,来防止重放攻击
  2. 规范对结构体的编码方式

格式

在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名字,版本号等数据,不可能被重放攻击了吧。

步骤

  1. 准备前缀
  2. 计算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)
)
);
  1. 拼接
0x19 0x01 DOMAIN_SEPARATOR_HASH "abc"
  1. 对于结构体
     // 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