diff --git a/EIPS/eip-1014.md b/EIPS/eip-1014.md index c2279255647a1..dc3aac23bbced 100644 --- a/EIPS/eip-1014.md +++ b/EIPS/eip-1014.md @@ -10,14 +10,100 @@ created: 2018-04-20 ### Specification -Adds a new opcode at 0xf5, which takes 4 stack arguments: endowment, memory_start, memory_length, salt. Behaves identically to CREATE, except using `keccak256(msg.sender ++ salt ++ init_code)[12:]` instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. +Adds a new opcode at 0xf5, which takes 4 stack arguments: endowment, memory_start, memory_length, salt. Behaves identically to CREATE, except using `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]` instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. + +The `CREATE2` has the same `gas` schema as `CREATE`, but also an extra `hashcost` of `GSHA3WORD * ceil(len(init_code) / 32)`, to account for the hashing that must be performed. The `hashcost` is deducted at the same time as memory-expansion gas and `CreateGas` is deducted: _before_ evaluation of the resulting address and the execution of `init_code`. + +- `0xff` is a single byte, +- `address` is always `20` bytes, +- `salt` is always `32` bytes (a stack item). + +The preimage for the final hashing round is thus always exactly `85` bytes long. + +The coredev-call at 2018-08-10 decided to use the formula above. + ### Motivation Allows interactions to (actually or counterfactually in channels) be made with addresses that do not exist yet on-chain but can be relied on to only possibly eventually contain code that has been created by a particular piece of init code. Important for state-channel use cases that involve counterfactual interactions with contracts. -#### Option 2 +### Rationale + +#### Address formula + +* Ensures that addresses created with this scheme cannot collide with addresses created using the traditional `keccak256(rlp([sender, nonce]))` formula, as `0xff` can only be a starting byte for RLP for data many petabytes long. +* Ensures that the hash preimage has a fixed size, + +#### Gas cost + +Since address calculation depends on hashing the `init_code`, it would leave clients open to DoS attacks if executions could repeatedly cause hashing of large pieces of `init_code`, since expansion of memory is paid for only once. This EIP uses the same cost-per-word as the `SHA3` opcode. + +### Clarifications + +The `init_code` is the code that, when executed, produces the runtime bytecode that will be placed into the state, and which typically is used by high level languages to implement a 'constructor'. + +This EIP makes collisions possible. The behaviour at collisions is specified by [EIP 684](https://github.com/ethereum/EIPs/issues/684): + +> If a contract creation is attempted, due to either a creation transaction or the CREATE (or future CREATE2) opcode, and the destination address already has either nonzero nonce, or nonempty code, then the creation throws immediately, with exactly the same behavior as would arise if the first byte in the init code were an invalid opcode. This applies retroactively starting from genesis. + +Specifically, if `nonce` or `code` is nonzero, then the create-operation fails. + +With [EIP 161](https://eips.ethereum.org/EIPS/eip-161) + +> Account creation transactions and the CREATE operation SHALL, prior to the execution of the initialisation code, increment the nonce over and above its normal starting value by one + +This means that if a contract is created in a transaction, the `nonce` is immediately non-zero, with the side-effect that a collision within the same transaction will always fail -- even if it's carried out from the `init_code` itself/ + +It should also be noted that `SELFDESTRUCT` has no immediate effect on `nonce` or `code`, thus a contract cannot be destroyed and recreated within one transaction. + +### Examples + +Example 0 +* address `0x0000000000000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* init_code `0x00` +* gas (assuming no mem expansion): `32006` +* result: `0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38` + +Example 1 +* address `0xdeadbeef00000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* init_code `0x00` +* gas (assuming no mem expansion): `32006` +* result: `0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3` + +Example 2 +* address `0xdeadbeef00000000000000000000000000000000` +* salt `0x000000000000000000000000feed000000000000000000000000000000000000` +* init_code `0x00` +* gas (assuming no mem expansion): `32006` +* result: `0xD04116cDd17beBE565EB2422F2497E06cC1C9833` + +Example 3 +* address `0x0000000000000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* init_code `0xdeadbeef` +* gas (assuming no mem expansion): `32006` +* result: `0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e` + +Example 4 +* address `0x00000000000000000000000000000000deadbeef` +* salt `0x00000000000000000000000000000000000000000000000000000000cafebabe` +* init_code `0xdeadbeef` +* gas (assuming no mem expansion): `32006` +* result: `0x60f3f640a8508fC6a86d45DF051962668E1e8AC7` + +Example 5 +* address `0x00000000000000000000000000000000deadbeef` +* salt `0x00000000000000000000000000000000000000000000000000000000cafebabe` +* init_code `0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef` +* gas (assuming no mem expansion): `32012` +* result: `0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C` -Use `keccak256(0xff ++ msg.sender ++ salt ++ init_code)[12:]` +Example 6 +* address `0x0000000000000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* init_code `0x` +* gas (assuming no mem expansion): `32000` +* result: `0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0` -Rationale: ensures that addresses created with this scheme cannot collide with addresses created using the traditional `keccak256(rlp([sender, nonce]))` formula, as 0xff can only be a starting byte for RLP for data many petabytes long. diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 38612d40a999b..4df0e5a70375a 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -21,23 +21,27 @@ This proposal outlines a protocol in which DOM environments expose a read-only p ## Specification -### Definitions +### Concepts -1. **Read-only provider** +#### RFC-2119 - A read-only provider has no populated accounts and any RPC request that requires an account will fail. +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). -2. **Full provider** +#### Read-only provider - A full provider has populated accounts and any RPC request that requires an account will succeed. +A read-only provider has no populated accounts and any RPC request that requires an account will fail. -3. **`Provider#enable`** +#### Full provider - Providers exposed by DOM environments define a new `enable` method that returns a Promise. Calling this method triggers a user interface that allows the user to approve or deny full provider access for a given dapp. The returned Promise is resolved if the user approves full provider access or rejected if the user denies full provider access. +A full provider has populated accounts and any RPC request that requires an account will succeed. - ```js - ethereum.enable(): Promise - ``` +#### `Provider#enable` + +Providers exposed by DOM environments define a new `enable` method that returns a Promise. Calling this method triggers a user interface that allows the user to approve or deny full provider access for a given dapp. The returned Promise is resolved if the user approves full provider access or rejected if the user denies full provider access. + +```js +ethereum.enable(): Promise +``` ### Protocol @@ -71,15 +75,15 @@ IF provider is undefined ##### `[1] ENABLE` -Dapps MUST request a full provider by calling the `enable` method on the default read-only provider. This method MUST trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method MUST return a Promise that is resolved with an array of the user's public addresses if the user approves full provider access or rejected if the user denies full provider access. +Dapps **MUST** request a full provider by calling the `enable` method on the default read-only provider. This method **MUST** trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method **MUST** return a Promise that is resolved with an array of the user's public addresses if the user approves full provider access or rejected if the user denies full provider access. ##### `[2] RESOLVE` -If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved with an array of the user's public addresses. +If a user approves full provider access, DOM environments **MUST** expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method **MUST** be resolved with an array of the user's public addresses. ##### `[3] REJECT` -If a user denies full provider access, the Promise returned when calling the `enable` method MUST be rejected with an informative Error. +If a user denies full provider access, the Promise returned when calling the `enable` method **MUST** be rejected with an informative Error. ### Example initialization @@ -100,13 +104,13 @@ window.addEventListener('load', async () => { ### Constraints -* Browsers MUST expose a read-only provider at `window.ethereum` by default. -* Browsers MUST NOT expose a full provider globally by default. -* Dapps MUST request access to a full provider. -* Users MUST be able to approve or deny full provider access. -* A full provider MUST be exposed at `window.ethereum` after user approval. -* Dapps MUST be notified of user approval of full provider access. -* Dapps MUST be notified of user denial of full provider access. +* Browsers **MUST** expose a read-only provider at `window.ethereum` by default. +* Browsers **MUST NOT** expose a full provider globally by default. +* Dapps **MUST** request access to a full provider. +* Users **MUST** be able to approve or deny full provider access. +* A full provider **MUST** be exposed at `window.ethereum` after user approval. +* Dapps **MUST** be notified of user approval of full provider access. +* Dapps **MUST** be notified of user denial of full provider access. ## Rationale diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index a3aa748226655..6dd91abe8fda3 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -32,7 +32,7 @@ ethereum.enable(): Promise; This shows a dialog to the user asking if they would like to authenticate any account(s) to the dapp. -Promise resolves with `True`, or rejects with `Error`. +Promise resolves with `true` or rejects with `Error`. ### Send @@ -149,7 +149,7 @@ web3.setProvider(ethereum); ethereum .send('eth_getBlockByNumber', ['latest', 'true']) .then(block => { - console.log(`Block ${block.number}:\n${block}`); + console.log(`Block ${block.number}:`, block); }) .catch(error => { console.error( @@ -167,16 +167,16 @@ ethereum // Example 3: Log available accounts ethereum - .send('eth_accounts') - .then(accounts => { - console.log(`Accounts:\n${accounts.join('\n')}`); - }) - .catch(error => { - console.error( - `Error fetching accounts: ${error.message}. + .send('eth_accounts') + .then(accounts => { + console.log(`Accounts:\n${accounts.join('\n')}`); + }) + .catch(error => { + console.error( + `Error fetching accounts: ${error.message}. Code: ${error.code}. Data: ${error.data}` - ); - }); + ); + }); } }) .catch(error => { @@ -193,14 +193,14 @@ ethereum .then(subscriptionId => { subId = subscriptionId; ethereum.on(subscriptionId, block => { - if (result instanceOf Error) { + if (block instanceof Error) { const error = result; console.error( `Error from newHeads subscription: ${error.message}. Code: ${error.code}. Data: ${error.data}` ); } else { - console.log(`New block ${block.number}:\n${block}`); + console.log(`New block ${block.number}:`, block); } }); }) @@ -208,7 +208,7 @@ ethereum console.error( `Error making newHeads subscription: ${error.message}. Code: ${error.code}. Data: ${error.data}` - ); + ); }); // to unsubscribe ethereum @@ -233,9 +233,7 @@ ethereum.removeListener('accountsChanged', logAccounts); // Example 6: Log if connection ends ethereum.on('close', (code, reason) => { - console.log( - `Ethereum provider connection closed: ${reason}. Code: ${code}` - ); + console.log(`Ethereum provider connection closed: ${reason}. Code: ${code}`); }); ``` @@ -338,7 +336,15 @@ class EthereumProvider extends EventEmitter { return new Promise((resolve, reject) => { window.mist .requestAccounts() - .then(resolve) + .then(accounts => { + if (accounts.length > 0) { + resolve(true); + } else { + const error = new Error('User Denied Full Provider'); + error.code = 4001; + reject(error); + } + }) .catch(reject); }); } @@ -363,16 +369,17 @@ class EthereumProvider extends EventEmitter { // Send jsonrpc request to Mist window.postMessage( { type: 'mistAPI_ethereum_provider_write', message: payload }, - origin + targetOrigin ); return promise; } - subscribe(subscriptionType, subscriptionMethod, params) { + subscribe(subscriptionType, subscriptionMethod, params = []) { return this.send(subscriptionType, [subscriptionMethod, ...params]).then( subscriptionId => { this._activeSubscriptions.push(subscriptionId); + return subscriptionId; } ); } @@ -386,6 +393,7 @@ class EthereumProvider extends EventEmitter { ); // Remove listeners on subscriptionId this.removeAllListeners(subscriptionId); + return success; } }); } @@ -440,7 +448,10 @@ class EthereumProvider extends EventEmitter { _connect() { // Send to Mist - window.postMessage({ type: 'mistAPI_ethereum_provider_connect' }, origin); + window.postMessage( + { type: 'mistAPI_ethereum_provider_connect' }, + targetOrigin + ); // Reconnect on close this.once('close', this._connect.bind(this)); diff --git a/EIPS/eip-1271.md b/EIPS/eip-1271.md index 2e9b159b2b831..8e5e9fb0d9848 100644 --- a/EIPS/eip-1271.md +++ b/EIPS/eip-1271.md @@ -24,7 +24,7 @@ Externally Owned Accounts (EOA) can sign messages with their associated private 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 act 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. +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). diff --git a/EIPS/eip-918.md b/EIPS/eip-918.md index 3c6275263dd4f..1dc296e56d3dd 100644 --- a/EIPS/eip-918.md +++ b/EIPS/eip-918.md @@ -314,22 +314,19 @@ The ERC918 Mineable Mint Packet Metadata should be prepared using following sche The preparation of a mineable mint packet on a JavaScript client would appear as follows: ``` solidity -const ethUtil = require("ethereumjs-util") - -function prepareDelegatedMintTxn(nonce, address, privateKey) { - var functionSig = web3.sha3("delegatedMintHashing(uint256,address)").substring(0,10) - var hashOf = "0x" + bytes4ToHex(functionSig) + uint256ToHex(nonce) +addressToHex(accounts[1]) - var data = ethUtil.sha3(hashOf) - var signature = ethUtil.ecsign(data, new Buffer(privateKey, 'hex')) - var sig = ethUtil.toRpcSig(signature.v, signature.r, signature.s) +function prepareDelegatedMintTxn(nonce, account) { + var functionSig = web3.utils.sha3("delegatedMintHashing(uint256,address)").substring(0,10) + var data = web3.utils.soliditySha3( functionSig, nonce, account.address ) + var sig = web3.eth.accounts.sign(web3.utils.toHex(data), account.privateKey ) // prepare the mint packet var packet = {} - packet.nonce = nonce; - packet.origin = address; - packet.signature = sig; - // deliver resulting JSON packet to pool or third party submitter + packet.nonce = nonce + packet.origin = account.address + packet.signature = sig.signature + // deliver resulting JSON packet to pool or third party var mineableMintPacket = JSON.stringify(packet, null, 4) /* todo: send mineableMintPacket to submitter */ + ... } ``` Once the packet is prepared and formatted it can then be routed to a third party that will submit the transaction to the contract's delegatedMint() function, thereby paying for the transaction gas and receiving the resulting tokens. The pool/third party must then manually payback the minted tokens minus fees to the original minter. @@ -341,9 +338,11 @@ var mineableMintPacket = ... var packet = JSON.parse(mineableMintPacket) erc918MineableToken.delegatedMint(packet.nonce, packet.origin, packet.signature) ``` -The Extension expands upon ERC918 realized here as a sub-contract: +The Delegated Mint Extension expands upon ERC918 realized as a sub-contract: ``` js -contract ERC918DelegatedMint is AbstractERC918 { +import 'openzeppelin-solidity/contracts/contracts/cryptography/ECDSA.sol'; + +contract ERC918DelegatedMint is AbstractERC918, ECDSA { /** * @notice Hash (keccak256) of the payload used by delegatedMint * @param _nonce the golden nonce @@ -363,9 +362,9 @@ contract ERC918DelegatedMint is AbstractERC918 { * @param _nonce the golden nonce * @param _origin the original minter */ - function delegatedMintHashing(uint256 _nonce, address _origin) internal pure returns (bytes32) { - /* "0xb548f23d": delegatedMintHashing(uint256,address) */ - return keccak256( abi.encodePacked( bytes4(0xb548f23d), _nonce, _origin) ); + function delegatedMintHashing(uint256 _nonce, address _origin) public pure returns (bytes32) { + /* "0x7b36737a": delegatedMintHashing(uint256,address) */ + return toEthSignedMessageHash(keccak256(abi.encodePacked( bytes4(0x7b36737a), _nonce, _origin))); } } ```