mirror of https://github.com/status-im/EIPs.git
96 lines
9.6 KiB
Markdown
96 lines
9.6 KiB
Markdown
---
|
|
eip: 1271
|
|
title: Standard Signature Validation Method for Contracts
|
|
author: Francisco Giordano (@frangio), Matt Condon (@shrugs), Philippe Castonguay (@PhABC), Amir Bandeali (@abandeali1), Jorge Izquierdo (@izqui), Bertrand Masius (@catageek)
|
|
discussions-to: https://github.com/ethereum/EIPs/issues/1271
|
|
status: Draft
|
|
type: Standards Track
|
|
category: ERC
|
|
created: 2018-07-25
|
|
---
|
|
|
|
<!--You can leave these HTML comments in your merged EIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new EIPs. Note that an EIP number will be assigned by an editor. When opening a pull request to submit your EIP, please use an abbreviated title in the filename, `eip-draft_title_abbrev.md`. The title should be 44 characters or less.-->
|
|
|
|
## Simple Summary
|
|
<!--"If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the EIP.-->
|
|
Many blockchain based applications allow users to sign off-chain messages instead of directly requesting users to do an on-chain transaction. This is the case for decentralized exchanges with off-chain orderbooks like [0x](https://0xproject.com/) and [etherdelta](https://etherdelta.com/). These applications usually assume that the message will be signed by the same address that owns the assets. However, one can hold assets directly in their regular account (controlled by a private key) *or* in a smart contract that acts as a wallet (e.g. a multisig contract). The current design of many smart contracts prevent contract based accounts from interacting with them, since contracts do not possess private keys and therefore can not directly sign messages. The proposal here outlines a standard way for contracts to verify if a provided signature is valid when the account is a contract.
|
|
|
|
## Abstract
|
|
<!--A short (~200 word) description of the technical issue being addressed.-->
|
|
Externally Owned Accounts (EOA) can sign messages with their associated private keys, but currently contracts cannot. This is a problem for many applications that implement signature based off-chain methods, since contracts can't easily interact with them as they do not possess a private key. Here, we propose a standard way for any contracts to verify whether a signature on a behalf of a given contract is valid.
|
|
|
|
## Motivation
|
|
<!--The motivation is critical for EIPs that want to change the Ethereum protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the EIP solves. EIP submissions without sufficient motivation may be rejected outright.-->
|
|
|
|
In the future, it is likely that many users will hold their assets in a smart contract instead of holding them in their externally owned account directly since contracts can improve user experience significantly while providing extra security. This means that contracts using signature based functions should not assume that a given address can provide ECDSA signatures. Otherwise, identity based contracts and contracts holding assets may not be able to interact with functions requiring ECDSA signatures directly.
|
|
|
|
Here, we use the term *smart account* to refer to any contract that acts as an account, which can include identity based methods (e.g. [ERC-725](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-725.md) & [ERC-1078](https://github.com/alexvandesande/EIPs/blob/ee2347027e94b93708939f2e448447d030ca2d76/EIPS/eip-1078.md)), asset ownership (e.g. Multisigs, proxy contracts) and/or executable signed messages methods (e.g. [ERC-1077)](https://github.com/alexvandesande/EIPs/blob/ee2347027e94b93708939f2e448447d030ca2d76/EIPS/eip-1077.md). This terminology is important for the reader to better distinguish a contract that acts as an account (e.g. a multisig, wallet or [Gnosis Safe](https://github.com/gnosis/safe-contracts) contract) and a contract that does not act as an account but requires signatures.
|
|
|
|
One example of an application that requires addresses to provide signatures would be decentralized exchanges with off-chain orderbook, where buy/sell orders are signed messages (see [0x](https://0xproject.com/) and [etherdelta](https://etherdelta.com/) for examples). In these applications, EOAs sign orders, signaling their desire to buy/sell a given asset and giving explicit permissions to the exchange smart contracts to conclude a trade via an ECDSA signature. When it comes to contracts however, ECDSA signature is not possible since contracts do not possess a private key. In the first version of the 0x protocol, smart contracts could not generate buy/sell orders for this very reason, as the `maker` needed to both own the assets *and* sign the order via ECDSA method. This was revised in their protocol version 2 (see below).
|
|
|
|
|
|
|
|
## Specification
|
|
<!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Ethereum platforms (go-ethereum, parity, cpp-ethereum, ethereumj, ethereumjs, and [others](https://github.com/ethereum/wiki/wiki/Clients)).-->
|
|
|
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
|
|
|
|
```javascript
|
|
pragma solidity ^0.5.0;
|
|
|
|
contract ERC1271 {
|
|
|
|
// bytes4(keccak256("isValidSignature(bytes,bytes)")
|
|
bytes4 constant internal MAGICVALUE = 0x20c13b0b;
|
|
|
|
/**
|
|
* @dev Should return whether the signature provided is valid for the provided data
|
|
* @param _data Arbitrary length data signed on the behalf of address(this)
|
|
* @param _signature Signature byte array associated with _data
|
|
*
|
|
* MUST return the bytes4 magic value 0x20c13b0b when function passes.
|
|
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
|
|
* MUST allow external calls
|
|
*/
|
|
function isValidSignature(
|
|
bytes memory _data,
|
|
bytes memory _signature)
|
|
public
|
|
view
|
|
returns (bytes4 magicValue);
|
|
}
|
|
```
|
|
|
|
`isValidSignature` can call arbitrary methods to validate a given signature, which could be context dependent (e.g. time based or state based), EOA dependent (e.g. signers authorization level within smart wallet), signature scheme Dependent (e.g. ECDSA, multisig, BLS), etc.
|
|
|
|
|
|
|
|
## Rationale
|
|
<!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.-->
|
|
|
|
Such a function is important because it allows *other contracts* to validate signed messages on the behalf of the smart wallet. This is necessary because not all signed messages will first pass by the smart wallet as in ERC-1077, since signatures can be requested by independent contracts. Action based signed messages do not require this method for external contracts since the action is `smart wallet A -> Contract C` (e.g. owner of smart wallet `A` wants to transfer tokens `T` to contract `C`), but when the action is in the opposite direction (`Contract A -> SmartAccount`) this external function is necessary (e.g. `contract A` requires smart wallet `A` to transfer tokens `T` when event `E` is triggered).
|
|
|
|
We believe the name of the proposed function to be appropriate considering that an *authorized* signers providing proper signatures for a given data would see their signature as "valid" by the smart wallet. Hence, an signed action message is only valid when the signer is authorized to perform a given action on the behalf of a smart wallet.
|
|
|
|
Two arguments are provided for simplicity of separating the data from the signature, but both could be concatenated in a single byte array if community prefers this.
|
|
|
|
`isValidSignature()` should not be able to modify states in order to prevent `GasToken` minting or similar attack vectors. A gas limit being passed is being considered, but could prevent some interesting use cases like large multisignatures or more costly validation and cryptographic schemes.
|
|
|
|
|
|
|
|
## Backwards Compatibility
|
|
<!--All EIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The EIP must explain how the author proposes to deal with these incompatibilities. EIP submissions without a sufficient backwards compatibility treatise may be rejected outright.-->
|
|
|
|
This EIP is backward compatible with previous work on signature validation since this method is specific to contract based signatures and not EOA signatures.
|
|
|
|
## Implementation
|
|
<!--The implementations must be completed before any EIP is given status "Final", but it need not be completed before the EIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details.-->
|
|
|
|
Existing implementations :
|
|
|
|
* The 0x project [implemented this method](https://github.com/0xProject/0x-monorepo/blob/05b35c0fdcbca7980d4195e96ec791c1c2d13398/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol#L187) in their protocol version 2.
|
|
* Zeppelin is [in the process](https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1104) of implementing this method.
|
|
|
|
## Copyright
|
|
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|