ERC提出了一个如何处理以太坊合约中签名数据的规范
原因:
当前市场上的若干多签钱包允许接受“预签名(presigned)”的交易。预签名数据是一个二进制的数据块(signed_data),同时还有签名信息(r,s,v)。而signed_data的解释没有被明确的定义,因此导致了如下问题:
① 标准以太坊交易可以以signed_data的形式提交。
一个以太坊交易可以被解压成RLPdata(RLP<nonce, gasPrice, startGas, to, value, data>),r,s,v。如果对于signed_data没有语法约束,这意味着RLPdata可以被当作一个有语法效力的“预签名”交易。
(RLP:一种序列化的方式,其与网络传输中json的序列化/反序列化有一些不同,RLP不仅兼顾网络传输,其编码特性更确保了编码后的一致性,因为每笔交易过程中要进行Keccak256,如果不能保证编码后的一致性,会导致其Hash值不同,那么验证者就无法验证交易是否由同一个人发出。)
②多重签名钱包还存在一个问题,即预签名交易没有绑定到特定的验证器(或验证者),即特定的钱包。例子:
1)用户A、B和C拥有2/3钱包X
2)用户A、B和D拥有2/3钱包Y
3)用户A和用户B向X提交指定的事务。
4)攻击者现在可以重用他们设计的事务到X,并提交到Y。
规范:
提出了以下的signed_data格式:
0x19 <1 byte version> <version specific data*> <data to sign*>.
前面的0x19是为了使该signed_data不是有效的RLP。
这意味着任何signed_data都不能是一个RLP结构,而是一个1字节的RLP有效负载,后面跟着其他内容。因此,任何EIP-191的signed_data都不可能是以太坊交易。
此外,之所以选择0x19,是因为从ethereum/go-ethereum#2940开始,在personal_sign中哈希之前会添加以下内容: “\x19Ethereum Signed Message:\n” + len(message)。因此,使用0x19可以通过更改版本0x45 (E)来扩展该方案,以处理这类签名。
版本字节注册表
Version 0x00
0x19 <0x00>
版本0x00对于特定版本的数据具有 。在多签钱包根据传递的签名执行的情况下,validator地址是多签钱包本身的地址。要签名的数据可以是任意数据。
Version 0x01
The version 0x01 is for structured data as defined in EIP-712
Version 0x45(E)
0x19 <0x45 (E)> <thereum Signed Message:\n" + len(message)>
版本0x45 (E)有<thereum Signed Message:\n" + len(Message)>作为特定版本的数据。要签名的数据可以是任意数据。
Example:
Version 0x00
function signatureBasedExecution(address target, uint256 nonce, bytes memory payload, uint8 v, bytes32 r, bytes32 s) public payable {
// Arguments when calculating hash to validate
// 1: byte(0x19) - the initial 0x19 byte 定义
// 2: byte(0) - the version byte 版本号
// 3: address(this) - the validator address 验证者地址
// 4-6 : Application specific data 特定数据
bytes32 hash = keccak256(abi.encodePacked(byte(0x19), byte(0), address(this), msg.value, nonce, payload));
// recovering the signer from the hash and the signature
addressRecovered = ecrecover(hash, v, r, s);
// logic of the wallet
// if (addressRecovered == owner) executeOnTarget(target, payload);}