{"action":"created","issue":{"url":"https://api.github.com/repos/ethereum/EIPs/issues/712","repository_url":"https://api.github.com/repos/ethereum/EIPs","labels_url":"https://api.github.com/repos/ethereum/EIPs/issues/712/labels{/name}","comments_url":"https://api.github.com/repos/ethereum/EIPs/issues/712/comments","events_url":"https://api.github.com/repos/ethereum/EIPs/issues/712/events","html_url":"https://github.com/ethereum/EIPs/pull/712","id":257207367,"number":712,"title":"Add eth_signTypedData as a standard for machine-verifiable and human-readable typed data signing with Ethereum keys","user":{"login":"LogvinovLeon","id":6204356,"avatar_url":"https://avatars1.githubusercontent.com/u/6204356?v=4","gravatar_id":"","url":"https://api.github.com/users/LogvinovLeon","html_url":"https://github.com/LogvinovLeon","followers_url":"https://api.github.com/users/LogvinovLeon/followers","following_url":"https://api.github.com/users/LogvinovLeon/following{/other_user}","gists_url":"https://api.github.com/users/LogvinovLeon/gists{/gist_id}","starred_url":"https://api.github.com/users/LogvinovLeon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LogvinovLeon/subscriptions","organizations_url":"https://api.github.com/users/LogvinovLeon/orgs","repos_url":"https://api.github.com/users/LogvinovLeon/repos","events_url":"https://api.github.com/users/LogvinovLeon/events{/privacy}","received_events_url":"https://api.github.com/users/LogvinovLeon/received_events","type":"User","site_admin":false},"labels":[{"id":290507092,"url":"https://api.github.com/repos/ethereum/EIPs/labels/ERC","name":"ERC","color":"d4c5f9","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":100,"created_at":"2017-09-12T22:52:46Z","updated_at":"2018-03-02T18:57:50Z","closed_at":null,"author_association":"NONE","pull_request":{"url":"https://api.github.com/repos/ethereum/EIPs/pulls/712","html_url":"https://github.com/ethereum/EIPs/pull/712","diff_url":"https://github.com/ethereum/EIPs/pull/712.diff","patch_url":"https://github.com/ethereum/EIPs/pull/712.patch"},"body":"# eth_signTypedData\r\n\r\n## Preamble\r\n\r\n    EIP: <to be assigned>\r\n    Title: Ethereum typed data signing\r\n    Author: Leonid Logvinov <logvinov.leon@gmail.com>\r\n    Type: Standard Track\r\n    Category: ERC\r\n    Status: Draft\r\n    Created: 2017-09-13\r\n    Replaces: 693\r\n\r\n## Simple Summary\r\n\r\nStandard for machine-verifiable and human-readable typed data signing with Ethereum keys.\r\n\r\n## Abstract\r\n\r\nEthereum clients provide the ability to sign UTF-8 strings with the `eth_sign` RPC call. This call however, doesn't give Signer UI's enough metadata to display the actual intent of the DApp when the data being signed is not a string. Over time, we expect value to be transferred on Ethereum using protocols that involve off-chain components (e.g state channels). These protocols are going to involve signing complex data structures with Ethereum keys, making a human-readable, machine-verifiable signing flow critical to user security.\r\n\r\nThis EIP adds an RPC method to sign arrays of arbitrary typed data, making it easy for the Signer UI to display this data to the user in a human-readable form, so that the user can be 100% confident of exactly what they are signing, while keeping the resulting signatures machine-verifiable. In addition, there is no room for a malicious DApp to provide the signer with alternative data then that being presented to the user, since the signer hashes the typedData before the user signs it.\r\n\r\nThis method should only be implemented in Ethereum clients with Signer UI's that require user approval before signing (MetaMask, MEW, Parity signer, Ledger, Trezor). It should not be implemented in clients where approval is already granted (e.g Geth with unlocked accounts).\r\n\r\n![eth_signtypeddata](https://user-images.githubusercontent.com/6204356/31608125-cc9799da-b277-11e7-8935-518ff80d8673.png)\r\n\r\nThis EIP is a continuation of a discussion here: https://github.com/ethereum/EIPs/pull/683\r\nI offered this solution in a comment: https://github.com/ethereum/EIPs/pull/683#issuecomment-327945854\r\nIt proposes a more general solution then the one offered here: https://github.com/ethereum/EIPs/pull/693\r\n\r\n## Motivation\r\n\r\nThere are a whole range of higher level protocols emerging on Ethereum. State channels, 0x protocol, Login protocols, etc... For scalability reasons these protocols want to sign some protocol-specific data off-chain. They then use those signed messages to trigger on-chain or other actions with important consequences.\r\n\r\n* Transferring ETH\r\n* Transferring tokens\r\n* Transferring ownership\r\n* etc...\r\n\r\n\r\nThe current `eth_sign` implementation allows signing of arbitrary data without specifying it's type or structure. Some signers assume that the data is a UTF-8 string. Some signers show it as a hex encoded string. This leads to user confusion since it is impossible to verify the message you're signing unless it's a plaintext UTF-8 string.\r\n\r\n<img width=\"359\" alt=\"eth_sign\" src=\"https://user-images.githubusercontent.com/6204356/31608133-d7aecba4-b277-11e7-90e6-18dcf0dd62d9.png\">\r\n\r\n\r\nCalling `eth_sign` on Metamask displays the string to sign. If the user is signing the result of hashing a complex structure involving multiple critical pieces of information, the only way to verify what they are signing is to re-hash the same structure in an independent script and make sure the hashes match.\r\n\r\nCalling `personal_sign` on Metamask with the raw bytes of a hash (e.g not an ASCII string) shows the user this even less verifiable message that they should sign.\r\n\r\n<img width=\"361\" alt=\"personal_sign\" src=\"https://user-images.githubusercontent.com/6204356/31608135-db679320-b277-11e7-9bdc-4df1a425459c.png\">\r\n\r\n\r\nThe main problem is that signers don't have enough metadata to display the DApp's real intent effectively. In order to do so, they need the plaintext input that the dApp wishes hashed and signed by the user.\r\n\r\n## Specification\r\n\r\nThis EIP proposes a new JSON RPC method to the `eth` namespace: `eth_signTypedData`. It accepts an array of values together with their specified type and human-readable name. The [json-schema](http://json-schema.org/) draft is defined below.\r\n\r\n### Params JSON Schema:\r\n```json-schema\r\n{\r\n  items: {\r\n    properties: {\r\n      name: {type: 'string'},\r\n      type: {type: 'string'}, // Solidity type as described here: https://github.com/ethereum/solidity/blob/93b1cc97022aa01e7daa9816bcc23108bbe008b5/libsolidity/ast/Types.cpp#L182\r\n      value: {\r\n        oneOf: [\r\n          {type: 'string'},\r\n          {type: 'number'},\r\n          {type: 'boolean'},\r\n        ],\r\n      },\r\n    },\r\n    type: 'object',\r\n  },\r\n  type: 'array',\r\n}\r\n```\r\n\r\n### Example params:\r\n// For this state channel POC: https://medium.com/@matthewdif/ethereum-payment-channel-in-50-lines-of-code-a94fad2704bc\r\n```javascript\r\ntypedData = [\r\n    {\r\n      \"name\": \"channel\",\r\n      \"type\": \"address\",\r\n      \"value\": \"0xb088a3Bc93F71b4DE97b9De773e9647645983688\",\r\n    },\r\n    {\r\n      \"name\": \"value\",\r\n      \"type\": \"uint\",\r\n      \"value\": 42,\r\n    },\r\n];\r\n```\r\n\r\n### How it can look in signer UI:\r\n\r\n![eth_signtypeddata](https://user-images.githubusercontent.com/6204356/31608139-e087df86-b277-11e7-90ba-5fc3c35334e8.png)\r\n\r\n\r\nIt's important to make the schema part of the signature (explanation can be found in “Rationale” section). The way the schema will be combined with the values to generate the hash signed by the user is shown below. First, the schema is encoded into a string using a method similar to [solidity events signatures](http://solidity.readthedocs.io/en/develop/contracts.html#low-level-interface-to-logs). It is then hashed together with the keccak256 hash of the data array.\r\n\r\n### Pseudocode examples:\r\n\r\n```javascript\r\n// Client-side code example\r\nconst typedData = [\r\n  {\r\n    'type': 'string',\r\n    'name': 'message',\r\n    'value': 'Hi, Alice!',\r\n  },\r\n  {\r\n    'type': 'uint',\r\n    'name': 'value',\r\n    'value': 42,\r\n  },\r\n];\r\nconst signature = await web3.eth.signTypedData(typedData);\r\n```\r\n\r\n```javascript\r\n// Signed code JS example\r\nimport * as _ from 'lodash';\r\nimport * as ethAbi from 'ethereumjs-abi';\r\n\r\nconst data = _.map(typedData, 'value');\r\nconst types = _.map(typedData, 'type');\r\nconst schema = _.map(typedData, entry => `${entry.type} ${entry.name}`);\r\nconst hash = ethAbi.soliditySHA3(\r\n  ['bytes32', 'bytes32'],\r\n  [\r\n    ethAbi.soliditySHA3(_.times(typedData.length, _.constant('string')), schema),\r\n    ethAbi.soliditySHA3(types, data),\r\n  ],\r\n);\r\n```\r\n\r\n```solidity\r\n// Solidity example\r\nstring message = 'Hi, Alice!';\r\nunit value = 42;\r\nconst hash = keccak256(\r\n  keccak256('string message', 'uint value'), // Probably hardcoded\r\n  keccak256(message, value),\r\n);\r\naddress recoveredSignerAddress = ecrecover(hash, v, r, s);\r\n```\r\n\r\nSignature will be returned in the same format as `eth_sign` (the ecSignature params concatenated together (r + s + v) and hex encoded.\r\n\r\n## Rationale\r\n\r\nSigning support in Ethereum clients is most useful for off-chain transactions. On-chain transactions in Ethereum have well-defined typed schemas (gas uint, gasPrice uint, etc) which allows signer UIs to show useful information about transactions to the user. We need the same for off-chain transactions.\r\n\r\nThe Solidity keccak256 function accepts an array of typed arguments and motivated the current design.\r\n\r\nThe main and the most important part of this change is to give Signer UIs a chance to give a user enough information to be able to verify what is being signed even when using untrusted DApps. Since the DApp does not get to provide the signer with a hard-to-read hash but must supply the exact values (in plain-text) that the user will be signing, the dangers of signing a malicious action (transaction or otherwise) are greatly reduced.\r\n\r\nThe schema information must be signed together with the data, so that the verifying party can be confident, that the user was shown the data with the corresponding type and names supplied by the DApp.\r\n\r\nThis approach is even possible to implement on hardware wallets with small screens (Ledger Nano S, Trezor). Users will be shown one line at a time (as currently with transactions).\r\n\r\n\r\n## Backwards Compatibility\r\n\r\nThis EIP does not break backwards compatibility.\r\n\r\n`eth_sign` and `personal_sign` will not be removed for backwards compatibility reasons even though the `eth_signTypedData` is a superset of their functionality. Current off-chain protocols will however need to modify the verifying code in their smart contracts to include the schema.\r\n\r\nExample of signing a simple string using `eth_signTypedData`\r\n\r\n```javascript\r\nconst signature = await web3.eth.signTypedData([\r\n  {\r\n    'type': 'string',\r\n    'name': 'message',\r\n    'value': 'Hi, Alice!',\r\n  },\r\n]);\r\n```\r\n\r\n\r\nThe implementation of `eth_signTypedData`  makes some assumptions about the crypto primitives being used (tightly-packed + keccak256 + secp256k1), but the same assumptions have already been made for the [keccak256](http://solidity.readthedocs.io/en/develop/units-and-global-variables.html) function in solidity. If Ethereum becomes more crypto-agnostic in the future and allows for other types of signatures - this EIP can be adjusted.\r\n\r\nThe choice of keccak256 is motivated by the fact, that it's twice [cheaper to verify in a smart contract](https://ethereum.stackexchange.com/questions/3184/what-is-the-cheapest-hash-function-available-in-solidity/3200#3200) when compared with sha3.\r\n\r\n## Test Cases\r\n\r\nNot ready Yet.\r\nProper test vector suite will be added in the next finalization steps.\r\n\r\n\r\n## Implementation\r\n\r\nIt is imlemented as an experimental feature in Metamask 3.11.0+.\r\nIt's not part of web3.js yet, so you can try using it like:\r\n```javascript\r\nconst data = {\r\n  'type': 'string',\r\n  'name': 'message',\r\n  'value': 'Hi, Alice!',\r\n};\r\nweb3.currentProvider.sendAsync({method: 'eth_signTypedData', params: [data, signerAddress], jsonrpc: '2.0', id: 1}, callback);\r\n```\r\n\r\n## Copyright\r\n\r\n\r\nCopyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).\r\n"},"comment":{"url":"https://api.github.com/repos/ethereum/EIPs/issues/comments/370018648","html_url":"https://github.com/ethereum/EIPs/pull/712#issuecomment-370018648","issue_url":"https://api.github.com/repos/ethereum/EIPs/issues/712","id":370018648,"user":{"login":"Recmo","id":4532328,"avatar_url":"https://avatars2.githubusercontent.com/u/4532328?v=4","gravatar_id":"","url":"https://api.github.com/users/Recmo","html_url":"https://github.com/Recmo","followers_url":"https://api.github.com/users/Recmo/followers","following_url":"https://api.github.com/users/Recmo/following{/other_user}","gists_url":"https://api.github.com/users/Recmo/gists{/gist_id}","starred_url":"https://api.github.com/users/Recmo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Recmo/subscriptions","organizations_url":"https://api.github.com/users/Recmo/orgs","repos_url":"https://api.github.com/users/Recmo/repos","events_url":"https://api.github.com/users/Recmo/events{/privacy}","received_events_url":"https://api.github.com/users/Recmo/received_events","type":"User","site_admin":false},"created_at":"2018-03-02T18:57:50Z","updated_at":"2018-03-02T18:57:50Z","author_association":"NONE","body":"@christianlundkvist \r\n> @Recmo If ABIv2 doesn't include variable names then that definitely needs to be added as well\r\n\r\nDon't get me started. ERC20 `transfer(address to, uint256 amount)` and NFT `transfer(address to, uint256 tokenId)` have the same bytes4 function selector! This gives you a really easy way to steal cryptokitties. The problem is that capturing type info is not enough for signing, you need to capture as much of semantics as you can. Including parameter names is a great improvement already.\r\n\r\n> The schema author can just add use case specific nonces as needed in the schema itself.\r\n\r\nThe schema format I proposed does not give you much room to tweak it. You can really only tweak the type names and parameter names. We could implore implementers to come up with a unique name for the top level struct, like `PineappleCoinMessage` instead of `Message`. Or even Java style reverse domain names in camelCase `NetworkPineappleMessage` for standards from `pineapple.network`. Or something ENS based.\r\n\r\nHow do you see a schema author adding a nonce?"}}
{"actor":{"display_login":"Recmo"}}