forked from DecentralizedClimateFoundation/DCIPs
109 lines
4.4 KiB
Markdown
109 lines
4.4 KiB
Markdown
---
|
|
eip: 191
|
|
title: Signed Data Standard
|
|
author: Martin Holst Swende (@holiman), Nick Johnson <arachnid@notdot.net>
|
|
discussions-to: https://github.com/ethereum/EIPs/issues/191
|
|
status: Final
|
|
type: Standards Track
|
|
category: ERC
|
|
created: 2016-01-20
|
|
---
|
|
|
|
# Abstract
|
|
|
|
This ERC proposes a specification about how to handle signed data in Ethereum contracts.
|
|
|
|
# Motivation
|
|
|
|
Several multisignature wallet implementations have been created which accepts `presigned` transactions. A `presigned` transaction is a chunk of binary `signed_data`, along with signature (`r`, `s` and `v`). The interpretation of the `signed_data` has not been specified, leading to several problems:
|
|
|
|
* Standard Ethereum transactions can be submitted as `signed_data`. An Ethereum transaction can be unpacked, into the following components: `RLP<nonce, gasPrice, startGas, to, value, data>` (hereby called `RLPdata`), `r`, `s` and `v`. If there are no syntactical constraints on `signed_data`, this means that `RLPdata` can be used as a syntactically valid `presigned` transaction.
|
|
* Multisignature wallets have also had the problem that a `presigned` transaction has not been tied to a particular `validator`, i.e a specific wallet. Example:
|
|
1. Users `A`, `B` and `C` have the `2/3`-wallet `X`
|
|
2. Users `A`, `B` and `D` have the `2/3`-wallet `Y`
|
|
3. User `A` and `B` submit `presigned` transactions to `X`.
|
|
4. Attacker can now reuse their presigned transactions to `X`, and submit to `Y`.
|
|
|
|
## Specification
|
|
|
|
We propose the following format for `signed_data`
|
|
|
|
```
|
|
0x19 <1 byte version> <version specific data> <data to sign>.
|
|
```
|
|
|
|
The initial `0x19` byte is intended to ensure that the `signed_data` is not valid RLP.
|
|
|
|
> For a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding.
|
|
|
|
That means that any `signed_data` cannot be one RLP-structure, but a 1-byte `RLP` payload followed by something else. Thus, any EIP-191 `signed_data` can never be an Ethereum transaction.
|
|
|
|
Additionally, `0x19` has been chosen because since ethereum/go-ethereum#2940 , the following is prepended before hashing in personal_sign:
|
|
|
|
```
|
|
"\x19Ethereum Signed Message:\n" + len(message).
|
|
```
|
|
|
|
Using `0x19` thus makes it possible to extend the scheme by defining a version `0x45` (`E`) to handle these kinds of signatures.
|
|
|
|
### Registry of version bytes
|
|
|
|
| Version byte | EIP | Description
|
|
| ------------ | -------------- | -----------
|
|
| `0x00` | [191][eip-191] | Data with intended validator
|
|
| `0x01` | [712][eip-712] | Structured data
|
|
| `0x45` | [191][eip-191] | `personal_sign` messages
|
|
|
|
#### Version `0x00`
|
|
|
|
```
|
|
0x19 <0x00> <intended validator address> <data to sign>
|
|
```
|
|
|
|
The version `0x00` has `<intended validator address>` for the version specific data. In the case of a Multisig wallet that perform an execution based on a passed signature, the validator address is the address of the Multisig itself. The data to sign could be any arbitrary data.
|
|
|
|
#### 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)> <data to sign>
|
|
```
|
|
|
|
The version `0x45` (E) has `<thereum Signed Message:\n" + len(message)>` for the version-specific data. The data to sign can be any arbitrary data.
|
|
|
|
> NB: The `E` in `Ethereum Signed Message` refers to the version byte 0x45. The character `E` is `0x45` in hexadecimal which makes the remainder, `thereum Signed Message:\n + len(message)`, the version-specific data.
|
|
|
|
[EIP-191]: ./eip-191.md
|
|
[EIP-712]: ./eip-712.md
|
|
|
|
### Example
|
|
|
|
The following snippets has been written in Solidity 0.8.0.
|
|
|
|
#### Version `0x00`
|
|
|
|
```solidity
|
|
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);
|
|
}
|
|
```
|
|
## Copyright
|
|
|
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|